Prolog遵循自然数的定义有哪些步骤?

时间:2014-04-22 08:24:51

标签: prolog successor-arithmetics

我正在学习如何在Prolog中编程,我找到了下一个定义自然数及其总和的程序:

sum( succ( X ), Y, succ( Z )) :- sum( X, Y, Z ).
sum( 0, X, X ).
?- sum( succ( succ(0)), succ( succ( succ(0))), Answer ).
Answer = succ( succ( succ( succ( succ(0)))))

(找到here

问题是我正在努力解决这个程序的执行流程。说实话,我不知道它做了什么。 Prolog如何找出答案的价值? Prolog遵循哪些步骤来找到答案的价值?

提前致谢。

3 个答案:

答案 0 :(得分:2)

有助于理解Prolog在确定现有谓词或设计新谓词时的操作方式。当您进行如下查询时:

sum( succ(succ(0)), succ(succ(succ(0))), Answer ).

Prolog将查找与sum(_, _, _)sum/3)匹配的事实和规则,并选择匹配的第一个事实和规则。现行规则是:

(1) sum( succ(X), Y, succ(Z) ) :- sum( X, Y, Z ).
(2) sum( 0, X, X ).

如果查看查询,它显然符合规则#1的模式。请记住,在Prolog中,变量可以实例化为任何类型的术语,并且同名的变量是统一的(实例化为相同的值)。当Prolog使用查询统一规则#1的“头部”时,它通过统一变量来实现,如下所示:

    X = succ(0)
    Y = succ(succ(succ(0)))
(A) Answer = succ(Z)

请注意,即使Answer尚未绑定(已分配具体值),succ(Z)的值仍为Z。现在我们遵循规则,该规则将查询sum(X, Y, Z),这将是查询:

sum( succ(0), succ(succ(succ(0))), Z )
       |        |                  |
       X        Y                  Z

Prolog现在将再次从顶部开始,因为这是sum/3的新查询。就像第一次一样,它将规则#1与以下统一匹配:

    X = 0
    Y = succ(succ(succ(0)))
(B) Z = succ(Z')

我使用上面的Z'将其与Z中的其他变量sum(succ(0), succ(succ(succ(0))), Z)区分开来,因为它与头部中使用的Z不同sum(..., succ(Z)) int f(x) { return 2*x; }。这就像你在C语言中声明为x的函数并且从某个地方用另一个局部变量x调用它一样,该名称sum(X, Y, Z')用于两个不同的地方并代表两个不同的变量)。

然后我们可以再次跟随下一个递归查询sum( 0, succ(succ(succ(0))), Z' ) | | | X Y Z' ,它变为:

0

此递归查询与规则#1不匹配,因为第一个参数succ(X) 0 = 0 X = succ(succ(succ(0))) (C) X = Z' 不匹配。但是,它符合规则#2:

X = succ(succ(succ(0)))

现在Z' = succ(succ(succ(0)))所以Z = succ(Z') = succ(succ(succ(succ(0)))) 。由于此规则中不再有任何查询,因此它最终会成功返回到查询的位置。将其返回上面的(B):

Answer = succ(Z) = succ(succ(succ(succ(succ(0)))))

并将其返回(A):

trace

你有它。使用@CapelliC提到的{{1}}工具,您可以在Prolog解释器中观察这些连续的步骤,并遵循变量的实例化。

答案 1 :(得分:2)

Prolog的“评估”通过将给定查询与程序的规则“ head 匹配,并在匹配替换下继续匹配规则的 body 来进行。选择规则时,为了唯一性,统一重命名其变量:

(1) sum( succ( X ), Y, succ( Z )) :- sum( X, Y, Z ).
(2) sum( 0, X, X ).

    ?- sum( succ( succ(0)), succ( succ( succ(0))), Answer    ).
(1) -> sum( succ(   X1   ),  Y1                  , succ( Z1 )) :- sum( X1, Y1, Z1 ).

        %%  X1 = succ(0), Y1 = succ( succ( succ(0))), succ(Z1) = Answer. %%

    -? sum( X1,              Y1,                   Z1        ).
    -? sum( succ( 0  ),      Y1,                   Z1        ). 
(1) -> sum( succ( X2 ),      Y2,                   succ( Z2 )) :- sum( X2, Y2, Z2 ).

        %%  X2 = 0, Y2 = Y1, succ(Z2) = Z1. %%

    -? sum( X2,              Y2,                   Z2        ).
    -? sum( 0,               Y2,                   Z2        ).
(2) -> sum( 0,               X3,                   X3        ).     %% DONE. %%

        %%  X3 = Y2, X3 = Z2. %%

从这里开始, Answer = succ(Z1) = succ( succ(Z2)) = succ( succ(X3)) = succ( succ( Y2)) = succ( succ (Y1)) = succ( succ( succ( succ( succ(0)))))

答案 2 :(得分:1)

对于这样一个简单的程序,跟踪/ 0它是要走的路。 leash / 1(对于新手来说并不完全明显)允许控制调试器接口:

21 ?- leash(-all),trace.
true.

[trace] 22 ?- sum( succ( succ(0)), succ( succ( succ(0))), Answer ).
   Call: (6) sum(succ(succ(0)), succ(succ(succ(0))), _G710)
   Call: (7) sum(succ(0), succ(succ(succ(0))), _G789)
   Call: (8) sum(0, succ(succ(succ(0))), _G791)
   Exit: (8) sum(0, succ(succ(succ(0))), succ(succ(succ(0))))
   Exit: (7) sum(succ(0), succ(succ(succ(0))), succ(succ(succ(succ(0)))))
   Exit: (6) sum(succ(succ(0)), succ(succ(succ(0))), succ(succ(succ(succ(succ(0))))))
Answer = succ(succ(succ(succ(succ(0))))).

你可以看到你的程序在第一个参数上进行有界递归搜索,用第一个子句(标记为6,7的调用)或第二个子句(标记为8的调用)统一它。