初学者Prolog堆栈溢出

初学者Prolog堆栈溢出,prolog,transitive-closure,failure-slice,Prolog,Transitive Closure,Failure Slice,我正在处理我的第一个Prolog任务,对于递归问题,我似乎无法停止堆栈溢出。就像上瘾一样;我不知道如何停止 让我给你举个例子。我想创建一个函数来确定一个对象Y是否是另一个对象X的一部分。下面是我正在使用的数据库: % Parts Database has(bicycle,wheel,2). has(bicycle,handlebar,1). has(bicycle,brake,2). has(wheel,hub,1). has(wheel,spoke,32). has(bicycle,frame

我正在处理我的第一个Prolog任务,对于递归问题,我似乎无法停止堆栈溢出。就像上瘾一样;我不知道如何停止

让我给你举个例子。我想创建一个函数来确定一个对象Y是否是另一个对象X的一部分。下面是我正在使用的数据库:

% Parts Database
has(bicycle,wheel,2).
has(bicycle,handlebar,1).
has(bicycle,brake,2).
has(wheel,hub,1).
has(wheel,spoke,32).
has(bicycle,frame,1).

has(car,steering_wheel,1).
has(car,stereo,1).
has(car,tires,4).
从这里,我想编写一个函数
partof(X,Y)
,如果Y是X的一部分,它将返回true。这就是我目前拥有的:

% Determines if Y is a part of X
partof(X,Y) :-
    has(X,Y,_).
partof(X,Y) :-
    partof(X,Z),
    partof(Z,Y).
这适用于所有
true
查询,但会溢出堆栈而不是返回
false。
例如:

?- partof(wheel, spoke).
true ;
ERROR: Out of local stack

?- partof(bicycle, spoke).
true ;
ERROR: Out of local stack

?- partof(wheel, X).
X = hub ;
X = spoke ;
ERROR: Out of local stack

我知道你们都在想,“这个愚蠢的白痴,不知道什么是基本情况,不知道如何摆脱递归。”好吧,我不怪你们。我觉得自己很笨。不过,一些耐心的指导会很有帮助。提前谢谢

Prolog中的递归与其他编程语言一样有效,但在Prolog中要复杂得多,因为在同一次运行中有两个相互独立的控制流交织在一起。在传统的命令式语言中,递归要么工作(并产生结果),要么不工作,因此循环可能会溢出某些堆栈

但在Prolog中,我们可能会首先得到许多有趣的答案,并且只会在一段时间后陷入无限循环。您的查询很幸运,在第一个解决方案/答案之后立即找到了循环。假设您接受了以下查询:

?- partof(X, Y).
   X = bicycle,
   Y = wheel
;  X = bicycle,
   Y = handlebar
;  X = bicycle,
   Y = brake
;  X = wheel,
   Y = hub
;  X = wheel,
   Y = spoke
;  X = bicycle,
   Y = frame
;  X = car,
   Y = steering_wheel
;  X = car,
   Y = stereo
;  X = car,
   Y = tires
;  X = bicycle,
   Y = hub
;  X = bicycle,
   Y = spoke
;
ERROR: Out of local stack
请注意,我们对这些解决方案并不感兴趣。我们只是在听最后一条消息。我们应该点击空格多少次,直到我们说一个查询终止?幸运的是,有一个更便宜的出路。我们只需添加
false
(一个永远不会为真的条件)作为附加目标。显然,查询决不能有解决方案。唯一有趣的是查询是否终止:

?- partof(X, Y), false. ERROR: Out of local stack 请注意,
has/3
已完全消除。换句话说,无论怎样修改
都有/3
不会停止这个循环!从未!您需要首先修改可见部分中的某些内容。剩下的不多了

有经验的Prolog程序员会立即看到此类问题。在a的帮助下,您可以学会自己识别这些部件。相信我,对于非常复杂的程序,总是可以选择检查你的直觉是否正确是非常有用的


下面是使用And
library(lambda)
定义传递闭包的另一种方法:



实际上,这只适用于纯单调程序

partof(X,Y):-has(X,Z,uu),partof(Z,Y)。
为了详细说明@capelical的评论,
partof(X,Y):-partof(X,Y)
将导致无限递归,因为每当Prolog要计算出
partof/2
时,它必须首先计算出
partof/2
。这是一个经典的无限循环。为什么它会停止?克里斯的改进迫使Prolog在循环之前做一些事情,因此它有机会失败并停止。@DanielLyons:我是Carlo:)谢谢,@DanielLyons!谢谢,@cape@对不起! partof(X,Y) :- false, has(X,Y,_). partof(X,Y) :- partof(X,Z), false, partof(Z,Y).
partof(X, Y) :-
   closure(\A^B^has(A, B,_), X, Y).