堆栈溢出:无限递归X \ == Y?

时间:2013-05-17 18:17:18

标签: prolog

这是我的计划:

depends(apple, tree).
depends(cider, apple).

depends(X,Y) :- X\==Y, depends(Z,Y),depends(X,Y).

如果我问下列问题:

depends(cider, tree).

我明白了:

GNU Prolog 1.3.0
By Daniel Diaz
Copyright (C) 1999-2007 Daniel Diaz
| ?- [apples].
apples.pro for byte code...
apples.pro compiled, 4 lines read - 765 bytes written, 28 ms

yes
| ?- depends(cider, tree).

Fatal Error: local stack overflow (size: 8192 Kb, environment variable used: LOCALSZ)

如果我运行'追踪',我可以看到X \ == Y的评价一遍又一遍地重复......

我在这里做错了什么? (除了购买'Seven Languages in Seven Weeks'的副本:-))

编辑:

所以这要归功于Daniel Lyons的笔记:将事实的名称与规则的名称区分开来阻止了递归:

depends(apple, tree).
depends(cider, apple).

depends_on(X,Y) :- depends(X,Z),depends(Z,Y). 

给出以下内容(打开'trace'):

{trace}
| ?- depends_on(cider, tree).
      1    1  Call: depends_on(cider,tree) ? 
      2    2  Call: depends(cider,_79) ? 
      2    2  Exit: depends(cider,apple) ? 
      3    2  Call: depends(apple,tree) ? 
      3    2  Exit: depends(apple,tree) ? 
      1    1  Exit: depends_on(cider,tree) ? 

yes

1 个答案:

答案 0 :(得分:4)

首先,你有一个单例变量:

depends(X,Y) :- X\==Y, depends(Z,Y),depends(X,Y).

相当于:

depends(X,Y) :- X\==Y, depends(_,Y), depends(X,Y).

Prolog"警告"关于单变量变量,你应该总是把它当作一个严重的错误,因为它几乎总是意味着你的代码没有按照你的想法去做。在这种情况下,我怀疑你认为Prolog会自动使X和Z不同,但它没有,而且这里没有别的指示Prolog用Z做某事,所以这只是浪费时间或真实问题的副本。

真正的问题是这段代码简化了这个:

depends(X,Y) :- X\==Y, depends(X,Y).

这是无限的递归。我可以给depends(chickens, eggs_and_flapjacks)而Prolog只是说,"嗯,chickenseggs_and_flapjacks不一样,我接下来要做的事情是什么?"然后跳回到开头只再问这个问题。这会导致堆栈溢出异常。如果您追踪,您将清楚地看到错误:

?- trace, depends(eggs, chickens).
Call: (7) depends(eggs, chickens) ? 
Call: (8) eggs\==chickens ? 
Exit: (8) eggs\==chickens ? 
Call: (8) depends(eggs, chickens) ? 
Call: (9) eggs\==chickens ? 
Exit: (9) eggs\==chickens ? 
Call: (9) depends(eggs, chickens) ? 
Call: (10) eggs\==chickens ? 
Exit: (10) eggs\==chickens ? 
Call: (10) depends(eggs, chickens) ? 
Call: (11) eggs\==chickens ? 
Exit: (11) eggs\==chickens ? 
Call: (11) depends(eggs, chickens) ? 
Call: (12) eggs\==chickens ? 
Exit: (12) eggs\==chickens ? 
Call: (12) depends(eggs, chickens) ? 

等。等

这就是你的整个问题。除了循环之外,你的代码还没有做任何事情。

修改图表遍历

您需要将此事实与谓词分开才能解决此特定问题。事情并非总是如此。你会想要这个:

depends_on(X, Y) :- depends(X, Y).
depends_on(X, Z) :- depends(X, Y), depends_on(Y, Z).

这为你提供了有限的递归。如果您尝试使用一个谓词,那么您将获得无限递归:

depends(X,Z) :- depends(X,Y), depends(Y,Z).

有几种方法可以解决这个问题,最简单的方法是将事实与谓词分开,就像我们上面所做的那样。一些Prolog实现支持tabling,我相信这也解决了这个问题(我高吗?);否则你可以跟踪你已经遍历过的关系并避免这种循环(这是处理无向图时的常用方法)。