谁能给我解释一下为什么这个序言查询的工作方式是这样。 该定义是:
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斩获SUCC ...如何? 此外,哪里零来自在第一个出口(9)? 我是新来的Prolog,和我想了解一门功课的语言。 任何帮助非常赞赏。
注:对于任何有兴趣,链接到本教程是http://www.learnprolognow.org/lpnpage.php?pagetype=html&pageid=lpn-htmlse9
“哪里零来自在第一个出口(9)?”
该呼叫add(0, succ(succ(0)), _G652)
是统一与说,如果的第一个参数的第一条add
为零,则第二和第三是相同的。 在这个特定的situatiob可变_G652
变得succ(succ(0))
“虽然递归不过,R斩获SUCC ...如何?”
这是第二个条款的申请结果。 这一条款状态(大约)你先带succ
从第一个参数,然后调用add
递归,最后,添加其他的“层” succ
从这个递归调用回来的第三个参数。
谓词add
不过是一个直接的实现除了在皮亚诺算术的: http://en.wikipedia.org/wiki/Peano_axioms#Addition
你看, call
和exit
是verbs
,行动的解释采取试图解决您提出的查询。 然后,跟踪暴露了做实际工作的细节,并且让你在历史的角度查看。
当Prolog的必然选择一个规则( call
),它使用的name
,你给它(所谓的functor
),并试图以unify
的规则的头上,每种说法。 然后,我们通常说的序言还考虑了arity
,即数量的参数,以供选择。
Unification
试图将“使等于”两个词,而值得一提的结果所谓的bindings
变量。 你已经知道的变量是那些刚开始的名字Uppercase
。 这样的名称识别规则未定值,即是placeholders
的参数。 为了避免混淆,当Prolog的显示的痕迹,变量被重命名,使我们能够识别他们,因为相关细节它的identities
证明期间建立或绑定。
然后你看到这样_G648
在跟踪符号。 他们停留的所谓目标,即尚未被实例化的参数R
和Z
。 R是唯一的(只发生在顶层调用),所以这段序言亲切保持用户友好的名称,但ž来自程序,可能会发生多次,然后得到了重新命名。
要回答这个查询
?- add(succ(succ(succ(0))), succ(succ(0)), R).
序言首先尝试匹配
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
将有更多的注释(希望)更清晰的轨迹:
(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( _ )
不递归调用后增加,但在其成立。 这也是尾递归模利弊优化的精髓。