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

时间:2012-04-25 02:23:22

标签: 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(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

3 个答案:

答案 0 :(得分:4)

您看,callexitverbs,解释程序尝试解决您提出的查询的操作。然后,跟踪会公开实际完成的工作的详细信息,并允许您从历史角度查看它。

当Prolog必须选择规则(call)时,它会使用您提供的name(所谓的functor),并尝试unify每个参数在规则的头部。然后我们通常会说Prolog也会考虑arity,即参数的数量,以供选择。

Unification尝试“等于”两个词,而值得注意的结果是所谓的bindings个变量。您已经知道变量是那些以Uppercase开头的名称。这些名称标识规则中未指定的值,即参数placeholders。为了避免混淆,当Prolog显示跟踪时,变量会被重命名,以便我们可以识别它们,因为相关的详细信息是identities或在证明期间建立的绑定。

然后,您会在跟踪中看到此类_G648符号。他们留下被调用目标的尚未实例参数,即RZ。 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( _ )未添加,但在之前已建立。这也是尾递归模数优化的本质。