如果可能的话,我希望有人解释这个程序(来自“现在学习prolog”一书)。它需要两个数字并将它们加在一起。
add(0,Y,Y).
add(s(X),Y,s(Z)) :- add(X,Y,Z).
原则上我理解,但我有一些问题。让我说我发出查询
?- add(s(s(0)), s(0), R).
结果是:
R = s(s(s(0))).
步骤1是与规则2的匹配。现在X变为s(0)而Y仍然是s(0)。但是,Z(根据书中)变为s(_G648)或s(),其中包含一个未实例化的变量。为什么是这样?
在最后一步,第一条规则匹配,结束递归。在这里,Y的内容不知何故最终出现在Z的未实例化部分!非常困惑,我需要一个简单的英语解释。
答案 0 :(得分:2)
第一个前提:
s(X)
定义为X
的后继者,所以基本上是s(X)= X + 1 让我们首先看一下另外一个带有后继的加法定义,我发现它更直观:
add(0,Y,Y).
add(s(A),B,C) :- add(A,s(B),C).
这基本相同,但递归更容易看到:
我们问
add(s(s(0)),s(0),R).
现在,第一步prolog说相当于
add(s(0),s(s(0)),R)
因为我们有add(s(A),B,C) :- add(A,s(B),C)
,如果我们看问题A = s(0)和B = s(0)。但是这仍然没有终止,所以我们必须重新应用A = 0和B = s(s(0))的等价,所以它变成
add(0,s(s(s(0))),R)
,给定add(0,Y,Y).
这意味着
R = s(s(s(0)))
你对add的定义基本上是相同的,但有两个递归:
首先它将第一个参数运行到0,所以它归结为添加(0,Y,Y):
add(s(s(0)),s(0),R)
X = s(0),Y = s(0)和s(Z)= R和Z = _G001
add(s(0),s(0),_G001)
X = 0,Y = s(0)和s(s(Z))= s(G_001)= R和Z = _G002
add(0,s(0),_G002)
所以现在它知道_G002是定义add(0,Y,Y)的s(0)但是必须追溯它的步骤,所以_G001是s(_G002)而R是s(_G001)是s(s) (_G002))是s(s(s(0)))。
所以重点是为了得到定义add(0,Y,Y)prolog必须为第一次递归引入内部变量,然后从第二次递归中计算R。
答案 1 :(得分:1)
如果您想了解Prolog程序的含义,您可能首先关注关系所描述的。然后你可能想了解它的终止属性。
如果您按照问题的建议深入了解具体执行的细节,您很快就会迷失在众多细节中。毕竟,Prolog有两个不同的隔行扫描控制流(AND-和OR-控制),除此之外,它还具有包含参数传递,赋值,比较和方程求解的统一。
简介:虽然计算机可以毫不费力地执行具体的查询以进行数以万计的推论,但在屏幕显示之后,您会感到疲倦。你无法击败计算机。幸运的是,有更好的方法来理解一个程序。
有关含义,请先查看规则。它写着:
add(s(X),Y,s(Z)) :- add(X,Y,Z).
请参阅中间的:-
?它意味着象征箭头。箭头从右到左指向有点不寻常。在非正式的写作中,你会把它写成从左到右。请阅读如下:
如果
add(X,Y,Z)
为真,那么add(s(X),Y,s(Z))
也是如此。
所以我们假设我们已经有一些add(X,Y,Z)
意思是“X + Y = Z”。鉴于此,我们可以得出结论,“(X + 1)+ Y =(Z + 1)”也成立。
之后您可能有兴趣了解它的终止属性。让我简单地说一下:要理解它,只需看看规则即可:第二个论点只是进一步说明。因此:第二个参数不影响终止。第一和第三个参数看起来都一样。因此:它们都以同样的方式影响终止!
实际上,如果第一个或第三个参数不与s(_)
统一,则添加/ 3终止。
在标记为failure-slice的其他答案中详细了解该内容,例如:
Prolog successor notation yields incomplete result and infinite loop
但现在回答add(s(s(0)), s(0), R).
的问题我只看第一个论点:是的!这将终止。就是这样。
答案 2 :(得分:0)
让我们将问题分为三个部分:关于实例化变量和累加器模式的问题,我在该示例的变体中使用了这些问题:
add(0,Y,Y).
add(s(X),Y,Z):-add(X,s(Y),Z).
以及关于使用替换组合的示例的评论。
Prolog应用什么来查看哪个规则(即Horn子句)匹配(其头部统一)是Unification Algorithm,它特别告诉我,如果我有一个变量,让我们说,X和一个funtor ,即f(Y)这两个术语统一(关于occurs check有一小部分要...检查但是没关系atm)因此有一个替换可以让你转换一个到另一个。
当你的第二个规则被调用时,R确实统一为s(Z)。不要害怕Prolog给新的,未实例化的变量赋予的内部表示,它只是一个变量名称(因为它以'_'开头)代表一个代码(Prolog必须有一种方法来表达不断新生成的变量和所以_G648,_G649,_G650等)。 当您调用Prolog过程时,您传递的未经实例化的参数(在本例中为R)用于包含过程完成执行时的结果,并且它将包含结果,因为在过程中的某个时刻调用它将立即实现某些目标(始终通过统一)。 如果在某些时候你有一个var,即K被认为是s(H)(或s(_G567),如果你愿意的话),它仍然被部分地实例化并且你需要递归地实例化你的完整输出。 要查看它将被实例化的内容,请在累加器模式段落和后续段落中读取,以便处理问题。
累加器模式取自函数式编程,简而言之,是一种有变量的方法, accumulator (在我的情况下是Y本身),在一些过程调用之间进行部分计算的负担。该模式依赖于递归,并且大致具有以下形式:
最后,关于你的例子,它遵循另一种模式,替换的组合,我认为你可以更好地理解累积器和通过统一实例化。
这是堆栈跟踪:
add(s(s(s(0))),s(0),S1). ^ S1=s(S2)=s(s(s(s(0))))
add( s(s(0)) ,s(0),S2). | S2=s(S3)=s(s(s(0)))
add( s(0) ,s(0),S3). | S3=s(S4)=s(s(0))
add( 0 ,s(0),S4). | S4=s(0)
add( 0 ,s(0),s(0)). ______|