prolog如何使用succ运行递归查询?
有人能给我解释一下为什么这个prolog查询是这样工作的吗。定义是: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
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