逻辑编程和函数编程之间的区别

时间:2011-11-28 14:48:31

标签: haskell functional-programming prolog

我一直在阅读许多文章,试图理解功能和逻辑编程之间的区别,但到目前为止我能够做到的唯一推论是逻辑编程通过数学表达式定义程序。但是这样的事情与逻辑编程无关。

我真的很感激功能和逻辑编程之间的区别。

8 个答案:

答案 0 :(得分:58)

我不会说逻辑编程通过数学表达式定义程序;这听起来更像是函数式编程。逻辑编程使用逻辑表达式(好吧,最终逻辑是数学)。

在我看来,功能和逻辑编程之间的主要区别在于“构建块”:函数编程使用函数,而逻辑编程使用谓词。谓词不是函数;它没有返回值。根据它的参数值,它可能是真的,也可能是假的;如果某些值未定义,它将尝试找到使谓词成立的值。

Prolog特别使用属于一阶逻辑的名为Horn clauses的特殊形式的逻辑子句; Hilog使用更高阶逻辑的子句。

当你写一个prolog谓词时,你正在定义一个号角子句: foo :- bar1, bar2, bar3.表示如果bar1,bar2和bar3为true,则foo为true。 请注意,我没有说是否,仅当;你可以为一个谓词设置多个子句:

foo:-
   bar1.
foo:-
  bar2.

表示如果bar1为true或bar2为true,则foo为true

有人说逻辑编程是函数式编程的超集,因为每个函数都可以表示为谓词:

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.

另一方面,在Prolog中你可以写

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

如果你调用abs(-3, R),Prolog会尝试第一个子句,并在执行到达-3 > 0时失败,但你不会得到错误; Prolog将尝试第二个子句并返回R = 3

我认为函数式语言不可能实现类似的东西(但我没有使用过这样的语言)。

总而言之,虽然两种范式都被认为是陈述性的,但它们却截然不同;如此不同,比较他们感觉就像比较功能和命令式的风格。我建议尝试一些逻辑编程;这应该是一个令人难以置信的经历。但是,你应该尝试理解哲学而不是简单地编写程序; Prolog允许您以功能性甚至命令式的方式编写(具有可怕的结果)。

答案 1 :(得分:26)

简而言之:

在函数式编程中,您的程序是一组函数定义。每个函数的返回值都被计算为一个数学表达式,可能使用传递的参数和其他定义的函数。例如,您可以定义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(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

两种风格都允许在程序中使用数学表达式。

答案 2 :(得分:21)

首先,功能和逻辑编程之间存在许多共性。也就是说,在一个社区中开发的许多概念也可以在另一个社区中使用。两种范式都是从粗略的实施开始,并努力实现纯洁。

但你想知道差异。

所以我将在一方面采用Haskell而另一方面采用限制Prolog。实际上所有当前的Prolog系统都提供某种约束,如B,Ciao,ECLiPSe,GNU,IF,SICStus,SWI,YAP,XSB。为了论证,我将使用一个非常简单的约束dif/2意味着不平等,即使在第一个Prolog实现中也存在 - 所以我不会使用比它更高级的东西。

缺少什么功能编程

最根本的区别在于变量的概念。在函数式编程中,变量表示具体值。此值不能完全定义,但只有那些定义的部分才能用于计算。在Haskell中考虑一下:

> 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.

所以我们在这里展示的是没有3个元素列表,其中第一个元素与其他元素不同,所有元素都相等。

这种通用性允许开发几种约束语言,这些语言作为库提供给Prolog系统,最突出的是CLPFDCHR

在函数式编程中没有直接的方法来获得类似的功能。你可以模仿事物,但仿真并不完全相同。

缺少什么逻辑编程

但是逻辑编程中缺少很多东西使函数式编程变得如此有趣。特别是:

高阶编程:函数式编程在这里有着悠久的传统,并且已经开发出了丰富的习语。对于Prolog,第一个提案可以追溯到20世纪80年代初,但它仍然不常见。至少ISO Prolog现在有一个名为call/2, call/3 ...的同源物。

Lambdas:同样,可以在这个方向上扩展逻辑编程,最突出的系统是Lambda Prolog。最近,lambdas也被开发for ISO Prolog

类型系统:曾经有过类似Mercury的尝试,但它没有那么多。并且没有任何系统具有与类型类似的功能。

纯度:Haskell完全是纯粹的,函数Integer - &gt;整数是一个函数。周围没有精美的印刷品。你仍然可以执行副作用。可比方法的发展非常缓慢。

功能和逻辑编程或多或少重叠的领域很多。例如,回溯和懒惰以及列表推导,延迟评估和freeze/2when/2block。 DCG和monads。我将把这些问题留给其他人......

答案 3 :(得分:16)

逻辑编程和函数编程使用不同的“隐喻”进行计算。这通常会影响您对生成解决方案的看法,有时意味着不同的算法自然而然地成为函数式程序员而不是逻辑程序员。

两者都基于数学基础,为“纯”代码提供更多好处;不具有副作用的代码。这两种范式都有强制纯度的语言,以及允许不受约束的副作用的语言,但在文化上,这些语言的程序员往往仍然重视纯洁。

我将考虑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定义了三个列表之间的关系,如果第三个列表是第一个列表的元素,后跟第二个列表的元素,则为true。我们认为append谓词,对于任何3个给定列表都是true或false,并且运行时系统旨在查找在我们调用它时使该谓词成立的值一些参数绑定到特定列表,一些参数未绑定。

使逻辑append与众不同的是,您可以使用它来计算将一个列表附加到另一个列表而产生的列表,但您也可以 使用它来计算列表需要追加到另一个的末尾以获得第三个列表(或者是否存在这样的列表),来计算您需要附加另一个列表才能获得第三个列表的列表,< strong>或为您提供两个可能的列表,这些列表可以附加在一起以获得给定的第三个(并探索所有可能的方法)。

虽然相当于你可以做任何事情,你可以在另一个中做一些事情,但它们会导致你对编程任务的不同思考方式。要在函数式编程中实现某些功能,您需要考虑如何根据其他函数调用的结果生成结果(您可能还需要实现)。要在逻辑编程中实现某些东西,你要考虑你的参数之间的关系(其中一些是输入,一些是输出,而不一定是从调用到调用的相同)将意味着所需的关系。

答案 4 :(得分:4)

Prolog,作为一种逻辑语言,为您提供免费回溯,非常明显。

详细说明,而且我确切地说,我在任何范例中都不是专家,在我看来,逻辑编程在解决问题方面更好。因为这正是语言的作用(例如,当需要回溯时,这显然是明显的)。

答案 5 :(得分:4)

我认为区别在于:

  • 命令式编程=建模动作
  • 函数编程=建模推理
  • 逻辑编程=建模知识

选择最适合自己心态的

答案 6 :(得分:1)

函数式编程: 下午6点,点亮。 逻辑编程: 天黑时,点亮。

答案 7 :(得分:-1)

函数式编程和命令式编程之间的区别基于两个概念:-

a:-该怎么办? b:-怎么办?

现在想一台像刚出生的婴儿的计算机,您希望该婴儿完成任务(该怎么办?)。现在宝宝可以 如果他不了解功能编程,那么他自己就会知道如何完成该任务。现在,如果那个海湾不知道如何 完成此任务,需要程序员帮助为该概念制定逻辑,这不是命令式编程。