我正在写一些Peano算法来更好地学习Prolog。以下是我提出的代码,它似乎与我在网上其他地方看到的相同:
add(X,z,X).
add(X,s(Y),s(Z)) :- add(X,Y,Z).
mult(_,z,z).
mult(X,s(Y),W) :- mult(X,Y,Z), add(X,Z,W).
但是,如果我尝试进行简单的查询,例如0
的除数对,我会遇到问题:
| ?- mult(X,Y,z).
Y = z ? ;
X = z
Y = s(z) ? ;
X = z
Y = s(s(z)) ? ;
X = z
Y = s(s(s(z))) ? ;
Fatal Error: global stack overflow (size: 32768 Kb, reached: 32765 Kb, environment variable used: GLOBALSZ)
它真的让我感到困惑,至于它如何能够一直到Y = 3
,而不是Y = 4
?
答案 0 :(得分:3)
发生堆栈溢出是因为对于您的查询,最终使用变量作为中间参数调用谓词add/3
。当你回溯到它时,你会得到一个导致堆栈溢出的循环。考虑通话add(X,Y,Z)
。第一个子句为您提供了第一个解决方案add(X,z,X)
。但是,在回溯时,当你使用第二个子句时,你用add(X,s(Y),s(Z))
统一你的查询并递归调用add(X,Y,Z)
,回到你开始的地方(记住,中间参数没有实例化,所以{{1在Y
中也不会在调用中实例化。如上所示,你能够得到前四个解决方案,这要归功于两个谓词的基本情况。当使用那些基础子句时(在回溯中已经筋疲力尽,你进入我刚才解释的循环。
尝试添加以下子句作为s(Y)
谓词的第一个子句:
add/3
重试您将获得的查询(希望您能快速使用add(X,Y,Z) :-
write('Called: '), writeq(add(X,Y,Z)), nl, fail.
):
Control-C
希望这有帮助。
答案 1 :(得分:1)
我知道这是一篇非常古老的帖子,但我刚开始学习Prolog并发现这个问题引人入胜。所以这是我的两分钱。
我注意到如果你改变你的规则
mult(X,s(Y),W) :- mult(X,Y,Z), add(X,Z,W).
分离
mult(X,s(Y),W) :- mult(X,Y,Z); add(X,Z,W).
并运行相同的查询
?- mult(X,Y,z).
你会得到一些结果,但是你可以看到SWI Prolog解释器在一点之后没有显示太多细节:
?- mult(X,Y,z).
Y = z ;
Y = s(z) ;
Y = s(s(z)) ;
Y = s(s(s(z))) ;
Y = s(s(s(s(z)))) ;
Y = s(s(s(s(s(z))))) ;
Y = s(s(s(s(s(s(z)))))) ;
Y = s(s(s(s(s(s(s(z))))))) ;
Y = s(s(s(s(s(s(s(s(z)))))))) ;
Y = s(s(s(s(s(s(s(s(s(z))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ;
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) .
在Gnu Prolog中做同样的事情看起来好多了,轻轻地说:
| ?- mult(X,Y,z).
Y = z ? ;
Y = s(z) ? ;
Y = s(s(z)) ? ;
Y = s(s(s(z))) ? ;
Y = s(s(s(s(z)))) ? ;
Y = s(s(s(s(s(z))))) ? ;
Y = s(s(s(s(s(s(z)))))) ? ;
Y = s(s(s(s(s(s(s(z))))))) ? ;
Y = s(s(s(s(s(s(s(s(z)))))))) ? ;
Y = s(s(s(s(s(s(s(s(s(z))))))))) ? ;
Y = s(s(s(s(s(s(s(s(s(s(z)))))))))) ? ;
Y = s(s(s(s(s(s(s(s(s(s(s(z))))))))))) ? ;
Y = s(s(s(s(s(s(s(s(s(s(s(s(z)))))))))))) ? ;
Y = s(s(s(s(s(s(s(s(s(s(s(s(s(z))))))))))))) ? ;
Y = s(s(s(s(s(s(s(s(s(s(s(s(s(s(z)))))))))))))) ? ;
Y = s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(z))))))))))))))) ? ;
Y = s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(z)))))))))))))))) ? ;
...
在SWI Prolog(和GNU Prolog)中,您可以调用调试器,这可以更好地说明Paulo Moura关于原始问题的优秀理论分析:
?- trace, mult(X,Y,z).
正如您将看到的,它似乎永远在运行,因此可以解释堆栈溢出。追踪追踪here的结果,尽在其中。您也可以通过运行上面的跟踪来跟踪browser上的无限循环。