我正在学习如何在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遵循哪些步骤来找到答案的价值?
提前致谢。
答案 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的调用)统一它。