Функциональное, декларативное и императивное программирование [закрыто]


466

Что означают термины функциональное, декларативное и императивное программирование?


3
Здесь есть несколько отличных ответов. Одна интересная вещь, которая до конца не выяснена, заключается в том, что декларативный и императивный являются взаимодополняющими и симбиотическими, больше, чем просто разные стили или что и как .
Кит

1
@ Kit Imo, некоторые ответы на этой странице объединяют термины. DP == ссылочная прозрачность (RT). DP и IP являются противоположностями, поэтому они не являются дополнением к целому, то есть вся программа может быть написана в любом стиле. Вызов функции может быть либо DP (RT), либо IP, ее реализация может быть или смесью. Они не являются симбиотическими в том смысле, что вызов функции IP в другой функции DP может сделать IP-вызов функции DP. Они симбиотичны в том смысле, что программы реального мира (например, функционально-реактивные) могут использовать сочетание, например, вызовы IP верхнего уровня в функции DP.
Шелби Мур III

можно добавить в вики или ссылку на что-то похожее на вики и т. д. Вот отличная ссылка в википедии en.wikipedia.org/wiki/Comparison_of_programming_paradigms
Джо


1
Этот вопрос обсуждается в Meta: meta.stackoverflow.com/q/342784/2751851
duplode

Ответы:


262

На момент написания этой статьи ответы с наибольшим количеством голосов на этой странице были неточными и запутанными в декларативном и обязательном определении, включая ответ, который цитирует Википедию. Некоторые ответы объединяют термины по-разному.

См. Также мое объяснение того, почему программирование электронных таблиц является декларативным, несмотря на то, что формулы изменяют ячейки.

Кроме того, в нескольких ответах утверждается, что функциональное программирование должно быть подмножеством декларативного. От этого зависит, будем ли мы отличать «функцию» от «процедуры». Давайте сначала обратимся к императиву против декларативного.

Определение декларативного выражения

Только атрибут , который может возможно дифференцировать декларативное выражение из императивного выражения является ссылочной прозрачностью (RT) его подвыражения. Все остальные атрибуты являются общими для обоих типов выражений или производными от RT.

100% декларативный язык (т. Е. Тот, в котором каждое возможное выражение является RT) не (среди других требований RT) не допускает мутации хранимых значений, например HTML и большей части Haskell.

Определение выражения RT

RT часто называют «без побочных эффектов». Термин « эффекты» не имеет точного определения, поэтому некоторые люди не согласны с тем, что «нет побочных эффектов» - это то же самое, что RT. RT имеет точное определение .

Так как каждый суб-выражение концептуально вызов функции, RT требует, чтобы выполнение функции (то есть выражение (ы) внутри вызываемой функции) не может получить доступ к изменяемого состояния, которое внешние функции ( Получение доступа к изменяемым локальное состояние является разрешается). Проще говоря, функция (реализация) должна быть чистой .

Определение чистой функции

Часто говорят, что чистая функция не имеет побочных эффектов. Термин « эффекты» не имеет точного определения, поэтому некоторые люди не согласны.

Чистые функции имеют следующие атрибуты.

  • единственный наблюдаемый результат - возвращаемое значение.
  • единственная выходная зависимость - это аргументы.
  • аргументы полностью определяются до того, как будет сгенерирован какой-либо вывод.

Помните, что RT применяется к выражениям (которые включают вызовы функций), а чистота применяется к (реализациям) функций.

Непонятным примером нечистых функций, которые создают выражения RT, является параллелизм, но это происходит потому, что чистота нарушается на уровне абстракции прерываний. Вам не нужно знать это. Чтобы сделать RT выражения, вы вызываете чистые функции.

Производные атрибуты RT

Любой другой атрибут, цитируемый для декларативного программирования, например, цитата из 1999 года, используемая в Википедии, либо происходит от RT, либо используется совместно с императивным программированием. Таким образом, доказывая, что мое точное определение является правильным.

Обратите внимание, что неизменность внешних значений является подмножеством требований к RT.

  • Декларативные языки не имеют структур управления циклами, например, forи while, поскольку из-за неизменности условие цикла никогда не изменится.

  • Декларативные языки не выражают поток управления, отличный от порядка вложенных функций (или логических зависимостей), потому что из-за неизменности другие варианты порядка оценки не изменяют результат (см. Ниже).

  • Декларативные языки выражают логические «шаги» (т. Е. Порядок вызова вложенной функции RT), но не является ли декларативное программирование тем, что каждый вызов функции является семантикой более высокого уровня (т. Е. «Что делать»). Отличие от императива заключается в том, что из-за неизменности (т. Е. В более общем смысле RT) эти «шаги» не могут зависеть от изменяемого состояния, а только от реляционного порядка выраженной логики (т. Е. Порядка вложения вызовов функций, то есть подвыражений). ).

    Например, абзац HTML <p>нельзя отобразить, пока не будут оценены подвыражения (т. Е. Теги) в абзаце. Нет изменяемого состояния, есть только зависимость порядка из-за логических отношений иерархии тегов (вложенность подвыражений, которые являются аналогично вложенными вызовами функций ).

  • Таким образом , существует производная атрибут неизменности ( в более общем случае РТ), что декларативные выражения, выражают только те логические отношения составных частей (т.е. аргументов функции суб-экспрессии) , а не изменяемые государственные отношения.

Порядок оценки

Выбор порядка вычисления подвыражений может дать переменный результат только в том случае, если какой-либо из вызовов функции не является RT (т. Е. Функция не является чистой), например, доступ к некоторому изменяемому состоянию, внешнему по отношению к функции, осуществляется внутри функции.

Например, если несколько вложенных выражений, например f( g(a, b), h(c, d) ), охотно и ленивые вычисления аргументов функции будут давать те же результаты , если функции f, gи hявляются чистыми.

Принимая во внимание, если функции f, gи hне чисты, то выбор порядка оценки может дать другой результат.

Обратите внимание, что вложенные выражения - это концептуально вложенные функции, поскольку операторы выражений - это просто вызовы функций, маскирующиеся под унарный префикс, унарный постфикс или двоичное представление инфикса.

Тангенциально, если все идентификаторы, например a, b, c, d, являются незыблемыми везде, состояние внешнего по отношению к программе не может быть доступно (например , I / O), и нет никакого разрушения уровня абстракции, то функции всегда чисты.

Кстати, у Haskell другой синтаксис f (g a b) (h c d).

Детали оценочного заказа

Функция - это переход состояния (не изменяемое сохраненное значение) от входа к выходу. Для RT композиций обращений к чистым функциям порядок выполнения этих переходов состояний не зависит. Переход состояния каждого вызова функции не зависит от других из-за отсутствия побочных эффектов и принципа, что функция RT может быть заменена ее кэшированным значением . Чтобы исправить распространенное заблуждение , чистая монадическая композиция всегда декларативна и RT , несмотря на то, что IOмонада Хаскелла, возможно, нечиста и, следовательно, обязательна относительно Worldсостояния, внешнего по отношению к программе (но в смысле пояснения ниже, побочные эффекты изолированы).

Стремительная оценка означает, что аргументы функции оцениваются до вызова функции, а ленивая оценка означает, что аргументы не оцениваются до (и если) доступа к ним внутри функции.

Определение : параметры функции объявляются на сайте определения функции , а аргументы функции предоставляются на сайте вызова функции . Знайте разницу между параметром и аргументом .

Концептуальна, все выражения (композиция) вызовы функций, например , константа функция без входов, унарные функции с одним входом, бинарные операторы инфиксных функций с двумя входами, конструкторами являются функциями, а также заявления даже управления (например if, for, while) можно моделировать с помощью функций. Порядок , что этот аргумент функция (не путать с вложенной функцией порядка вызова) оцениваются не объявлен синтаксисом, например , f( g() )может с готовностью оценить gто fна gрезультате «s или он может оценить fи только лениво оценить , gкогда его результат нужен в f.

Предостережение: полный язык Тьюринга (т. Е. Допускающий неограниченную рекурсию) совершенно декларативен, например, ленивая оценка вводит память и временную неопределенность. Но эти побочные эффекты, обусловленные выбором порядка оценки, ограничены потреблением памяти, временем выполнения, задержкой, отсутствием прерывания и внешним гистерезисом, т.е. внешней синхронизацией.

Функциональное программирование

Поскольку декларативное программирование не может иметь циклов, единственным способом итерации является функциональная рекурсия. Именно в этом смысле функциональное программирование связано с декларативным программированием.

Но функциональное программирование не ограничивается декларативным программированием . Функциональная композиция может быть противопоставлена ​​подтипу , особенно в отношении проблемы выражения , где расширение может быть достигнуто либо путем добавления подтипов, либо функциональной декомпозиции . Расширение может быть сочетанием обеих методологий.

Функциональное программирование обычно делает функцию объектом первого класса, то есть тип функции может появляться в грамматике везде, где может присутствовать любой другой тип. В результате функции могут вводить функции и работать с ними, обеспечивая таким образом разделение задач путем выделения композиции функций, то есть разделения зависимостей между подкомпьютерами детерминированных вычислений.

Например, вместо написания отдельной функции (и использования рекурсии вместо циклов, если функция также должна быть декларативной) для каждого из бесконечного числа возможных специализированных действий, которые могут быть применены к каждому элементу коллекции, функциональное программирование использует многократную итерацию функции, например map, fold, filter. Эти итерационные функции вводят первоклассную специализированную функцию действия. Эти итерационные функции выполняют итерацию коллекции и вызывают входную специализированную функцию действия для каждого элемента. Эти функции действия являются более краткими, потому что им больше не нужно содержать циклические операторы для итерации коллекции.

Однако обратите внимание, что если функция не является чистой, то это действительно процедура. Возможно, мы можем утверждать, что функциональное программирование, которое использует нечистые функции, действительно является процедурным программированием. Таким образом, если мы согласны с тем, что декларативные выражения являются RT, мы можем сказать, что процедурное программирование не является декларативным программированием, и, таким образом, мы можем утверждать, что функциональное программирование всегда RT и должно быть подмножеством декларативного программирования.

параллелизм

Эта функциональная композиция с первоклассными функциями может выражать глубину параллелизма , выделяя независимую функцию.

Принцип Брента: вычисления с работой w и глубиной d могут быть реализованы в p-процессоре PRAM за время O (max (w / p, d)).

И параллелизм, и параллелизм также требуют декларативного программирования , то есть неизменности и RT.

Так откуда взялось это опасное предположение, что параллелизм == параллелизм? Это естественное следствие языков с побочными эффектами: когда у вашего языка есть побочные эффекты повсюду, то каждый раз, когда вы пытаетесь сделать больше, чем одну вещь за раз, вы по существу имеете недетерминизм, вызванный чередованием эффектов от каждой операции , Так что в языках с побочными эффектами единственный способ получить параллелизм - это параллелизм; поэтому неудивительно, что мы часто видим, как они связаны друг с другом.

Порядок оценки FP

Обратите внимание, что порядок оценки также влияет на прекращение и производительность побочных эффектов функциональной композиции.

Стремительный (CBV) и ленивый (CBN) являются категориальными поединками [ 10 ], потому что они имеют обратный порядок оценки, то есть оцениваются ли сначала внешние или внутренние функции соответственно. Представьте себе перевернутое дерево, а затем энергично оценивает, начиная с ветви дерева функций, поднимаясь по иерархии ветвей к стволу функции верхнего уровня; в то время как ленивый оценивает от ствола до кончиков ветви. У Eager нет конъюнктивных продуктов («и», a / k / категорических «продуктов»), а у ленивых нет дизъюнктивных сопутствующих продуктов («или», a / k / категориальных «сумм») [ 11 ].

Представление

  • нетерпеливый

    Как и в случае не прекращения, жаждущий слишком жаждет конъюнктивной функциональной композиции, то есть структура управления композицией выполняет ненужную работу, которая не выполняется ленивым. К примеру , желая охотно и излишне отображает весь список к Булевым, когда она состоит со складкой , которая заканчивается на первый истинный элементе.

    Эта ненужная работа является причиной заявленного «до» дополнительного логарифмического коэффициента в последовательной временной сложности стремления против лени, как с чистыми функциями. Решение состоит в том, чтобы использовать функторы (например, списки) с ленивыми конструкторами (т. Е. Стремящимися к необязательным ленивым продуктам), потому что с рвением неверность рвения возникает из внутренней функции. Это связано с тем, что произведения являются конструктивными типами, то есть индуктивными типами с начальной алгеброй в начальной точке фиксации [ 11 ]

  • ленивый

    Как и в случае не прекращения, ленивый слишком ленив с дизъюнктивной функциональной композицией, то есть коиндуктивная окончательность может произойти позже, чем необходимо, что приводит как к ненужной работе, так и к недетерминированности запаздывания, чего не происходит с нетерпением [ 10 ] [ 11 ] , Примерами окончательности являются исключения состояния, времени, отсутствия завершения и времени выполнения. Это императивные побочные эффекты, но даже в чистом декларативном языке (например, Haskell) в монаде императивного ввода-вывода есть состояние (примечание: не все монады являются императивными!), Подразумеваемое в распределении пространства, а время - это состояние относительно императива реальный мир. Использование ленивых даже с опциональным рвением сопутствует утечкам «лени» во внутренние побочные продукты, потому что при ленивости неверность лени исходит из внешней функции(см. пример в разделе «Не завершение», где == - это внешняя функция двоичного оператора). Это связано с тем, что копроизведения ограничены конечностью, то есть коиндуктивными типами с конечной алгеброй конечного объекта [ 11 ].

    Ленивый вызывает неопределенность в разработке и отладке функций для задержки и пространства, отладка которых, вероятно, выходит за рамки возможностей большинства программистов, из-за диссонанса между объявленной иерархией функций и порядком оценки времени выполнения. Ленивые чистые функции, оцененные с нетерпением, потенциально могут вводить ранее невидимое прекращение во время выполнения. И наоборот, рыхлые чистые функции, оцененные с помощью lazy, могут потенциально вводить ранее невиданное пространство и неопределенность латентности во время выполнения.

Незавершение

Во время компиляции из-за проблемы Halting и взаимной рекурсии в полном языке Тьюринга функции не могут быть гарантированно завершены.

  • нетерпеливый

    С нетерпеливым, но не ленивым, для сочетания Head«и» Tail, если либо Headили Tailне завершается, то соответственно либо List( Head(), Tail() ).tail == Tail()или List( Head(), Tail() ).head == Head()не верно, потому что левая сторона не завершается, а правая сторона завершается.

    Тогда как с ленивыми обе стороны кончаются. Таким образом, eager слишком стремится к совместным продуктам и не завершается (включая исключения времени выполнения) в тех случаях, когда в этом нет необходимости.

  • ленивый

    С ленивым, но не нетерпеливым, для дизъюнкции 1«или» 2, если fне заканчивается, то List( f ? 1 : 2, 3 ).tail == (f ? List( 1, 3 ) : List( 2, 3 )).tailэто неверно, потому что заканчивается левая сторона, а правая - нет.

    Принимая во внимание, что с нетерпением ни одна из сторон не заканчивается, поэтому тест на равенство никогда не достигается. Таким образом, ленивый слишком ленив с дизъюнктивными копроизведениями, и в таких случаях не завершается (включая исключения во время выполнения) после выполнения большей работы, чем хотелось бы.

[ 10 ] Декларативные продолжения и категориальная двойственность, Филински, разделы 2.5.4. Сравнение CBV и CBN и 3.6.1 CBV и CBN в SCL.

[ 11 ] Декларативные продолжения и категориальная двойственность, Филински, разделы 2.2.1 Продукты и сопутствующие товары, 2.2.2 Терминальные и начальные объекты, 2.5.2 CBV с ленивыми продуктами и 2.5.3 CBN с готовыми побочными продуктами.


Даже при программировании декларативных ограничений ограничения не изменяются, пока решатель находит решение. Это очевидно, потому что нет способа указать время для их изменения. Даже указанные ограничения по всем другим ограничениям указываются перед запуском решателя, чтобы найти решение. Это аналогично декларативным формулам в электронной таблице .
Шелби Мур III

3
Аббревиатура не означает дать определение. Где я написал «RT часто сокращается как« без побочных эффектов »», это не означает, что определение «RT» - это «без побочных эффектов», потому что у людей могут быть разные определения «эффектов». Если бы я вместо этого сказал «RT часто сокращается до xyz», бессмысленный символ не дает никакого определения RT. RT имеет точное определение, которое никогда не меняется, независимо от того, какой символ используется для ссылки на него.
Шелби Мур III

Я не могу найти контрпример к моему утверждению, что каждый вид DP - RT. Например, значение (то есть значение) терминов контекстно-зависимой грамматики не видоизменяется в другое время или положение в грамматике. Смотрите мой комментарий по программированию ограничений выше.
Шелби Мур III

1
Приравнивание C в стиле ESP к RT в монаде состояний недопустимо , поскольку каждый оператор C может изменять глобальное состояние, тогда как «внутри» монады состояния каждый соответствующий оператор генерирует COPY состояния (измененный таким образом). Последний - RT - первый нет. Монадическая композиция всегда RT. DP == RT - это единственное значение для DP, которое представляет собой непересекающийся набор атрибутов (математическое доказательство, я прав, иначе DP не имеет смысла).
Шелби Мур III

1
Хотелось бы мне понять это после первого предложения. Я читал руководство по DAX, в котором говорилось, что это «функциональный язык». Что это значит? Я не знаю, иди спроси свой поп.
Nick.McDermaid

103

На самом деле не существует однозначного, объективного определения для них. Вот как бы я их определил:

Обязательное условие - основное внимание уделяется тому, какие шаги должен предпринять компьютер, а не тому, что будет делать компьютер (например, C, C ++, Java).

Декларативный - основное внимание уделяется тому, что должен делать компьютер, а не тому, как он должен это делать (например, SQL).

Функциональный - это подмножество декларативных языков, которые уделяют большое внимание рекурсии


1
Помните несколько вещей: 1) объяснение предназначено, чтобы быть простым, а не всеобъемлющим 2) как я сказал, есть много способов определить эти языки. Таким образом, ответ вполне может быть неправильным для вас и для кого-то другого.
Джейсон Бейкер

3
Функциональное программирование не является «подмножеством декларативных языков». Декларативное программирование требует неизменности хранимых значений, функциональное программирование - нет, если это не чистый FP. Смотри мой ответ . Смотрите также объяснение ячеек электронной таблицы . Правильные объективные определения не являются «двусмысленными». Императивное программирование также ориентировано «на то, что должен делать компьютер». Только различие императивное программирование имеет дело с изменяемыми сохраненными значениями.
Шелби Мур III

5
@ShelbyMooreIII - Я склонен согласиться с Эриком Мейером в этом вопросе. На самом деле не существует такого понятия, как «не чистый функциональный язык». Насколько мне известно, Ocaml, F # и тому подобное являются императивными языками с функциональными структурами данных. Но, как я уже сказал в своем ответе, я не верю, что существует какой-либо объективный, однозначный ответ на этот вопрос. Есть несколько способов определения вещей.
Джейсон Бейкер

3
Можно математически доказать, что он объединяет термины, когда ни одно из определений не является однозначным, потому что выбранные атрибуты не являются непересекающимся множеством. Если вы определяете FP как только чистое FP (то есть RT), то оно не отличается от DP, ср. мой ответ . Непересекающиеся атрибуты FP включают в себя тип функции первого класса, который может быть императивной функцией. Я нашел более фундаментальные условия двусмысленности здесь и здесь . Предпочтение чистого FP ортогонально определению только FP.
Шелби Мур III

21
@ShelbyMooreIII - я исходил из того, что ОП хотел получить ответ на английском, а не по математике. Если это было неверное предположение, тогда мои извинения.
Джейсон Бейкер

54

императив и декларативность описывают два противоположных стиля программирования. императивом является традиционный подход «пошаговый рецепт», в то время как декларативный больше «это то, что я хочу, теперь вы решаете, как это сделать».

эти два подхода встречаются в процессе программирования - даже при использовании одного и того же языка и одной и той же программы. как правило, декларативный подход считается предпочтительным, поскольку он освобождает программиста от необходимости указывать так много деталей, а также дает меньше шансов на ошибки (если вы описываете желаемый результат, и некоторый хорошо протестированный автоматический процесс может работать в обратном направлении от этого до определите шаги, тогда вы можете надеяться, что все будет надежнее, чем указывать каждый шаг вручную).

с другой стороны, императивный подход дает вам более низкий уровень контроля - это «подход на основе микроуправления» к программированию. и это может позволить программисту использовать знания о проблеме, чтобы дать более эффективный ответ. поэтому нередки случаи, когда некоторые части программы пишутся в более декларативном стиле, но критические по скорости части должны быть более императивными.

как вы можете себе представить, язык, который вы используете для написания программы, влияет на то, насколько декларативным вы можете быть - язык, который имеет встроенные «умные способности» для определения того, что делать, если дать описание результата, что позволит сделать гораздо более декларативным подход, в котором программисту необходимо сначала добавить этот тип интеллекта с императивным кодом, прежде чем он сможет создать более декларативный уровень сверху. так, например, такой язык, как пролог, считается очень декларативным, поскольку он имеет встроенный процесс поиска ответов.

пока вы заметите, что я не упомянул функциональное программирование. это потому, что это термин, значение которого не имеет непосредственного отношения к двум другим. в самом простом, функциональном программировании это означает, что вы используете функции. в частности, вы используете язык, который поддерживает функции как «значения первого класса» - это означает, что вы можете не только писать функции, но вы можете писать функции, которые пишут функции (которые пишут функции, которые ...), и передают функции в функции. короче говоря - эти функции такие же гибкие и распространенные, как такие вещи, как строки и числа.

тогда может показаться странным, что функциональные, императивные и декларативные часто упоминаются вместе. Причина этого - следствие принятия идеи функционального программирования "до крайности". функция, в ее самом чистом смысле, является чем-то из математики - своего рода «черный ящик», который требует некоторого ввода и всегда дает один и тот же вывод. и такого рода поведение не требует хранения изменяющихся переменных. поэтому, если вы разрабатываете язык программирования, целью которого является реализация очень чистой, математически под влиянием идеи функционального программирования, вы в конечном итоге отвергаете в значительной степени идею значений, которые могут изменяться (в определенном, ограниченном, техническом смысле).

и если вы делаете это - если вы ограничиваете, как переменные могут изменяться - тогда почти случайно вы в конечном итоге заставляете программиста писать программы, которые являются более декларативными, потому что большая часть императивного программирования описывает, как изменяются переменные, и вы больше не можете сделай это! получается, что функциональное программирование, в частности программирование на функциональном языке, имеет тенденцию давать более декларативный код.

Подводя итог, то:

  • императив и декларативный являются двумя противоположными стилями программирования (одни и те же имена используются для языков программирования, которые поощряют эти стили)

  • Функциональное программирование - это стиль программирования, в котором функции становятся очень важными и, как следствие, изменение значений становится менее важным. ограниченная способность указывать изменения в ценностях заставляет более декларативный стиль.

поэтому «функциональное программирование» часто называют «декларативным».


5
Лучшее объяснение до сих пор. Кажется, что функциональные и ООП ортогональны императивному и декларативному.
Дидье А.

Вы бы сказали, что логическое программирование декларативно? Или это само по себе ортогонально?
Дидье А.

51

В двух словах:

Императивный язык specfies ряд инструкций , которые компьютер выполняет в последовательности (сделать это, то сделать это).

Декларативный язык объявляет набор правил о том, что результаты должны быть результатом каких входов (например, если у вас есть, то результат B). Движок применит эти правила к входным данным и выдаст выходные данные.

Функциональный язык объявляет набор математических / логических функций , которые определяют , как вход переводятся на выход. например. f (y) = y * y. это тип декларативного языка.


1
Функциональное программирование не «типом декларативного языка». Декларативное программирование требует неизменности хранимых значений, нечистое функциональное программирование - нет. Смотри мой ответ . Смотрите также объяснение ячеек электронной таблицы . Только причина , обязательно логика (он же инструкции) выполняются в последовательности является то , что из - за наличия изменяемых сохраненных значений, результат зависит от порядка оценки. Используя ваш словарный запас, «инструкция» может (и «правило» не может) работать с изменяемыми значениями.
Шелби Мур III

23

Императив: как достичь нашей цели

   Take the next customer from a list.
   If the customer lives in Spain, show their details.
   If there are more customers in the list, go to the beginning

Декларативный: чего мы хотим достичь

   Show customer details of every customer living in Spain

Вы описываете функциональное программирование против не-FP, не декларативное или императивное программирование. Функциональное программирование ортогонально полярности между императивным и декларативным программированием. Декларативное программирование требует неизменности хранимых значений, нечистое функциональное программирование - нет. Смотри мой ответ .
Шелби Мур III

22

Императивное программирование означает любой стиль программирования, в котором ваша программа структурирована из инструкций, описывающих, как будут выполняться операции, выполняемые компьютером .

Декларативное программирование означает любой стиль программирования, в котором ваша программа представляет собой описание проблемы или решения, но в ней явно не указано, как будет выполняться работа .

Функциональное программирование - это программирование путем оценки функций и функций функций ... Поскольку (строго определенное) функциональное программирование означает программирование путем определения свободных от побочных эффектов математических функций, поэтому оно является формой декларативного программирования, но не единственным видом декларативного программирования. ,

Логическое программирование (например, в Прологе) является еще одной формой декларативного программирования. Он включает вычисления, решая, является ли логическое утверждение истинным (или может ли оно быть удовлетворено). Программа, как правило, представляет собой набор фактов и правил - то есть описание, а не набор инструкций.

Термин перезапись (например, CASL) является еще одной формой декларативного программирования. Это включает в себя символическое преобразование алгебраических терминов. Это полностью отличается от логического программирования и функционального программирования.


Функциональное программирование не является «формой декларативного программирования». Декларативное программирование требует неизменности хранимых значений, нечистое функциональное программирование - нет. Смотри мой ответ . Смотрите также объяснение ячеек электронной таблицы . Термин «работа» в «описать, как сделать работу» не определен. Только причина , важно , логика ( так называемые «инструкции») выполняются в последовательности является то , что из - за наличия изменяемых сохраненных значений, результат зависит от порядка оценки.
Шелби Мур III

2
Пожалуйста, примите это как прочитанное, что я говорил о чисто функциональном программировании . Эти парадигмы могут пересекаться, и я не хочу увязать, сравнивая гибридные языки. Теоретически, по крайней мере, функциональное программирование связано с функциями, а не с описанием того, как компьютер будет выполнять каждое вычисление, поэтому я утверждаю, что оно декларативно.
Дафидд Рис

Я отредактировал свой ответ и в разделе «Функциональное программирование» добавил сценарий, в котором можно утверждать, что FP всегда чист, а нечистый FP - это действительно «процедурное программирование». Извиняюсь за то, что не включил эту интерпретацию ранее.
Шелби Мур III

13

императив - выражения описывают последовательность действий, которые нужно выполнить (ассоциативно)

декларативные - выражения - это объявления, которые способствуют поведению программы (ассоциативные, коммутативные, идемпотентные, монотонные)

функционал - выражения имеют значение как единственный эффект; семантика поддерживает эквациональное мышление


1
Декларативные выражения способствуют предполагаемому поведению программы, императивы могут способствовать предполагаемому или непреднамеренному. Декларативный не должен быть коммутативным и идемпотентным, если это преднамеренная семантика. Мне нравится ваша краткая сущность функционала, поэтому я проголосовал за нее.
Шелби Мур III

10

Поскольку я написал свой предыдущий ответ, я сформулировал новое определение декларативного свойства, которое приводится ниже. Я также определил императивное программирование как двойственное свойство.

Это определение лучше того, которое я дал в моем предыдущем ответе, потому что оно лаконично и носит более общий характер. Но это может быть более трудным, чтобы понять, потому что значение теорем о неполноте, применимых к программированию и жизни в целом, людям трудно обернуть вокруг себя.

В приведенном объяснении этого определения обсуждается роль, которую чисто функциональное программирование играет в декларативном программировании.

Все экзотические типы программирования вписываются в следующую таксономию декларативного и императивного, поскольку в следующем определении утверждается, что они являются двойственными.

Декларативный и императивный

Декларативное свойство является странным, тупым, и его трудно уловить в технически точном определении, которое остается общим и не двусмысленным, потому что это наивное представление о том, что мы можем объявить значение (то есть семантику) программы, не вызывая непреднамеренных побочных эффектов. Между выражением смысла и избеганием непреднамеренных эффектов существует внутреннее противоречие, которое на самом деле вытекает из теорем о неполноте программирования и нашей вселенной.

Упрощенно, технически неточно и часто неоднозначно определять декларативное как « что делать » и обязательно как « как делать » . Неоднозначный случай - это « что » - это « как » в программе, которая выводит программу - компилятор.

Очевидно, что неограниченная рекурсия, которая делает язык Тьюринга завершенным , также аналогична семантике, а не только синтаксической структуре оценки (операционная семантика). Это логически пример, аналогичный теореме Геделя: « любая полная система аксиом также несовместна ». Обдумайте противоречивую странность этой цитаты! Это также пример, который демонстрирует, что выражение семантики не имеет доказуемой границы, поэтому мы не можем доказать 2, что программа (и аналогично ее семантике) останавливается, как теорема Остановки.

Теоремы о неполноте вытекают из фундаментальной природы нашей вселенной, которая, как указано во Втором законе термодинамики, гласит, что « энтропия (то есть число независимых возможностей) стремится к максимуму навсегда ». Кодирование и дизайн программы никогда не заканчиваются - она ​​жива! - потому что она пытается удовлетворить потребности реального мира, а семантика реального мира всегда меняется и имеет тенденцию к увеличению возможностей. Люди никогда не перестают открывать новые вещи (в том числе ошибки в программах ;-).

Чтобы точно и технически охватить это вышеупомянутое желаемое понятие в этой странной вселенной, которая не имеет границ (учтите, что «вне» нашей вселенной нет), требуется краткое, но обманчиво-непростое определение, которое будет звучать неправильно, пока оно не будет объяснено глубоко.

Определение:


В декларативном свойстве может существовать только один возможный набор операторов, которые могут выражать каждую конкретную модульную семантику.

Императивное свойство 3 является двойственным, где семантика несовместима по составу и / или может быть выражена с помощью вариаций наборов утверждений.


Это определение декларативного является исключительно локальным в семантической области, что означает, что оно требует, чтобы модульная семантика поддерживала свое непротиворечивое значение независимо от того, где и как она была реализована и использована в глобальной области. Таким образом, каждая декларативная модульная семантика должна быть ортогональной по отношению ко всем возможным другим, а не невозможным (из-за теорем о неполноте) глобальным алгоритмом или моделью для подтверждения последовательности, что также является точкой « Больше не всегда лучше » Роберта Харпера, профессора компьютерных наук в Университете Карнеги-Меллона, один из разработчиков Standard ML.

Примеры этих модульных декларативной семантики включают категории теории функторы , например , вApplicative , номинальный ввод, пространств имен, названных полей и WRT на операционном уровне семантики то чисто функционального программирования.

Таким образом, хорошо разработанные декларативные языки могут более четко выражать значение , хотя и с некоторой потерей общности в том, что может быть выражено, и в то же время выигрывают в том, что можно выразить с внутренней последовательностью.

Примером вышеупомянутого определения является набор формул в ячейках программы для работы с электронными таблицами, которые, как ожидается, не будут иметь одинакового значения при перемещении в разные ячейки столбцов и строк, то есть изменились идентификаторы ячеек. Идентификаторы ячеек являются частью и не являются лишними по отношению к предполагаемому значению. Таким образом, каждый результат электронной таблицы уникален по отношению к идентификаторам ячеек в наборе формул. Последовательная модульная семантика в этом случае заключается в использовании идентификаторов ячеек в качестве ввода и вывода чистых функций для формул ячеек (см. Ниже).

Язык гипертекстовой разметки, то есть HTML, язык статических веб-страниц, является примером высоко (но не совсем 3 ) декларативного языка, который (по крайней мере, до HTML 5) не обладал способностью выражать динамическое поведение. HTML, пожалуй, самый простой язык для изучения. Для динамического поведения императивный язык сценариев, такой как JavaScript, обычно сочетался с HTML. HTML без JavaScript соответствует декларативному определению, поскольку каждый номинальный тип (т. Е. Теги) сохраняет свое непротиворечивое значение в рамках композиции в правилах синтаксиса.

Конкурентное определение для декларативного - это коммутативные и идемпотентные свойства семантических операторов, то есть, что операторы могут быть переупорядочены и продублированы без изменения значения. Например, операторы, присваивающие значения именованным полям, могут быть переупорядочены и дублированы без изменения смысла программы, если эти имена являются модульными по отношению к какому-либо подразумеваемому порядку. Имена иногда подразумевают порядок, например, идентификаторы ячеек включают их столбец и положение строки - перемещение итога в электронной таблице меняет его значение. В противном случае эти свойства неявно требуют глобальногосогласованность семантики. Как правило, невозможно разработать семантику операторов, чтобы они оставались непротиворечивыми при случайном порядке или дублировании, поскольку порядок и дублирование являются неотъемлемой частью семантики. Например, выражения «Foo существует» (или конструкция) и «Foo не существует» (и уничтожение). Если кто-то считает случайное несоответствие эндемичным для предполагаемой семантики, то он принимает это определение как достаточно общее для декларативного свойства. По сути, это определение является бесполезным как обобщенное определение, потому что оно пытается сделать последовательность ортогональной семантике, то есть игнорировать тот факт, что вселенная семантики динамически не ограничена и не может быть охвачена в глобальной парадигме когерентности.

Требование коммутативных и идемпотентных свойств для (структурной оценки порядка) операционной семантики нижнего уровня преобразует операционную семантику в декларативную локализованную модульную семантику, например, чисто функциональное программирование (включая рекурсию вместо императивных циклов). Тогда порядок выполнения деталей реализации не влияет (то есть распространяется глобально ) на согласованность семантики более высокого уровня. Например, порядок оценки (и теоретически также дублирования) формул электронной таблицы не имеет значения, потому что выходные данные не копируются на входные данные до тех пор, пока не будут вычислены все выходные данные, т.е. аналогично чистым функциям.

C, Java, C ++, C #, PHP и JavaScript не являются особенно декларативными. Синтаксис Copute и синтаксис Python более декларативно связаны с намеченными результатами , то есть согласованной синтаксической семантикой, которая устраняет посторонние, так что можно легко понять код после того, как он его забыл. Copute и Haskell усиливают детерминизм операционной семантики и поощряют « не повторяться » (DRY), потому что они допускают только чисто функциональную парадигму.


2 Даже там, где мы можем доказать семантику программы, например, с помощью языка Coq, это ограничивается семантикой, выраженной в типизации , и типизация никогда не может охватить всю семантику программы, даже для языков, которые не полный по Тьюрингу, например, с помощью HTML + CSS можно выразить несовместимые комбинации, которые, таким образом, имеют неопределенную семантику.

3 Многие объяснения неправильно утверждают, что только императивное программирование имеет синтаксически упорядоченные операторы. Я прояснил эту путаницу между императивным и функциональным программированием . Например, порядок операторов HTML не уменьшает согласованность их значения.


Изменить: я разместил следующий комментарий в блоге Роберта Харпера:

в функциональном программировании ... диапазон изменения переменной является типом

В зависимости от того, как можно отличить функциональное от императивного программирования, ваш «присваиваемый» в императивной программе также может иметь тип, ограничивающий его изменчивость.

Единственное неразборчивое определение, которое я в настоящее время ценю для функционального программирования, - это: а) функции в качестве объектов и типов первого класса, б) предпочтение рекурсии над циклами и / или в) чистые функции, то есть те функции, которые не влияют на желаемую семантику программы, когда она запоминается ( таким образом, совершенно чистого функционального программирования не существует в денотационной семантике общего назначения из-за воздействий операционной семантики, например, выделения памяти ).

Идемпотентное свойство чистой функции означает, что вызов функции для ее переменных может быть заменен ее значением, что обычно не относится к аргументам императивной процедуры. Чистые функции кажутся декларативными по отношению к несоставленным переходам между типами ввода и результата.

Но состав чистых функций не поддерживает такую ​​согласованность, потому что можно моделировать императивный процесс побочного эффекта (глобального состояния) на чистом функциональном языке программирования, например, IOMonad на Haskell, и, кроме того, совершенно невозможно предотвратить это в любой тьюринговский полный чисто функциональный язык программирования.

Как я писал в 2012 году, что похоже на аналогичное согласие с комментариями в вашем недавнем блоге , что декларативное программирование является попыткой уловить идею, что предполагаемая семантика никогда не бывает непрозрачной. Примерами непрозрачной семантики являются зависимость от порядка, зависимость от стирания семантики более высокого уровня на уровне операционной семантики (например, приведения не являются преобразованиями, а уточненные генерики ограничивают семантику более высокого уровня ), а также зависимость от значений переменных, которые нельзя проверить (доказано). правильно) языком программирования.

Таким образом, я пришел к выводу, что только полные языки без тьюринга могут быть декларативными.

Таким образом, одним однозначным и отличительным признаком декларативного языка может быть то, что его вывод может быть доказан как подчиняющийся некоторому перечисляемому набору порождающих правил. Например, для любой конкретной HTML-программы (без учета различий в способах расхождения интерпретаторов), которая не является сценарием (т.е. не завершена по Тьюрингу), ее выходная изменчивость может быть перечисляемой. Или, более кратко, программа HTML является чистой функцией ее изменчивости. То же самое относится к программе электронных таблиц, которая является чистой функцией ее входных переменных.

Поэтому мне кажется, что декларативные языки являются антитезой неограниченной рекурсии , т.е. согласно второй теореме Гёделя о неполноте самореференциальные теоремы не могут быть доказаны.

Лези Лэмпорт написал сказку о том, как Евклид мог обойти теоремы Гёделя о неполноте, примененные к математическим доказательствам в контексте языка программирования, для сравнения между типами и логикой (соответствие Карри-Говарда и т. Д.).


Роберт Харпер, похоже, согласен со мной по поводу бессмысленности большинства определений декларативного , но я не думаю, что он видел мое выше. Он приближается к моему определению, где обсуждает денотационную семантику, но не доходит до моего определения. Модель (Денотационная семантика) выше уровня .
Шелби Мур III

7

Императивное программирование: рассказывать «машине», как что-то сделать, и в результате произойдет то, что вы хотите.

Декларативное программирование: рассказать «машине» о том, что вы хотели бы случиться, и дать компьютеру понять, как это сделать.

Пример императива

function makeWidget(options) {
    const element = document.createElement('div');
    element.style.backgroundColor = options.bgColor;
    element.style.width = options.width;
    element.style.height = options.height;
    element.textContent = options.txt;

    return element;
}

Пример декларативного

function makeWidget(type, txt) {
    return new Element(type, txt);
}

Примечание. Разница не в краткости, сложности или абстракции. Как уже говорилось, разница в том, как против чего .


2
хорошо, но лучше, если вы предоставите хотя бы один пример для обоих!
Пардип Джейн

4

В наше время новый фокус: нам нужны старые классификации?

В императиве / декларативный / Функциональные аспекты были хорошо в прошлом , чтобы классифицировать общие языки, но в настоящее время все «большом языке» (как Java, Python, JavaScript и т.д.) имеют некоторый вариант (обычно Каркасы ) , чтобы выразить с «другим фокусом» чем его основной (обычный императив), и выражать параллельные процессы, декларативные функции, лямбды и т. д.

Итак, хороший вариант этого вопроса: «Какой аспект хорош для классификации фреймворков сегодня?» ... Важным аспектом является то, что мы можем обозначить как "стиль программирования" ...

Фокус на слияние данных с алгоритмом

Хороший пример для объяснения. Как вы можете прочитать о JQuery в Википедии ,

Набор основных функций jQuery - выбор элементов DOM, обход и манипулирование - с помощью его механизма выбора (...) создал новый «стиль программирования», алгоритмы слияния и структуры данных DOM

Таким образом, jQuery - лучший (популярный) пример сосредоточения внимания на «новом стиле программирования» , то есть не только на объектной ориентации, это « Объединение алгоритмов и структур данных ». JQuery несколько реактивен, так как электронные таблицы, но не «ориентированные на ячейки», « ориентированные на DOM-узлы » ... Сравнение основных стилей в этом контексте:

  1. Без слияния : во всех «больших языках», в любом функциональном / декларативном / императивном выражении, обычным является «не слияние» данных и алгоритма, за исключением некоторой объектной ориентации, то есть слияние с точки зрения строгой алгебраической структуры .

  2. Некоторое слияние : все классические стратегии слияния, в настоящее время имеют некоторую структуру, использующую его в качестве парадигмы ... поток данных , программирование на основе событий (или старые специфичные для домена языки, такие как awk и XSLT ) ... Как и программирование с современными электронными таблицами, они также примеры стиля реактивного программирования .

  3. Большое слияние : это "стиль jQuery" ... jQuery - это предметно-ориентированный язык, ориентированный на " алгоритмы слияния и структуры данных DOM ".
    PS: другие «языки запросов», такие как XQuery, SQL (с PL в качестве опции императивного выражения) также являются примерами объединения алгоритмов данных, но они являются островками , без объединения с другими системными модулями ... Spring , когда используется find()-variants и Спецификации , является еще одним хорошим примером объединения.


3

Декларативное программирование - это программирование, выражающее некоторую вневременную логику между входом и выходом, например, в псевдокоде, следующий пример будет декларативным:

def factorial(n):
  if n < 2:
    return 1
  else:
    return factorial(n-1)

output = factorial(argvec[0])

Мы просто определяем здесь отношение, называемое «факториалом», и определяем отношение между выходом и входом как это отношение. Как должно быть очевидно здесь, любой структурированный язык позволяет декларативное программирование в некоторой степени. Основная идея декларативного программирования - неизменяемые данные: если вы присваиваете переменную, вы делаете это только один раз, а потом никогда. Другие, более строгие определения влекут за собой то, что побочных эффектов вообще не может быть, эти языки иногда называют «чисто декларативными».

Тот же результат в императивном стиле будет:

a = 1
b = argvec[0]
while(b < 2):
  a * b--

output = a

В этом примере мы не выражали вечных статических логических отношений между входом и выходом, мы меняли адреса памяти вручную, пока один из них не сохранил желаемый результат. Должно быть очевидно, что все языки допускают декларативную семантику в некоторой степени, но не все допускают императив, некоторые «чисто» декларативные языки допускают побочные эффекты и мутации в целом.

Говорят, что декларативные языки часто указывают «что должно быть сделано», а не «как это сделать». Я думаю, что это неправильно, декларативные программы все еще определяют, как нужно переходить от ввода к выводу, но по-другому, указанные вами отношения должны быть эффективно вычислимыми (важный термин, ищите его, если вы его не знаете). Другим подходом является недетерминированное программирование, которое на самом деле просто указывает, каким условиям удовлетворяется результат, прежде чем ваша реализация просто исчерпает все пути проб и ошибок, пока не добьется успеха.

К чисто декларативным языкам относятся Haskell и Pure Prolog. Скользящая шкала от одного к другому будет: Pure Prolog, Haskell, OCaml, Scheme / Lisp, Python, Javascript, C--, Perl, PHP, C ++, Pascall, C, Fortran, Assembly


Вы не определили функциональное программирование. Вы неправильно подразумевали, «некоторые« чисто »декларативные языки», что декларативное программирование может быть нечистым . Декларативное программирование требует неизменности хранимых значений, императивное программирование - нет. Смотри мой ответ . Неизменность - это "неподвластное времени" качество - обратите внимание, что ваши декларативные значения factorialне изменяются.
Шелби Мур III

3

Некоторые хорошие ответы здесь относительно отмеченных «типов».

Я представляю некоторые дополнительные, более «экзотические» концепции, часто связанные с толпой функционального программирования:

  • Домен-специфический язык или DSL- программирование: создание нового языка для решения рассматриваемой проблемы.
  • Мета-программирование : когда ваша программа пишет другие программы.
  • Эволюционное программирование : где вы создаете систему, которая постоянно совершенствуется или генерирует последовательно лучшие поколения подпрограмм.

3

Я думаю, что ваша таксономия неверна. Есть два противоположных типа императивных и декларативных. Функциональный это просто подтип декларативного. Кстати, Википедия утверждает тот же факт.


+1: Да, парадигмы - это яблоки и апельсины.
Nikhil Chelliah

FP - это не просто декларативный подтип. FP ортогональна полярности императива против DP. DP требует неизменности хранимых значений, нечистый FP - нет. Википедия связывает чистый FP с FP, с абсурдным утверждением, что следующие понятия «вообще чужды императивному программированию»: первоклассные функции, рекурсия, порядок оценки и статическая типизация. Затем Википедия признает нечистым «Функциональное программирование на нефункциональных языках».
Шелби Мур III

Википедия не права в этом вопросе. Многие популярные функциональные языки позволяют программирование в «декларативном стиле», если вы выбираете, но не декларативные языки. Но то же самое можно сказать и о C, где вы все равно можете программировать в функциональном стиле, если захотите, используя void * s.
Plynx

Возможно, мне следовало бы быть более ясным в этом вопросе, но с другой стороны, я бы не стал связываться с не совсем уместными (imo) деталями. Я вижу, что функциональные языки, как правило, используются декларативно. Вы можете попытаться написать декларативно и / или функционально в ASM или C, или вы можете написать императивную программу на Лиспе, но я сомневаюсь, что это будет очень полезно или информативно для автора вопроса. Поэтому, по сути, я все еще считаю свой ответ уместным, даже если бы он мог быть сформулирован иначе.
Рорик

2

Короче говоря, чем больше стиль программирования подчеркивает то, что (чтобы сделать), абстрагируясь от деталей того, как (чтобы сделать это), тем больше этот стиль считается декларативным. Противоположность верна для императива. Функциональное программирование связано с декларативным стилем.


Смотрите мои комментарии ниже других ответов. ФП не всегда декларативный. Что против того, как неправильная таксономия для IP против DP, так как у DP и IP есть логика, которая включает что и как.
Шелби Мур III
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.