【问题标题】:Stackoverflow in Prolog Peano Arithmetic [duplicate]Prolog Peano算术中的Stackoverflow [重复]
【发布时间】:2013-09-27 11:16:21
【问题描述】:

我正在编写一些 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

【问题讨论】:

  • 这是一个非常奇怪的行为,而且很难追查...我想知道这个数量是否可能与错误有关。

标签: prolog stack-overflow failure-slice successor-arithmetics


【解决方案1】:

发生堆栈溢出的原因是,对于您的查询,谓词 add/3 最终以变量作为中间参数调用。当你回溯到它时,你会得到一个导致堆栈溢出的循环。考虑调用add(X,Y,Z)。第一个子句为您提供了第一个解决方案add(X,z,X)。但是,在回溯时,当您使用第二个子句时,您将查询与add(X,s(Y),s(Z)) 统一并递归调用add(X,Y,Z),回到您开始的位置(请记住,中间参数没有实例化,所以Ys(Y)也不会在调用中实例化。如上所示,您可以获得前四个解决方案,这要归功于两个谓词的基本情况。当这些基本子句(回溯)的使用用尽时,您会得到进入我刚才解释的循环。

尝试添加以下子句作为add/3 谓词的第一个子句:

add(X,Y,Z) :-
    write('Called: '), writeq(add(X,Y,Z)), nl, fail.

重试您将得到的查询(希望您能快速使用Control-C):

| ?- mult(X,Y,z).

Y = z ? ;
Called: add(_279,z,z)

X = z
Y = s(z) ? ;
Called: add(_279,z,_307)
Called: add(_279,_279,z)

X = z
Y = s(s(z)) ? ;
Called: add(_279,z,_309)
Called: add(_279,_279,_309)
Called: add(z,z,z)

X = z
Y = s(s(s(z))) ? ;
Called: add(s(_307),_307,_309)
Called: add(s(z),s(s(z)),z)
Called: add(s(s(_311)),_311,_313)
Called: add(s(s(z)),s(s(s(s(z)))),z)
Called: add(s(s(s(_315))),_315,_317)
Called: add(s(s(s(z))),s(s(s(s(s(s(z)))))),z)
Called: add(s(s(s(s(_319)))),_319,_321)
Called: add(s(s(s(s(z)))),s(s(s(s(s(s(s(s(z)))))))),z)
Called: add(s(s(s(s(s(_323))))),_323,_325)
...

希望这会有所帮助。

【讨论】:

  • 附加说明:如果您仔细查看上面无尽打印输出的开头,您会注意到您正在尝试添加 add/3 的前两个参数的递增值,检查它们是否添加高达z。然而,基本条款从不适用......
  • 这是一个令人印象深刻的分析! Prologs 的内部工作非常简单,但它们展示自己的方式可能令人难以置信。 :) 你认为解决这种情况的最好方法是什么?我可以添加一个谓词,说 X 和 Y 应该小于或等于 Z,但我应该把它放在 add 还是 mult 中?
【解决方案2】:

我知道这是一篇很老的帖子,但我刚刚开始学习 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 上的无限循环。

【讨论】:

  • 首先,您的新定义过于笼统。 mult(s(s(z)),s(z),s(s(s(z)))). 成功。即 2x1=3。
  • 感谢您的反馈,尤其是当它来自更有知识的人时。在回复之前,我应该更多地研究 Peano 公理。谢谢!
猜你喜欢
  • 2019-01-03
  • 1970-01-01
  • 1970-01-01
  • 2019-07-09
  • 1970-01-01
  • 2021-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多