初学者Prolog Stack Overflow

时间:2017-11-15 18:19:07

标签: 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,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

我知道你们都在想着自己,#34;这个愚蠢的白痴,并不知道基本情况是什么。我不知道如何退出递归。"好吧,我不怪你。我感到愚蠢。但是,一些患者指导会非常有帮助。提前谢谢!

1 个答案:

答案 0 :(得分:3)

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

这个技巧可以扩展到整个程序 1 。只需在您喜欢的位置添加 false 即可。剩下的程序称为失败切片与原始程序的共享仍然很多:它需要更少(或相等)的许多推断才能执行。因此,如果失败切片循环,则原始程序也循环。以下是程序的最小循环失败切片:

partof(X,Y) :- false,
    has(X,Y,_).
partof(X,Y) :-
    partof(X,Z), false,
    partof(Z,Y).

请注意,has/3已完全取消。换句话说,修改has/3无论如何都不会停止这个循环!决不!您需要先修改可见部分中的某些内容。剩下的不多了!

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

以下是使用closure/3library(lambda)定义传递闭包的另一种方法:

partof(X, Y) :-
   closure(\A^B^has(A, B,_), X, Y).

1实际上,这仅适用于纯粹的单调程序