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