Что означают термины функциональное, декларативное и императивное программирование?
Функциональное, декларативное и императивное программирование
Ответы (14)
На момент написания этой статьи самые популярные ответы на этой странице были неточными и путаными в отношении декларативного и императивного определения, включая ответ, цитирующий Википедию. В некоторых ответах термины объединяются по-разному.
См. Также мое объяснение, почему программирование электронных таблиц является декларативным, независимо от того, что формулы мутировать клетки.
Кроме того, в нескольких ответах утверждается, что функциональное программирование должно быть подмножеством декларативного. От этого зависит, будем ли мы отличать функцию от процедуры. Давайте сначала разберемся с императивным и декларативным.
Определение декларативного выражения
Атрибут only, который может отличить декларативное выражение от императивного выражения, - это ссылочная прозрачность (RT) его подвыражений. Все остальные атрибуты либо являются общими для обоих типов выражений, либо являются производными от RT.
100% декларативный язык (то есть язык, в котором каждое возможное выражение - это RT) не допускает (среди других требований RT) изменение сохраненных значений, например HTML и большая часть Haskell.
Определение выражения RT
Часто считается, что ЛТ не имеет побочных эффектов. Термин эффекты не имеет точного определения, поэтому некоторые люди не согласны с тем, что никакие побочные эффекты не совпадают с RT. RT имеет точное определение:
Выражение
e
является ссылочно прозрачным, если для всех программp
каждое вхождениеe
вp
может быть заменено результатом вычисленияe
, не влияя на наблюдаемый результатp
.
Поскольку каждое подвыражение концептуально является вызовом функции, RT требует, чтобы реализация функции (т. Е. Выражение (я) внутри вызываемой функции) не могла получить доступ к изменяемому состоянию, которое является внешним для функции. (доступ к изменяемому локальному состоянию разрешен). Проще говоря, функция (реализация) должна быть чистой.
Определение чистой функции
Часто говорят, что чистая функция не имеет побочных эффектов. Термин эффекты не имеет точного определения, поэтому некоторые люди с этим не согласны.
Чистые функции имеют следующие атрибуты.
- единственный наблюдаемый результат - это возвращаемое значение.
- единственная выходная зависимость - это аргументы.
- аргументы полностью определяются до того, как будет сгенерирован какой-либо вывод.
Помните, что RT применяется к выражениям (включая вызовы функций), а чистота применяется к (реализациям) функций.
Непонятным примером нечистых функций, которые создают RT-выражения, является параллелизм, но это связано с тем, что чистота нарушена на уровне абстракции прерываний. Вам действительно не нужно этого знать. Чтобы составить RT-выражения, вы вызываете чистые функции.
Производные атрибуты RT
Любой другой атрибут, указанный для декларативного программирования, например цитата из 1999 г., использованная Википедией , либо происходит от RT, либо используется в императивном программировании. Таким образом доказывается правильность моего точного определения.
Обратите внимание, неизменность внешних значений является подмножеством требований для RT.
В декларативных языках нет структур управления циклами, например
for
иwhile
, потому что из-за неизменности условие цикла никогда не изменится.Декларативные языки не выражают поток управления, кроме порядка вложенных функций (также известного как логические зависимости), потому что из-за неизменности другие варианты порядка оценки не меняют результат (см. Ниже).
Декларативные языки выражают логические шаги (т. Е. Порядок вызова вложенных функций RT), но является ли каждый вызов функции семантикой более высокого уровня (т. Е. Что делать) не является требованием декларативного программирования. Отличие от императива в том, что из-за неизменности (то есть в более общем смысле RT) эти шаги не могут зависеть от изменяемого состояния, а только от реляционного порядка выраженной логики (то есть порядка вложенности вызовов функций , также известные как подвыражения).
Например, абзац HTML <p>
не может быть отображен до тех пор, пока не будут вычислены подвыражения (т. Е. Теги) в абзаце. Нет изменяемого состояния, только зависимость порядка из-за логической взаимосвязи иерархии тегов (вложенность подвыражений, которые являются аналогично вложенные вызовы функций).
- Таким образом, существует производный атрибут неизменности (в более общем смысле RT), который декларативные выражения выражают только логические отношения составных частей (то есть аргументов функции подвыражения ), а не отношения изменяемое состояние.
Порядок оценки
Выбор порядка оценки подвыражений может дать различный результат только в том случае, если любой из вызовов функции не является RT (т.е. функция не является чистой), например доступ к некоторому изменяемому состоянию, внешнему по отношению к функции, осуществляется внутри функции.
Например, учитывая некоторые вложенные выражения, например f( g(a, b), h(c, d) )
, нетерпеливое и ленивое вычисление аргументов функции даст те же результаты, если функции f
, g
и h
являются чистыми.
Принимая во внимание, что если функции f
, g
и h
не являются чистыми, то выбор порядка оценки может дать другой результат.
Обратите внимание, что вложенные выражения являются концептуально вложенными функциями, поскольку операторы выражений - это просто вызовы функций, маскирующиеся под унарный префикс, унарный постфикс или двоичную инфиксную нотацию.
По касательной, если все идентификаторы, например a
, b
, c
, d
, неизменяемы везде, состояние, внешнее по отношению к программе, недоступно (т.е. ввод-вывод), и нет разрыва уровня абстракции, тогда функции всегда чистые.
Кстати, у Haskell другой синтаксис, f (g a b) (h c d)
.
Подробная информация о заказе на ознакомление
Функция - это переход состояния (не изменяемое сохраненное значение) от входа к выходу. Для RT-композиций вызовов чистых функций порядок выполнения этих переходов состояний не зависит. Переход состояния каждого вызова функции не зависит от других из-за отсутствия побочных эффектов и принципа того, что функция RT может быть заменена ее кешированное значение. Чтобы исправить популярное заблуждение, чистая монадическая композиция - это всегда декларативно и RT strong >, несмотря на то, что монада IO
в Haskell является возможно нечистой и, следовательно, обязательной по отношению к состояние World
, внешнее по отношению к программе (но в смысле предупреждения ниже побочные эффекты изолированы).
Активная оценка означает, что аргументы функций оцениваются до вызова функции, а ленивая оценка означает аргументы не оцениваются до тех пор, пока (и если) они не будут доступны в функции.
Определение: функция параметры объявляются на сайте определения функции, а аргументы передаются в функции позвонить на сайт. Знайте разницу между параметром и аргументом.
По сути, все выражения являются (составом) вызовов функций, например константы - это функции без входов, унарные операторы - это функции с одним входом, двоичные инфиксные операторы - это функции с двумя входами, конструкторы - это функции, и даже управляющие операторы (например, if
, for
, while
) могут быть смоделированы с помощью функций. Чтобы эти Функции аргумента (не путать с порядком вызова вложенных функций) оцениваются не объявляется синтаксисом, например f( g() )
может нетерпеливо оценивать g
, затем f
по результату g
, или он может оценивать f
и только лениво оценивать g
, когда его результат нужен в f
.
Предостережение, никакой полный язык Тьюринга (т.е. который допускает неограниченную рекурсию) не является полностью декларативным, например ленивое вычисление вводит недетерминизм памяти и времени. Но эти побочные эффекты из-за выбора порядка оценки ограничиваются потреблением памяти, временем выполнения, задержкой, незавершением и внешний гистерезис, следовательно, внешняя синхронизация.
Функциональное программирование
Поскольку в декларативном программировании не может быть циклов, единственный способ итерации - это функциональная рекурсия. В этом смысле функциональное программирование связано с декларативным программированием.
Но функциональное программирование не ограничивается декларативным программированием. Функциональная композиция может быть противопоставлена подтипам, особенно в отношении Проблема выражения, где расширение может быть достигнуто с помощью либо добавление подтипов, либо функциональная декомпозиция. Расширение может представлять собой сочетание обеих методологий.
Функциональное программирование обычно делает функцию первоклассным объектом, то есть тип функции может появляться в грамматике везде, где это может быть любой другой тип. В результате функции могут вводить данные и работать с ними, таким образом обеспечивая разделение задач, подчеркивая композицию функций, то есть разделяя зависимости между подвычислениями детерминированного вычисления.
Например, вместо написания отдельной функции (и вместо этого используется рекурсия циклов, если функция также должна быть декларативной) для каждого из бесконечного числа возможных специализированных действий, которые могут быть применены к каждому элементу коллекции, функциональное программирование использует многократно используемые итерационные функции, например map
, fold
, filter
. Эти итерационные функции вводят первоклассную специализированную функцию действия. Эти функции итерации выполняют итерацию по коллекции и вызывают специализированную функцию действия ввода для каждого элемента. Эти функции действий более краткие, потому что им больше не нужно содержать операторы цикла для итерации коллекции.
Однако обратите внимание, что если функция не является чистой, то это действительно процедура. Возможно, мы можем утверждать, что функциональное программирование, использующее нечистые функции, на самом деле является процедурным программированием. Таким образом, если мы согласны с тем, что декларативные выражения являются RT, тогда мы можем сказать, что процедурное программирование не является декларативным программированием, и, таким образом, мы можем утверждать, что функциональное программирование всегда является RT и должно быть подмножеством декларативного программирования.
Параллелизм
Эта функциональная композиция с первоклассными функциями может выражать глубину в параллелизм, выделив независимую функцию.
Принцип Брента: вычисления с работой w и глубиной d могут быть реализованы в PRAM с p-процессором за время O (max (w / p, d)).
И параллелизм, и параллелизм также требуют декларативного программирования, т. Е. Неизменяемости и RT. .
Итак, откуда взялось это опасное предположение, что параллелизм == параллелизм? Это естественное следствие языков с побочными эффектами: когда у вашего языка есть побочные эффекты повсюду, то всякий раз, когда вы пытаетесь делать более одного дела за раз, вы, по сути, получаете недетерминизм, вызванный чередованием эффектов от каждой операции. . Таким образом, в языках с побочными эффектами единственный способ добиться параллелизма - это параллелизм; поэтому неудивительно, что мы часто видим их вместе.
Порядок оценки FP
Обратите внимание, что порядок оценки также влияет на завершение работы и побочные эффекты функциональной композиции.
Жадный (CBV) и ленивый (CBN) - категориальные поединки [10], потому что у них обратный порядок оценки, т. е. сначала оцениваются внешние или внутренние функции. Представьте себе перевернутое дерево, а затем нетерпеливые вычисления от ветвей дерева функций вверх по иерархии ветвей к стволу функции верхнего уровня; тогда как ленивый оценивает от ствола до кончиков веток. У нетерпеливого нет конъюнктивных продуктов (и / k / категориальных продуктов), а у ленивого нет дизъюнктивных сопутствующих продуктов (или, как / k / a категориальных сумм) [11].
Производительность
- Жаждущий
Как и в случае без завершения, нетерпеливый слишком нетерпелив с конъюнктивной функциональной композицией, то есть структура управления составом выполняет ненужную работу, которая не выполняется с ленивым. Например, , без необходимости отображает весь список в логические значения, если он состоит из свертки, которая заканчивается на первом истинном элементе.
Эта ненужная работа является причиной заявленного до дополнительного коэффициента log n в последовательной временной сложности нетерпеливого и ленивого как с чистыми функциями. Решение состоит в том, чтобы использовать функторы (например, списки) с ленивыми конструкторами (то есть с необязательными ленивыми продуктами), потому что с нетерпением некорректность происходит от внутренней функции. Это потому, что продукты являются конструктивными типами, т. Е. Индуктивными типами с начальной алгеброй в начальной фиксированной точке [11]
- Ленивый
Как и в случае без завершения, ленивый слишком ленив с дизъюнктивной функциональной композицией, то есть коиндуктивная завершенность может произойти позже, чем необходимо, что приведет как к ненужной работе, так и к недетерминированию опозданий, что не происходит с нетерпеливым [10] [11]. Примерами окончательности являются исключения состояния, времени, незавершенности и времени выполнения. Это императивные побочные эффекты, но даже в чистом декларативном языке (например, Haskell) в императивной монаде ввода-вывода есть состояние (примечание: не все монады императивные!), Неявное в распределении пространства, а время - это состояние относительно императивного реальный мир. Использование ленивого режима даже с необязательным активным сопродуктом приводит к утечке лени во внутренние сопутствующие продукты, поскольку с ленивым режимом лень некорректно происходит от внешней функции (см. пример в разделе «Незавершение», где == - функция внешнего бинарного оператора). Это связано с тем, что копроизведения ограничены конечностью, т. Е. Коиндуктивными типами с конечной алгеброй на конечном объекте [11].
Ленивый вызывает недетерминизм при разработке и отладке функций для задержки и пространства, отладка которых, вероятно, выходит за рамки возможностей большинства программистов из-за диссонанс между объявленной иерархией функций и порядком оценки во время выполнения. Ленивые чистые функции, оцениваемые с нетерпением, потенциально могут привести к невидимому ранее прерыванию во время выполнения. И наоборот, нетерпеливые чистые функции, оцениваемые с ленивым подходом, потенциально могут привнести ранее невидимое пространство и неопределенность задержки во время выполнения.
Без прекращения действия
Во время компиляции из-за проблемы остановки и взаимной рекурсии в полном языке Тьюринга, как правило, нельзя гарантировать завершение функций.
- Жаждущий
С нетерпением, но не ленивым, для соединения 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 с активными сопутствующими продуктами.
На самом деле для них нет однозначного, объективного определения. Вот как я их определил:
Обязательно. Основное внимание уделяется действиям, которые компьютер должен предпринять, а не тому, что он будет делать (например, C, C ++, Java).
Декларативный. Основное внимание уделяется тому, что компьютер должен делать, а не тому, как он должен это делать (например, SQL).
Функциональный - подмножество декларативных языков, в которых большое внимание уделяется рекурсии.
[Serializable]
, с помощью которых вы можете украсить свои классы и методы. Эти атрибуты также функциональны по своей природе.
- person RBT; 08.12.2016
императивный и декларативный описывают два противоположных стиля программирования. императивом является традиционный подход «пошаговый рецепт», в то время как декларативным является больше «это то, что я хочу, теперь вы решаете, как это сделать».
эти два подхода используются во всем программировании - даже с использованием одного и того же языка и одной и той же программы. как правило, декларативный подход считается предпочтительным, потому что он освобождает программиста от необходимости указывать так много деталей, а также имеет меньше шансов для ошибок (если вы описываете желаемый результат, и какой-то хорошо протестированный автоматический процесс может работать в обратном направлении от этого к определите шаги, тогда вы можете надеяться, что это более надежно, чем указывать каждый шаг вручную).
с другой стороны, императивный подход дает вам более низкий уровень контроля - это «подход микроменеджера» к программированию. и это может позволить программисту использовать знания о проблеме, чтобы дать более эффективный ответ. поэтому нет ничего необычного в том, что некоторые части программы пишутся в более декларативном стиле, а части, критичные к скорости, становятся более императивными.
как вы можете себе представить, язык, который вы используете для написания программы, влияет на то, насколько вы можете быть декларативным - язык, который имеет встроенный «ум» для решения, что делать с учетом описания результата, позволит сделать гораздо более декларативным подход, чем тот, где программисту нужно сначала добавить такого рода интеллект с помощью императивного кода, прежде чем он сможет построить более декларативный слой поверх. так, например, такой язык, как пролог, считается очень декларативным, потому что он имеет встроенный процесс, который ищет ответы.
пока вы заметите, что я не упомянул функциональное программирование. это потому, что это термин, значение которого не имеет непосредственного отношения к двум другим. в простейшем случае функциональное программирование означает, что вы используете функции. в частности, что вы используете язык, который поддерживает функции как «значения первого класса» - это означает, что вы можете не только писать функции, но и писать функции, которые пишут функции (которые пишут функции, которые ...) и передают функции в функции. Короче говоря, эти функции так же гибки и распространены, как строки и числа.
Тогда может показаться странным, что функциональное, императивное и декларативное часто упоминаются вместе. Причина этого - следствие доведения идеи функционального программирования до «крайности». функция в чистом смысле слова - это что-то из математики - своего рода «черный ящик», который принимает некоторые входные данные и всегда дает один и тот же результат. и такое поведение не требует хранения изменяющихся переменных. поэтому, если вы разрабатываете язык программирования, целью которого является реализация очень чистой, математически обусловленной идеи функционального программирования, вы в конечном итоге в значительной степени отвергаете идею ценностей, которые могут изменяться (в определенном, ограниченном, техническом смысле).
и если вы сделаете это - если вы ограничите то, как могут изменяться переменные, - то почти случайно вы в конечном итоге вынудите программиста писать программы, которые являются более декларативными, потому что большая часть императивного программирования описывает, как изменяются переменные, и вы больше не можете сделай это! Таким образом, оказывается, что функциональное программирование - в частности, программирование на функциональном языке - имеет тенденцию давать более декларативный код.
резюмируя, то:
императивный и декларативный - два противоположных стиля программирования (одни и те же имена используются для языков программирования, которые поощряют эти стили)
Функциональное программирование - это стиль программирования, в котором функции становятся очень важными, и, как следствие, изменение значений становится менее важным. ограниченная возможность указывать изменения в значениях вынуждает использовать более декларативный стиль.
поэтому «функциональное программирование» часто называют «декларативным».
В двух словах:
Императивный язык определяет последовательность инструкций, которые компьютер выполняет последовательно (сделайте то, затем сделайте то).
Декларативный язык объявляет набор правил о том, какие выходные данные должны быть результатом каких входов (например, если у вас есть A, то результатом будет B). Двигатель будет применять эти правила к входам и выдавать выходные данные.
Функциональный язык объявляет набор математических / логических функций, которые определяют, как ввод преобразуется в вывод. например. f (y) = y * y. это тип декларативного языка.
Настоятельно необходимо: как достичь нашей цели
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
Императивное программирование означает любой стиль программирования, в котором ваша программа построена из инструкций, описывающих, как будут происходить операции, выполняемые компьютером.
Декларативное программирование означает любой стиль программирования, в котором ваша программа является описанием проблемы или решения, но не указывает явно, как будет выполняться работа.
Функциональное программирование - это программирование путем оценки функций и функций функций ... Поскольку (строго определенное) функциональное программирование означает программирование путем определения математических функций без побочных эффектов, поэтому оно является формой декларативного программирования, но это не единственный вид декларативного программирования.
Логическое программирование (например, в Прологе) - это еще одна форма декларативного программирования. Он включает в себя вычисления, решающие, истинно ли логическое утверждение (или может ли оно быть выполнено). Программа обычно представляет собой набор фактов и правил, то есть описание, а не набор инструкций.
Перезапись терминов (например, CASL) - это еще одна форма декларативного программирования. Он включает в себя символическое преобразование алгебраических терминов. Это полностью отличается от логического программирования и функционального программирования.
императив - выражения описывают последовательность действий, которые необходимо выполнить (ассоциативно)
декларативное - выражения - это объявления, которые способствуют поведению программы (ассоциативное, коммутативное, идемпотентное, монотонное).
функциональный - выражения имеют значение только как эффект; семантика поддерживает эквациональные рассуждения
Поскольку я написал свой предыдущий ответ, я сформулировал новое определение декларативного свойства, которое цитируется ниже. Я также определил императивное программирование как двойственное свойство.
Это определение превосходит то, что я дал в моем предыдущем ответе, потому что оно лаконично и носит более общий характер. Но это может быть труднее понять, потому что последствия теорем о неполноте, применимые к программированию и жизни в целом, трудно осмыслить людям.
Процитированное объяснение определения обсуждает роль, которую чистое функциональное программирование играет в декларативном программировании.
Все экзотические типы программирования вписываются в следующую классификацию декларативного и императивного программирования, поскольку следующее определение утверждает, что они двойственны.
Декларативное и императивное
Декларативное свойство странно, непонятно, и его трудно уловить в технически точном определении, которое остается общим и недвусмысленным, потому что это наивное представление о том, что мы можем объявить значение (также известное как семантика) программы без непредвиденных побочных эффектов. Существует внутреннее противоречие между выражением смысла и предотвращением непреднамеренных эффектов, и это напряжение фактически происходит от теоремы о неполноте программирования и нашей вселенной.
Было бы слишком упрощенно, технически неточно и часто двусмысленно определять декларативное как «что делать» и императивное как «как делать ”. Неоднозначным случаем является то, что «что» является «как» в программе, которая выводит программу - компилятор.
Очевидно, неограниченная рекурсия, которая делает язык Тьюринга полным, также аналогичным образом присутствует в семантика - не только в синтаксической структуре оценки (также известной как операционная семантика). Логически это пример, аналогичный теореме Гёделя: «любая полная система аксиом также несовместима». Вдумайтесь в противоречивую странность этой цитаты! Это также пример, демонстрирующий, что выражение семантики не имеет доказуемой границы, поэтому мы не можем доказать 2, что программа (и аналогично ее семантика) останавливает, также известную как теорема об остановке.
Теоремы о неполноте проистекают из фундаментальной природы нашей Вселенной, которая, как сказано во втором законе термодинамики, состоит в том, что «энтропия (также известная как количество независимых возможностей) всегда стремится к максимуму em> ». Кодирование и дизайн программы никогда не заканчиваются - она живая! - потому что она пытается удовлетворить потребности реального мира, а семантика реального мира всегда меняется и стремится к большему количеству возможностей. Люди никогда не перестают открывать для себя что-то новое (включая ошибки в программах ;-).
Чтобы точно и технически уловить это вышеупомянутое желаемое понятие в этой странной вселенной, у которой нет границ (вдумайтесь, «вне» нашей вселенной нет), требуется краткое, но обманчиво-непростое определение, которое будет звучать неверно, пока не будет объяснено. глубоко.
Определение:
В декларативном свойстве может существовать только один возможный набор операторов, который может выражать каждую конкретную модульную семантику.
Императивное свойство 3 является двойным, при котором семантика несовместима по составу и / или могут быть выражены вариациями наборов утверждений.
Это определение декларативного является явно локальным в семантической области, что означает, что оно требует, чтобы модульная семантика сохраняла свое согласованное значение независимо от того, где и как она создается и используется в глобальной области. Таким образом, каждая декларативная модульная семантика должна быть изначально ортогональна всем возможным другим, а не невозможным (из-за теорем о неполноте) глобальным алгоритмом или моделью для проверки согласованности, что также является точкой « Больше не всегда лучше »Роберта Харпера, профессора компьютерных наук в Университете Карнеги-Меллона, одного из разработчиков Standard ML.
Примеры этой модульной декларативной семантики включают функторы теории категорий, например.
Applicative
, номинальная типизация, пространства имен, именованные поля и w.r.t. до операционного уровня семантики, затем чисто функционального программирования.Таким образом, хорошо разработанные декларативные языки могут более четко выражать смысл, хотя и с некоторыми потеря общности в том, что может быть выражено, но выигрыш в том, что может быть выражено с внутренней последовательностью.
Примером вышеупомянутого определения является набор формул в ячейках программы для работы с электронными таблицами, которые не должны давать одинаковое значение при перемещении в другие ячейки столбца и строки, т. Е. При изменении идентификаторов ячеек. Идентификаторы ячеек являются частью предполагаемого значения, а не лишними. Таким образом, каждый результат в электронной таблице уникален. идентификаторам ячеек в наборе формул. Согласованной модульной семантикой в этом случае является использование идентификаторов ячеек в качестве входных и выходных данных чистых функций для формул ячеек (см. Ниже).
Язык гипертекстовой разметки, также известный как HTML, язык для статических веб-страниц, является примером очень (но не идеального) 3) декларативный язык, который (по крайней мере, до HTML 5) не имел возможности выражать динамическое поведение. HTML, пожалуй, самый простой язык для изучения. Для динамического поведения императивный язык сценариев, такой как JavaScript, обычно сочетался с HTML. HTML без JavaScript соответствует декларативному определению, потому что каждый номинальный тип (то есть теги) сохраняет свое согласованное значение при композиции в рамках правил синтаксиса.
Конкурирующим определением декларативного является определение коммутативного и idempotent семантических утверждений, то есть эти утверждения можно переупорядочивать и дублировать без изменения значения. Например, операторы, присваивающие значения именованным полям, могут быть переупорядочены и дублированы без изменения смысла программы, если эти имена являются модульными w.r.t. на любой подразумеваемый заказ. Иногда имена подразумевают порядок, например идентификаторы ячеек включают их столбец и позицию строки - перемещение итога в электронной таблице меняет его значение. В противном случае эти свойства неявно требуют глобальной согласованности семантики. Как правило, невозможно спроектировать семантику операторов, чтобы они оставались согласованными при случайном порядке или дублировании, потому что порядок и дублирование являются неотъемлемой частью семантики. Например, утверждения «Foo существует» (или конструкция) и «Foo не существует» (и разрушение). Если рассматривать случайную несогласованность, присущую предполагаемой семантике, то это определение принимается как достаточно общее для декларативного свойства. По сути, это определение бессмысленно как обобщенное определение, потому что оно пытается сделать согласованность ортогональной семантике, то есть игнорировать тот факт, что универсум семантики динамически неограничен и не может быть зафиксирован в глобальной согласованности парадигма.
Требование коммутативных и идемпотентных свойств для (порядка структурной оценки) операционной семантики нижнего уровня преобразует операционную семантику в декларативную локализованную модульную семантику, например чистое функциональное программирование (включая рекурсию вместо императивных циклов). Тогда порядок работы деталей реализации не влияет (т. Е. Распространяется глобально) на согласованность семантики более высокого уровня. Например, порядок оценки (и теоретически также дублирование) формул электронной таблицы не имеет значения, потому что выходные данные не копируются во входные данные до тех пор, пока не будут вычислены все выходные данные, то есть аналогично чистым функциям.
C, Java, C ++, C #, PHP и JavaScript не особо декларативны. Синтаксис Copute и синтаксис Python более декларативно связаны с предполагаемыми результатами, т. Е. Согласованной синтаксической семантикой, исключающей посторонние так что можно легко понять код после того, как они его забыли. Copute и Haskell обеспечивают детерминизм операционной семантики и поощряют «не повторяться» (DRY), потому что они допускают только чисто функциональную парадигму.
2 Даже если мы можем доказать семантику программы, например в языке Coq это ограничено семантикой, которая выражена в типизации, и типизация никогда не может охватить всю семантику программы - даже для языков, не являющихся полными по Тьюрингу, например с помощью HTML + CSS можно выражать противоречивые комбинации, которые, таким образом, имеют неопределенную семантику.
3 Во многих объяснениях неверно утверждается, что только императивное программирование имеет синтаксически упорядоченные операторы. Я разъяснил эту путаницу между императивным и функциональным программированием. Например, порядок операторов HTML не снижает единообразия их смысла.
Изменить: я разместил следующий комментарий к блогу Роберта Харпера:
в функциональном программировании ... диапазон изменения переменной - это тип
В зависимости от того, как отличить функциональное программирование от императивного, ваш «назначаемый» в императивной программе также может иметь тип, ограничивающий его изменчивость.
Единственное не запутанное определение функционального программирования, которое я сейчас ценю, - это а) функции как первоклассные объекты и типы, б) предпочтение рекурсии перед циклами и / или в) чистые функции, т. Е. Те функции, которые не влияют на желаемую семантику. программы при запоминании (, таким образом, совершенно чистое функциональное программирование не существует в денотационной семантике общего назначения из-за воздействия операционной семантики, например, выделения памяти).
Свойство идемпотентности чистой функции означает, что вызов функции для ее переменных может быть заменен ее значением, что обычно не относится к аргументам императивной процедуры. Чистые функции кажутся декларативными относительно. к несоставным переходам состояний между типами ввода и результата.
Но композиция чистых функций не поддерживает такой согласованности, потому что можно моделировать императивный процесс побочного эффекта (глобального состояния) на чистом функциональном языке программирования, например IOMonad Haskell, и, более того, полностью невозможно предотвратить это на любом полностью чистом функциональном языке программирования Тьюринга.
Как я писал в 2012 году, консенсус комментариев в вашем недавнем блоге, это декларативное программирование - это попытка уловить представление о том, что предполагаемая семантика никогда не бывает непрозрачной. Примерами непрозрачной семантики являются зависимость от порядка, зависимость от стирания семантики более высокого уровня на уровне операционной семантики (например, приведение типов - это не преобразования, а обобщенные дженерики ограничивают семантику более высокого уровня), а также зависимость от значений переменных, которые не могут быть проверены (корректны) с помощью языка программирования.
Таким образом, я пришел к выводу, что декларативными могут быть только полные по Тьюрингу языки.
Таким образом, одним недвусмысленным и отличным признаком декларативного языка может быть то, что его выходные данные подчиняются некоторому перечислимому набору правил генерации. Например, для любой конкретной программы HTML (игнорируя различия в способах расхождения интерпретаторов), которая не написана по сценарию (т.е. не является полной по Тьюрингу), ее выходная изменчивость может быть перечислимой. Или, если говорить более кратко, программа HTML является чистой функцией своей изменчивости. То же самое и программа электронных таблиц - это чистая функция своих входных переменных.
Мне кажется, что декларативные языки являются противоположностью неограниченной рекурсии, т.е. согласно второй теореме Гёделя о неполноте теоремы о самореференции не могут быть доказаны.
Лези Лэмпорт написала сказку о том, как Евклид мог работали над теоремами Гёделя о неполноте, примененными к математическим доказательствам в контексте языка программирования, для соответствия между типами и логикой (соответствие Карри-Ховарда и т. д.).
Императивное программирование: говорите «машине», как что-то делать, и в результате произойдет то, что вы хотите.
Декларативное программирование: сообщая «машине», что вы хотите, и позволяя компьютеру выяснить, как это сделать.
Пример императивного
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);
}
Примечание: разница не в краткости, сложности или абстракции. Как уже говорилось, разница в том, как и что.
В наши дни новый фокус: нам нужны старые классификации?
Императивные / декларативные / функциональные аспекты были хороши в прошлом для классификации общих языков, но в настоящее время у всех «больших языков» (таких как Java, Python, Javascript и т. Д.) Есть некоторые опции (обычно frameworks), чтобы выразить "другой фокус", чем его основной (обычно императив), и чтобы выразить параллель процессы, декларативные функции, лямбды и т. д.
Итак, хороший вариант этого вопроса: «Какой аспект сегодня хорош для классификации фреймворков?» ... Важный аспект - это то, что мы можем обозначить как «стиль программирования». ..
Сосредоточьтесь на слиянии данных с алгоритмом
Хороший пример для объяснения. Как вы можете прочитать о jQuery в Википедии,
Набор основных функций jQuery - выбор, обход и манипулирование элементами DOM -, включенный его механизмом выбора (...), создал новый "стиль программирования", объединяющий алгоритмы и структуры данных DOM.
Итак, jQuery - лучший (популярный) пример фокусировки на "новом стиле программирования", который не только ориентирован на объект, - это "Fusing алгоритмы и структуры данных". jQuery в некоторой степени реагирует на электронные таблицы, но не «ориентирован на ячейки», он «ориентирован на DOM-узел» ... Сравнение основных стилей в этом контексте:
Никакого слияния: во всех "больших языках", в любом функциональном / декларативном / императивном выражении обычным является "отсутствие слияния" данных и алгоритма, за исключением некоторой объектной ориентации, т. е. слияние с точки зрения строгой алгебрической структуры.
Некоторое слияние: все классические стратегии слияния в настоящее время имеют некоторую структуру, использующую его как парадигму ... поток данных, событийно-ориентированное программирование (или старый домен определенные языки как awk и XSLT) ... Как и программирование с использованием современных электронных таблиц, они также являются примерами реактивное программирование.
Big fusion: это "стиль jQuery" ... jQuery - это язык для конкретной предметной области, ориентированный на "объединение алгоритмов и DOM-структуры данных".
PS: другие "языки запросов", такие как XQuery, SQL (с PL в качестве обязательного параметра выражения), также являются примерами слияния алгоритмов данных, но они являются островами, без слияния с другими системные модули ... Spring при использованииfind()
-вариантов и спецификации em> clauses - еще один хороший пример слияния.
Декларативное программирование - это программирование, выражающее некоторую вневременную логику между входом и выходом, например, в псевдокоде следующий пример будет декларативным:
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
не изменяет никакого значения.
- person Shelby Moore III; 03.12.2011
Здесь есть несколько хороших ответов относительно отмеченных "типов".
Я предлагаю несколько дополнительных, более «экзотических» концепций, часто связанных с толпой функционального программирования:
- Язык, специфичный для домена или Программирование DSL: создание нового языка для решения возникшей проблемы.
- Мета-программирование: когда ваша программа пишет другие программы.
- Эволюционное программирование: вы создаете систему, которая постоянно совершенствуется или генерирует все более совершенные поколения подпрограмм.
Я считаю, что ваша таксономия неверна. Есть два противоположных типа: императивный и декларативный. Функциональный - это всего лишь подтип декларативного. Кстати, википедия утверждает тот же факт.
Короче говоря, чем больше стиль программирования подчеркивает, что (делать), абстрагируясь от деталей того, как (делать), тем в большей степени этот стиль считается декларативным. Обратное верно для императива. Функциональное программирование связано с декларативным стилем.