逻辑编程和function编程的区别

我一直在阅读许多文章,试图理解function和逻辑编程之间的区别,但迄今为止唯一能够做出的演绎是逻辑编程通过mathexpression式来定义程序。 但是这样的事情与逻辑编程没有关系。

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

我不会说逻辑编程通过mathexpression式来定义程序, 这听起来更像是函数式编程。 逻辑编程使用逻辑expression式(当然,逻辑最终是math)。

在我看来,function和逻辑编程的主要区别是“构build块”:function编程使用function,而逻辑编程使用谓词。 谓词不是一个函数; 它没有返回值。 根据它的论点的价值,它可能是真的或假的; 如果某些值未定义,则将尝试查找将使谓词为真的值。

Prolog尤其使用了属于一阶逻辑的名为Horn子句的逻辑子句的特殊forms; Hilog使用更高阶逻辑的子句。

当你写一个prolog谓词时,你正在定义一个horn子句: foo :- bar1, bar2, bar3. 意味着如果bar1,bar2和bar3为真,则foo为真。 请注意,我并没有说只要; 对于一个谓词你可以有多个子句:

 foo:- bar1. foo:- bar2. 

意味着如果bar1为真或bar2为真,则foo为真

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

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

可以写成

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

但我认为这样的陈述有点误导

逻辑和function的另一个区别是回溯。 在函数式编程中,一旦进入函数体,就不能失败并移到下一个定义。 例如,你可以写

 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

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

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

简而言之:

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

在逻辑编程中,你的程序是一组谓词。 谓词通常被定义为子句集合,其中每个子句可以使用mathexpression式,其他定义的谓词和命题演算来定义。 例如,您可以定义一个“阶乘”谓词,它只要第二个参数是第一个阶乘,

 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 

两种样式都允许在程序中使用mathexpression式。

首先,function和逻辑编程之间有许多共同之处。 也就是说,在一个社区中发展起来的很多概念也可以用于另一个社区。 两种模式都是从相当粗糙的实施开始,并努力实现纯洁。

但是你想知道差异。

所以我会把Haskell和Prolog作为一个约束。 实际上,目前所有的Prolog系统都提供某种types的约束,比如B,Ciao,ECLiPSe,GNU,IF,SICStus,SWI,YAP,XSB。 为了说明这个问题,我将使用一个非常简单的约束dif/2来表示即使在第一个Prolog实现中也存在的不等式 – 所以我不会使用任何比这更先进的东西。

什么function编程缺乏

最根本的区别是围绕着一个variables的概念。 在函数式编程中,variables表示一个具体的值。 这个值不能完全定义,但是只有那些被定义的部分才能用于计算。 考虑在Haskell中:

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

在第四个元素之后,值是未定义的。 不过,你可以安全的使用前四个元素:

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

请注意,函数式程序中的语法被巧妙地限制,以避免variables未定义。

在逻辑编程中,variables不需要引用具体的值。 所以,如果我们想要一个3个元素的列表,我们可以说:

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

在这个答案中,列表的元素是variables。 所有这些variables的可能实例都是有效的解决scheme 像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] . 

看到答案总是包含相同的variables? 现在,我可以结合这两个查询:

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

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

这种普遍性允许开发几种约束语言,这些约束语言是作为Prolog系统的库提供的,其中最突出的是CLPFD和CHR 。

在函数式编程中没有直接的方法来获得类似的function。 你可以模仿的东西,但模拟是不完全相同的。

什么逻辑编程是缺乏的

但是逻辑程序devise中有许多东西缺乏,使得函数式编程变得如此有趣。 尤其是:

高级编程:函数编程在这里有着非常悠久的传统,并且已经开发了一套丰富的习语。 对于Prolog来说,第一个build议可以追溯到二十世纪八十年代初期,但它还不是很常见。 至lessISO Prolog现在已经有了应用叫做call/2, call/3 ...的同源词。

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

types系统:曾经有过像水星这样的尝试,但还没有发现太多。 而且没有与types类相媲美的function。

纯度:Haskell完全纯粹,一个函数Integer – > Integer是一个函数。 潜藏在周围没有精美的印刷品。 而且你仍然可以执行副作用。 可比较的方法正在慢慢地发展。

有许多function和逻辑编程或多或less重叠的领域。 例如回溯和懒惰和列表推断,懒惰评估和freeze/2when/2block 。 DCG和单子。 我会离开讨论这些问题给其他人…

逻辑编程和函数式编程使用不同的“隐喻”进行计算。 这经常会影响您如何考虑生成解决scheme,有时意味着不同的algorithm自然而然地会被程序员所使用,而不是逻辑程序员。

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

我将考虑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). 

这些实现相同的algorithm,甚至以基本相同的方式工作,但是它们意味着非常不同的东西。

function附件定义了将ys附加到xs结尾的列表。 我们认为从两个列表到另一个列表作为一个函数append ,运行时系统被devise为计算函数的结果,当我们调用它在两个列表。

逻辑append定义了三个列表之间的关系,如果第三个列表是第一个列表的元素,后面是第二个列表的元素,则这是正确的。 我们认为append是一个对于任何3个给定列表是真或假的谓词 ,并且运行时系统被devise为当我们用一些绑定到特定列表的参数调用该谓词并且其中一些绑定到未绑定时,查找将使这个谓词成为真的值。

使逻辑append不同的事情是你可以用它来计算从一个列表追加到另一个列表的结果列表,但是你也可以使用它来计算你需要追加到另一个列表的末尾来得到第三个列表列表(或者是否不存在这样的列表), 或者计算你需要追加另一个列表来获得第三个列表的列表, 或者给你两个可能的列表,这些列表可以被附加到一起以得到给定的第三个列表(并且探索所有可能的方式做这个)。

虽然相当于你可以做任何事情,但是它们会导致你对编程任务有不同的思考方式。 为了在函数式编程中实现某些function,您可以考虑如何从其他函数调用的结果(您可能还需要实现)中产生结果。 为了在逻辑编程中实现某些东西,你可以考虑你的参数(其中一些是input的,一些是输出的,而不一定是来自调用的那些相同的)之间的关系将意味着期望的关系。

Prolog是一种逻辑语言,可以让你免费回溯,这一点非常明显。

详细说明一下,我确切地说,我没有任何范式的专家,在我看来,逻辑编程在解决问题的时候会更好。 因为这正是语言所做的事情(例如,当需要回溯时显然是清楚的)。

我认为不同的是这样的:

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

select最适合你的想法

function编程:下午6点,亮起。 逻辑编程:当黑暗时亮起。