Парадигма программирования — это относительно высокоуровневый способ структурирования и концептуализации реализации компьютерной программы . Язык программирования можно классифицировать как поддерживающий одну или несколько парадигм. [1]
Парадигмы разделены и описываются различными измерениями программирования. Некоторые парадигмы касаются последствий модели выполнения , таких как разрешение побочных эффектов или определение того, определяется ли моделью выполнения последовательность операций. Другие парадигмы касаются способа организации кода, например, группировки в блоки, включающие как состояние, так и поведение. Третьи посвящены синтаксису и грамматике .
Некоторые распространенные парадигмы программирования включают (показаны в иерархическом порядке): [2] [3] [4]
Парадигмы программирования возникли в результате компьютерных исследований существующих практик разработки программного обеспечения . Результаты позволяют описывать и сравнивать методы программирования и языки, используемые для написания программ. В перспективе другие исследования изучают процессы разработки программного обеспечения и описывают различные методологии их описания и сравнения.
Язык программирования можно описать с помощью парадигм. Некоторые языки поддерживают только одну парадигму. Например, Smalltalk поддерживает объектно-ориентированный подход, а Haskell — функциональный. Большинство языков поддерживают несколько парадигм. Например, программа, написанная на C++, Object Pascal или PHP, может быть чисто процедурной , чисто объектно-ориентированной или содержать аспекты обеих или других парадигм.
При использовании языка, поддерживающего несколько парадигм, разработчик выбирает, какие элементы парадигмы использовать. Однако этот выбор может не включать рассмотрение парадигм как таковых. Разработчик часто использует возможности языка в той мере, в какой язык их предоставляет и в той степени, в которой разработчик их знает. Классификация полученного кода по парадигмам часто является академической деятельностью, выполняемой ретроспективно.
Языки, отнесенные к императивной парадигме, имеют две основные особенности: они определяют порядок, в котором происходят операции, с помощью конструкций, которые явно контролируют этот порядок, и допускают побочные эффекты, при которых состояние может быть изменено в один момент времени, в пределах одной единицы кода. , а затем прочитать в другой момент времени внутри другой единицы кода. Связь между единицами кода не является явной.
Напротив, языки декларативной парадигмы не устанавливают порядок выполнения операций. Вместо этого они предоставляют ряд доступных операций в системе, а также условия, при которых каждая из них может выполняться. [7] Реализация модели выполнения языка отслеживает, какие операции могут выполняться свободно, и выбирает порядок независимо. Подробнее см. в разделе «Сравнение мультипарадигмальных языков программирования ».
В объектно-ориентированном программировании код организован в объекты , содержащие состояние, которым владеет и (обычно) управляет код объекта. Большинство объектно-ориентированных языков также являются императивными языками.
В объектно-ориентированном программировании программы рассматриваются как набор взаимодействующих объектов. В функциональном программировании программы рассматриваются как последовательность вычислений функций без сохранения состояния. При программировании компьютеров или систем со многими процессорами в процессно-ориентированном программировании программы рассматриваются как наборы параллельных процессов, которые действуют на общие логические структуры данных .
Многие парадигмы программирования известны как своими методами, которые они запрещают , так и теми, которые они поддерживают . Например, чисто функциональное программирование запрещает побочные эффекты , а структурное программирование запрещает конструкцию goto . Частично по этой причине те, кто привык к старым парадигмам, часто считают новые парадигмы доктринерскими или чрезмерно жесткими. [8] Тем не менее, отказ от определенных методов может облегчить понимание поведения программы и доказательство теорем о корректности программы.
Парадигмы программирования также можно сравнить с моделями программирования , которые позволяют вызывать модель выполнения , используя только API. Модели программирования также можно разделить на парадигмы на основе особенностей модели выполнения.
Для параллельных вычислений обычным является использование модели программирования вместо языка. Причина в том, что детали параллельного оборудования просачиваются в абстракции, используемые для программирования оборудования. Это приводит к тому, что программисту приходится сопоставлять шаблоны алгоритма с шаблонами модели выполнения (которые были вставлены из-за утечки аппаратного обеспечения в абстракцию). Как следствие, ни один язык параллельного программирования не подходит для решения всех вычислительных задач. Таким образом, удобнее использовать базовый последовательный язык и вставлять вызовы API в модели параллельного выполнения через модель программирования. Такие модели параллельного программирования можно классифицировать в соответствии с абстракциями, отражающими аппаратное обеспечение, такими как общая память , распределенная память с передачей сообщений , понятия места , видимого в коде, и т. д. Их можно считать разновидностью парадигмы программирования, применимой только к параллельным языкам и моделям программирования.
Некоторые исследователи языков программирования критикуют понятие парадигм как классификации языков программирования, например Харпер [9] и Кришнамурти. [10] Они утверждают, что многие языки программирования не могут быть строго отнесены к одной парадигме, а скорее включают в себя функции нескольких парадигм. См. Сравнение мультипарадигмальных языков программирования .
Со временем развивались различные подходы к программированию. Классификация каждого подхода была описана либо во время его первой разработки, но часто только спустя некоторое время, ретроспективно. Ранним подходом, сознательно идентифицированным как таковой, является структурированное программирование , пропагандируемое с середины 1960-х годов. Концепция парадигмы программирования как таковая появилась, по крайней мере, в 1978 году в лекции Роберта Флойда на Премии Тьюринга под названием « Парадигмы программирования» , в которой цитируется понятие парадигмы, использованное Томасом Куном в его «Структуре научных революций» ( 1962). [11] Ранние языки программирования не имели четко определенных парадигм программирования, и иногда в программах широко использовались операторы перехода. Свободное использование которых приводит к созданию спагетти-кода , который трудно понять и поддерживать. Это привело к развитию парадигм структурированного программирования, которые запрещали использование операторов перехода; позволяя использовать только более структурированные программные конструкции. [12]
Машинный код — это самый низкий уровень компьютерного программирования, поскольку именно машинные инструкции определяют поведение на самом низком уровне абстракции, возможном для компьютера. Поскольку это наиболее предписывающий способ кодирования, он классифицируется как императивный.
Его иногда называют языком программирования первого поколения .
В языке ассемблера появилась мнемоника для машинных инструкций и адресов памяти . Ассемблер относят к императивным и иногда называют языком программирования второго поколения .
В 1960-х годах языки ассемблера были разработаны для поддержки библиотеки COPY и довольно сложных возможностей условной генерации макросов и предварительной обработки, CALL для подпрограмм , внешних переменных и общих разделов (глобальных), что позволило значительно повторно использовать код и изолировать от особенностей оборудования за счет использования логические операторы, такие как READ/WRITE/GET/PUT. Сборка использовалась и до сих пор используется для систем, критичных ко времени, а часто и во встроенных системах , поскольку она обеспечивает максимальный контроль над тем, что делает машина.
Процедурные языки , также называемые языками программирования третьего поколения , первыми описываются как языки высокого уровня . Они поддерживают словарный запас, связанный с решаемой проблемой. Например,
Эти языки относятся к процедурной парадигме. Они напрямую контролируют пошаговый процесс, которому следует компьютерная программа. Поэтому эффективность и результативность такой программы во многом зависят от навыков программиста.
В попытке улучшить процедурные языки были созданы языки объектно-ориентированного программирования (ООП), такие как Simula , Smalltalk , C++ , Eiffel , Python , PHP , Java и C# . В этих языках данные и методы управления данными находятся в одной и той же кодовой единице, называемой объектом . Эта инкапсуляция гарантирует, что единственный способ доступа объекта к данным — это методы объекта, содержащего данные. Таким образом, внутреннюю работу объекта можно изменить, не затрагивая код, использующий этот объект.
Александром Степановым , Ричардом Столлманом [13] и другими программистами ведутся споры относительно эффективности парадигмы ООП по сравнению с процедурной парадигмой. Необходимость того, чтобы каждый объект имел ассоциативные методы, заставляет некоторых скептиков связывать ООП с раздуванием программного обеспечения ; попытка решить эту дилемму произошла с помощью полиморфизма .
Хотя большинство языков ООП относятся к третьему поколению, можно создать объектно-ориентированный язык ассемблера. Примером этого является сборка высокого уровня (HLA), которая полностью поддерживает расширенные типы данных и объектно-ориентированное программирование на языке ассемблера, несмотря на свое раннее происхождение. Таким образом, различные парадигмы программирования можно рассматривать скорее как мотивационные мемы их сторонников, а не обязательно отражать прогресс от одного уровня к другому. [ нужна цитата ] Точное сравнение эффективности конкурирующих парадигм часто затрудняется из-за новой и отличающейся терминологии, применяемой к схожим объектам и процессам, а также многочисленных различий в реализации на разных языках.
Программа декларативного программирования описывает, в чем состоит проблема, а не способы ее решения. Программа структурирована как набор свойств, которые необходимо найти в ожидаемом результате, а не как процедура, которой необходимо следовать. Учитывая базу данных или набор правил, компьютер пытается найти решение, соответствующее всем желаемым свойствам. Архетипом декларативного языка является язык четвертого поколения SQL и семейство функциональных языков и логического программирования.
Функциональное программирование — это подмножество декларативного программирования. Программы, написанные с использованием этой парадигмы, используют функции , блоки кода, которые ведут себя как математические функции . Функциональные языки препятствуют изменению значений переменных посредством присваивания , вместо этого широко используется рекурсия .
Парадигма логического программирования рассматривает вычисления как автоматизированное рассуждение над совокупностью знаний. Факты о проблемной области выражаются в виде логических формул, и программы выполняются с применением к ним правил вывода до тех пор, пока не будет найден ответ на проблему или пока не будет доказано, что набор формул несовместим.
Символьное программирование — это парадигма, описывающая программы, способные манипулировать формулами и компонентами программы как данными. [4] Таким образом, программы могут эффективно модифицировать себя и «обучаться», что делает их пригодными для таких приложений, как искусственный интеллект , экспертные системы , обработка естественного языка и компьютерные игры. Языки, поддерживающие эту парадигму, включают Lisp и Prolog . [14]
Дифференцируемое программирование структурирует программы таким образом, чтобы их можно было дифференцировать повсюду, обычно посредством автоматического дифференцирования . [15] [16]
Грамотное программирование , как форма императивного программирования , структурирует программы как человеко-центрированную сеть, как в гипертекстовом эссе: документация является неотъемлемой частью программы, и программа структурирована в соответствии с логикой прозаического изложения, а не удобством компилятора.
Символьные методы, такие как отражение , которые позволяют программе ссылаться на себя, также можно рассматривать как парадигму программирования. Однако это совместимо с основными парадигмами и, таким образом, само по себе не является настоящей парадигмой.