有人可以向我解释为什么这个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被剥离了,并且它是recurses。虽然递归,但R获得了succ ......怎么样?!此外,零点来自第一个出口(9)?我是prolog的新手,我正在努力理解作业的语言。任何帮助非常感谢。
注意:对于任何感兴趣的人,本教程的链接是http://www.learnprolognow.org/lpnpage.php?pagetype=html&pageid=lpn-htmlse9
答案 0 :(得分:4)
您看,call
和exit
是verbs
,解释程序尝试解决您提出的查询的操作。然后,跟踪会公开实际完成的工作的详细信息,并允许您从历史角度查看它。
当Prolog必须选择规则(call
)时,它会使用您提供的name
(所谓的functor
),并尝试unify
每个参数在规则的头部。然后我们通常会说Prolog也会考虑arity
,即参数的数量,以供选择。
Unification
尝试“等于”两个词,而值得注意的结果是所谓的bindings
个变量。您已经知道变量是那些以Uppercase
开头的名称。这些名称标识规则中未指定的值,即参数placeholders
。为了避免混淆,当Prolog显示跟踪时,变量会被重命名,以便我们可以识别它们,因为相关的详细信息是identities
或在证明期间建立的绑定。
然后,您会在跟踪中看到此类_G648
符号。他们留下被调用目标的尚未实例参数,即R
和Z
。 R是唯一的(仅在顶级调用中出现),所以这个Prolog保持用户友好名称,但Z来自程序,可能多次出现,然后重命名。
回答此问题
?- add(succ(succ(succ(0))), succ(succ(0)), R).
Prolog首次尝试匹配
add(0,Y,Y).
并失败,因为succ(succ(succ(0))不能等于0。 然后尝试
add(succ(X),Y,succ(Z)) :- add(X,Y,Z).
因此必须解决这些绑定(左边是调用者的术语):
succ(succ(succ(0))) = succ(X)
succ(succ(0)) = Y
R = Z
你可以看到为什么X变成succ(succ(0))
,我们有一个新的目标要证明,即刚刚建立了绑定的规则体add(X,Y,Z)
:
加(SUCC(SUCC(0)),SUCC(SUCC(0)),_ G648)
等等......直到X变为0
并且目标匹配
add(0,Y,Y).
然后Y变为succ(succ(0)),值得注意的是还在调用规则中给Z
赋值。
HTH
答案 1 :(得分:1)
“零点来自第一个出口(9)?”
调用add(0, succ(succ(0)), _G652)
与第一个子句统一,该子句表示如果add
的第一个参数为零,则第二个和第三个参数相同。在此特定情境中,变量_G652
变为succ(succ(0))
。
“虽然递归,但R获得了一个succ ......怎么样?!”
这是第二个条款的应用结果。该子句(粗略地)表明您首先从第一个参数中剥离succ
,然后递归调用add
,最后,在第三个参数中添加另一个“{1}}层”从这个递归调用。
谓词succ
只不过是在Peano算术中直接实现添加:http://en.wikipedia.org/wiki/Peano_axioms#Addition
答案 2 :(得分:0)
带有更多注释的(希望)更易读的痕迹是:
(6) Call: add(succ(succ(succ(0))), succ(succ(0)), R) % R = succ(_G648)
(7) Call: add(succ(succ(0)), succ(succ(0)), _G648) % _G648 = succ(_G650)
(8) Call: add(succ(0), succ(succ(0)), _G650) % _G650 = succ(_G652)
(9) Call: add(0, succ(succ(0)), _G652) % _G652 = succ(succ(0))
(9) Exit: add(0, succ(succ(0)), succ(succ(0)))
(8) Exit: add(succ(0), succ(succ(0)), succ(succ(succ(0))))
(7) Exit: add(succ(succ(0)), succ(succ(0)), succ(succ(succ(succ(0)))))
(6) Exit: add(succ(succ(succ(0))), succ(succ(0)), succ(succ(succ(succ(succ(0))))))
正如您所看到的,这四个出口是多余的,最终答案已经在(9) Exit
处已知;那时只有一个出口就足够了:
% R = succ(_G648)
% _G648 = succ(_G650)
% _G650 = succ(_G652)
% _G652 = succ(succ(0))
% thus,
% R = succ( succ( succ( succ(succ(0)) )))
这确实是在尾部调用优化下发生的事情,因为谓词定义 确实是尾递归的,而R
下的结果是以自上而下的方式构建的,其中“ “通过实例化逻辑变量逐渐填补”。因此,在递归调用之后,succ( _ )
未添加,但在之前已建立。这也是尾递归模数优化的本质。