摘自Bratko的书,Prolog Programming for Artificial Intelligence (4th Edition) 我们有以下无效的代码-
anc4(X,Z):-
anc4(X,Y),
parent(Y,Z).
anc4(X,Z):-
parent(X,Z).
在书的第55页,图2.15中,显示parent(Y,Z)
一直在调用,直到堆栈内存不足为止。
我不明白的是,序言对anc4(X,Y)进行了递归调用,而不是对父对象(Y,Z)进行了递归调用。为什么序言不遍历第一行 anc4(X,Y)
,而是遍历第二行?
您能否详细说明为什么parent(Y,Z)
行一直被调用?
谢谢。
答案 0 :(得分:5)
“问题”(即目标顺序)的起源深深扎根于该语言的基础。
Prolog基于按时间顺序回溯的简单高效策略来实现SLD resolution,而像anc4/2
这样的左递归子句会导致无限递归。
实际上,逗号运算符(,)/2
代表
仅在左表达式成立时评估右表达式
因此,子句中的目标顺序实际上是程序的必要部分。
对于您的具体情况,
... , parent(Y,Z).
如果不能调用
anc4(X,Y),
不成立。
目标顺序的对应对象是<子句> 。
也就是说,在子句交换之后,整个程序的语义有所不同:
anc4(X,Z):-
parent(X,Z).
anc4(X,Z):-
anc4(X,Y),
parent(Y,Z).
为了更好地理解问题,我认为也值得尝试这个定义。
答案 1 :(得分:1)
默认情况下,如果没有 tabling 机制,Prolog无法处理左递归。只有某些Prolog系统支持制表,通常您需要显式声明要列出的谓词。
如果您使用的是XSB,YAP或SWI-Prolog,请尝试在包含anc4/2
谓词定义的源文件顶部添加以下指令:
:- table(anc4/2).
,然后重试您的查询。列表机制可以检测查询何时调用自身的 variant (*),并中止该分支的执行,直到找到查询答案为止,然后尝试其他分支(您的情况由第二子句提供) 。如果发生这种情况,将使用该答案恢复执行。
(*)此处的变量表示变量重命名时两个术语相等。