prolog如何使用succ运行递归查询?

prolog如何使用succ运行递归查询?,prolog,successor-arithmetics,Prolog,Successor Arithmetics,有人能给我解释一下为什么这个prolog查询是这样工作的吗。定义是: add(0,Y,Y). add(succ(X),Y,succ(Z)):- add(X,Y,Z). 鉴于此: ?- add(succ(succ(succ(0))), succ(succ(0)), R). 以下是查询的跟踪: Call: (6) add(succ(succ(succ(0))), succ(succ(0)), R) Call: (7) add(succ(succ(0)), succ(su

有人能给我解释一下为什么这个prolog查询是这样工作的吗。定义是:

add(0,Y,Y). 
add(succ(X),Y,succ(Z)):- add(X,Y,Z).
鉴于此:

?-  add(succ(succ(succ(0))),  succ(succ(0)),  R).
以下是查询的跟踪:

Call:  (6)  add(succ(succ(succ(0))),  succ(succ(0)),  R) 

Call:  (7)  add(succ(succ(0)),  succ(succ(0)),  _G648) 

Call:  (8)  add(succ(0),  succ(succ(0)),  _G650) 

Call:  (9)  add(0,  succ(succ(0)),  _G652) 

Exit:  (9)  add(0,  succ(succ(0)),  succ(succ(0))) 

Exit:  (8)  add(succ(0),  succ(succ(0)),  succ(succ(succ(0)))) 

Exit:  (7)  add(succ(succ(0)),  succ(succ(0)), 
                                              succ(succ(succ(succ(0))))) 

Exit:  (6)  add(succ(succ(succ(0))),  succ(succ(0)), 
                                                succ(succ(succ(succ(succ(0))))))
关于该教程,最让我困惑的部分是,在第一个参数中,succ被剥离,并且它再次出现。在递归过程中,R获得了成功。。。怎样?!还有,第一个出口(9)的零是从哪里来的?我对prolog是新手,我正在努力理解这门语言作为家庭作业。非常感谢您的帮助

注意:对于任何感兴趣的人,本教程的链接是“第一个出口(9)的零从哪里来?”

调用
add(0,suc(suc(0)),_G652)
与第一个子句统一,该子句表示如果
add
的第一个参数为零,则第二个和第三个参数相同。在这种特殊情况下,b变量
\u G652
变为
succ(succ(0))

“但在递归过程中,R获得了成功…如何?!”

这是适用第二条的结果。该子句(大致)说明,首先从第一个参数中剥离
succ
,然后递归调用
add
,最后,将另一层
succ
添加到从该递归调用返回的第三个参数中


谓词
add
不过是Peano算术中加法的直接实现:

你看,
调用
退出
动词
,解释器试图解决你提出的查询时所采取的动作。然后,跟踪将显示实际完成的工作的细节,并允许您从历史的角度进行查看

当Prolog必须选择一个规则(调用)时,它使用您给它的名称(所谓的函数),并尝试统一规则头部的每个参数。然后我们通常说,Prolog还考虑了
arity
,即参数的数量,以供选择

Unification
试图“使两个项相等”,值得注意的结果就是所谓的变量绑定。您已经知道,变量是以
大写字母开头的那些名称。此类名称标识规则中未指定的值,即参数的
占位符。为了避免混淆,当Prolog显示跟踪时,变量被重命名,以便我们能够识别它们,因为相关的细节是证明过程中建立的
标识
或绑定

然后您会在跟踪中看到这样的
\u G648
符号。它们停留在被调用目标的尚未实例化的参数上,即
R
Z
。R是唯一的(仅出现在顶级调用中),因此这个序言保留了用户友好的名称,但是Z来自程序,可能出现多次,然后被重命名

回答这个问题

?-  add(succ(succ(succ(0))),  succ(succ(0)),  R).
Prolog首先尝试匹配

add(0,Y,Y). 
失败,因为不能使succ(succ(0))等于0。 然后尝试

add(succ(X),Y,succ(Z)) :- add(X,Y,Z).
因此必须解决这些绑定(调用方术语的左边):

您可以看到为什么X变为
succ(succ(0))
,我们有一个新的目标要证明,即规则体
添加(X,Y,Z)
,并使用刚刚建立的绑定:

添加(成功(成功(0)),成功(成功(0)),_G648)

以此类推…直到X变成
0
并且目标匹配

添加(0,Y,Y)。

然后Y变为succ(succ(0)),值得注意的是,还为调用规则中的
Z
指定了一个值

HTH

更清晰、注释更多的轨迹是:

(6)调用:添加(成功(成功(成功)(成功(0))),成功(成功(0)),R)%R=suc(_G648)
(7) 调用:添加(成功(成功(0)),成功(成功(0)),_G648)%_G648=成功(_G650)
(8) 调用:添加(成功(0),成功(成功(0)),_G650)%_G650=成功(_G652)
(9) 调用:添加(0,成功(成功(0)),_G652)%_G652=成功(成功(0))
(9) 退出:添加(0,成功(成功(0)),成功(成功(0)))
(8) 退出:添加(成功(0),成功(成功(0)),成功(成功(0)))
(7) 退出:添加(成功(成功(0)),成功(成功(0)),成功
(6) 退出:添加(成功(成功(成功)(成功(0))),成功(成功(0)),成功(成功)(成功(成功)(成功(0)))))))
如您所见,这四个出口是多余的,在
(9)出口处已经知道了最终答案;在这一点上,只有一个出口就足够了:

%R=succ(\u G648)
%_G648=成功(_G650)
%_G650=成功(_G652)
%_G652=成功(成功(0))
%因此,,
%R=成功(成功(成功(成功)(成功(0)))

这确实是尾部调用优化下发生的事情,因为谓词定义确实是尾部递归的,
R
下的结果是自上而下构建的,逻辑变量的实例化逐渐填补了“漏洞”。因此
不是在递归调用之后添加的,而是在递归调用之前建立的。这也是尾部递归模cons优化的本质。

通常,
succ/1
写为
s/1
。请看标签上的答案。我明白你的意思,我对这一点有点理解。我不理解的是规则的负责人也在做一些事情,我只是假设在尸体被证明之前它什么也没做。
succ(succ(succ(0))) = succ(X)
succ(succ(0)) = Y
R = Z