Разница между логическим программированием и функциональным программированием - PullRequest
64 голосов
/ 28 ноября 2011

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

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

Ответы [ 8 ]

59 голосов
/ 28 ноября 2011

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

На мой взгляд, основное различие между функциональным и логическим программированием - это «строительные блоки»: функциональное программирование использует функции, в то время как логическое программирование использует предикаты.Предикат не является функцией;у него нет возвращаемого значения.В зависимости от значения его аргументов это может быть истина или ложь;если некоторые значения не определены, он попытается найти значения, которые сделали бы предикат истинным.

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

Когда вы пишете предикат пролога, вы определяете условие рога: foo :- bar1, bar2, bar3. означает, что foo равно true, если bar1, bar2 и bar3 - true.обратите внимание, что я не сказал, если и только если;Вы можете иметь несколько предложений для одного предиката:

foo:-
   bar1.
foo:-
  bar2.

означает, что foo истинно, если bar1 истинно или если bar2 истинно

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

foo(x,y) -> x+y.

можно записать как

foo(X, Y, ReturnValue):-
   ReturnValue is X+Y.

, но я думаю, что такие утверждения немного вводят в заблуждение

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

abs(x) -> 
   if x>0 x else -x

или даже использовать охранники:

abs(x) x>0 -> x;
abs(x) x=<0 -> -x.

, но вы не можете написать

abs(x) ->
   x>0,
   x;
abs(x) ->
   -x.

с другой стороны, в Прологе вы могли бынапишите

abs(X, R):-
   X>0,
   R is X.
abs(X, R):-
   R is -X.

, если затем вы вызовете abs(-3, R), Prolog попытается выполнить первое предложение и потерпит неудачу, когда выполнение достигнет точки -3 > 0, но вы не получите ошибку;Пролог попробует второе предложение и вернет R = 3.

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

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

26 голосов
/ 28 ноября 2011

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

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

factorial 0 = 1                       // a factorial of 0 is 1
factorial n = n * factorial (n - 1)   // a factorial of n is n times factorial of n - 1 

В логическом программировании ваша программа представляет собой набор предикатов. Предикаты обычно определяются как наборы предложений, где каждое предложение может быть определено с использованием математических выражений, других определенных предикатов и исчисления высказываний. Например, вы можете определить предикат 'factorial', который сохраняется всякий раз, когда второй аргумент является факториалом first:

factorial(0, 1).               // it is true that a factorial of 0 is 1
factorial(X, Y) :-             // it is true that a factorial of X is Y, when all following are true:
    X1 is X - 1,                   // there is a X1, equal to X - 1,
    factorial(X1, Z),              // and it is true that factorial of X1 is Z, 
    Y is Z * X.                    // and Y is Z * X

Оба стиля позволяют использовать математические выражения в программах.

21 голосов
/ 28 ноября 2011

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

Но вы хотите знать различия.

Так что я возьму Хаскель с одной стороны и Пролог с ограничениями с другой. Практически все современные системы Prolog предлагают ограничения, такие как B, Ciao, ECLiPSe, GNU, IF, SICStus, SWI, YAP, XSB. В качестве аргумента я буду использовать очень простое ограничение dif/2, означающее неравенство, которое присутствовало даже в самой первой реализации Пролога - поэтому я не буду использовать ничего более продвинутого, чем это.

Чего не хватает функциональному программированию

Самое фундаментальное различие вращается вокруг понятия переменной. В функциональном программировании переменная обозначает конкретное значение. Это значение не должно быть полностью определено, но только те части, которые определены, могут использоваться в вычислениях. Рассмотрим в Хаскеле:

> let v = iterate (tail) [1..3] 
> v
[[1,2,3],[2,3],[3],[],*** Exception: Prelude.tail: empty list

После 4-го элемента значение не определено. Тем не менее, вы можете безопасно использовать первые 4 элемента:

> take 4 v
[[1,2,3],[2,3],[3],[]]

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

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

?- length(Xs,3).
Xs = [_G323, _G326, _G329].

В этом ответе элементы списка являются переменными. Все возможные экземпляры этих переменных являются допустимыми решениями. Как Xs = [1,2,3]. Теперь предположим, что первый элемент должен отличаться от остальных элементов:

?- length(Xs,3), Xs = [X|Ys], maplist(dif(X), Ys).
Xs = [X, _G639, _G642],
Ys = [_G639, _G642],
dif(X, _G642),
dif(X, _G639).

Позже мы можем потребовать, чтобы все элементы в Xs были равны. Прежде чем я напишу это, я попробую это один:

?- maplist(=(_),Xs).
Xs = [] ;
Xs = [_G231] ;
Xs = [_G231, _G231] ;
Xs = [_G231, _G231, _G231]  ;
Xs = [_G231, _G231, _G231, _G231] .

Видите, что ответы содержат всегда одну и ту же переменную? Теперь я могу объединить оба запроса:

?- length(Xs,3), Xs = [X|Ys], maplist(dif(X), Ys), maplist(=(_),Xs).
false.

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

Эта общность позволила разработать несколько языков ограничений, которые предлагаются в качестве библиотек для систем Prolog, наиболее выдающимися являются CLPFD и CHR .

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

Чего не хватает в логическом программировании

Но есть много вещей, которых не хватает в логическом программировании, которые делают функциональное программирование настолько интересным. В частности:

Программирование высшего порядка. Функциональное программирование имеет здесь очень давнюю традицию и разработало богатый набор идиом. Для Пролога первые предложения относятся к началу 1980-х годов, но они все еще не очень распространены. По крайней мере, ISO Prolog теперь имеет гомолог для применения под названием call/2, call/3 ....

Лямбда: Опять же, можно расширить логическое программирование в этом направлении, наиболее известной системой является Лямбда-пролог . Совсем недавно лямбды были разработаны также для ISO Prolog .

Системы типов: были попытки, подобно Меркурию, но он не завоевал особого внимания. И нет системы с функциональностью, сопоставимой с классами типов.

Чистота: Haskell полностью чист, функция Integer -> Integer - это функция. Нет мелкого шрифта, скрывающегося вокруг. И все же вы можете выполнять побочные эффекты. Сопоставимые подходы развиваются очень медленно.

Есть много областей, в которых функциональное и логическое программирование более или менее пересекаются. Например, откат назад, ленивость и списки, ленивая оценка и freeze/2, when/2, block. DCG и монады. Я оставлю обсуждение этих вопросов другим ...

16 голосов
/ 29 ноября 2011

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

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

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

В функциональном программировании мы можем считать append примерно таким:

append [] ys = ys
append (x:xs) ys = x : append xs ys

В логическом программировании мы можем считать append примерно таким:

append([], Ys, Ys).
append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs).

Они реализуют один и тот же алгоритм и даже работают в основном одинаково, но они "означают" что-то оченьразные.

Функционал append определяет список, который получается в результате добавления ys в конец xs.Мы думаем о append как о функции из двух списков в другом списке, и система времени выполнения предназначена для вычисления результата функции, когда мы вызываем ее в двух списках.

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

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

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

4 голосов
/ 09 января 2013

Я думаю, что разница в следующем:

  • императивное программирование = действия по моделированию
  • программирование функций = логическое обоснование моделирования
  • логическое программирование = знание моделирования

выбери то, что тебе больше по душе

4 голосов
/ 28 ноября 2011

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

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

1 голос
/ 01 ноября 2017

функциональное программирование: при 6 вечера свет горит.логическое программирование: когда темно, свет включен.

0 голосов
/ 10 июля 2018

Разница между функциональным программированием и императивным программированием основана на двух концепциях: -

a: - Что делать? б: - Как это сделать?

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

...