我有一些关于带有黑色和白色顶点的图形的程序:
black(root).
black(v1).
black(v3).
black(v4).
edge(root,root).
edge(v1,root).
edge(v2,v1).
edge(v3,v1).
edge(v4,v3).
edge(v5,v2).
edge(v5,v4).
edge(v6,v5).
vertex(X) :- edge(X,_).
vertex(X) :- edge(_,X).
white(X) :-
vertex(X),
not(black(X)).
foo(root).
foo(X) :-
edge(X,Y),
black(Y),
foo(Y).
当我运行目标foo(X)
时,我遇到了问题:
如果我删除事实edge(root,root)
,程序会找到一些解决方案(6种不同的解决方案)。否则,我得到无限的解决方案,所有都是X=root
。
为什么会这样?
答案 0 :(得分:4)
首先快速回答一下,向您展示Prolog程序员如何看待您的程序,其解释如下。您的程序不会终止,因为以下failure-slice不会终止:
black(root).black(v1) :- false.black(v3) :- false.black(v4) :- false. edge(root,root).edge(v1,root) :- false.edge(v2,v1) :- false.edge(v3,v1) :- false.edge(v4,v3) :- false.edge(v5,v2) :- false.edge(v5,v4) :- false.edge(v6,v5) :- false.foo(root) :- false. foo(X) :- X = root, Y = root, edge(X,Y), black(Y), foo(Y), false.
所有直通文本与理解非终止无关。正如您所看到的,其余部分相对较短,因此很快就能掌握。
Prolog无法直接检测到此循环。但是,您可以将closure0/3
定义here用于此目的:
edge_toblack(X, Y) :-
edge(X, Y),
black(Y).
foo(X) :-
closure0(edge_toblack, X,root).
现在了解详情。
在回答您的问题之前,为什么会发生这种情况,让我们退后一步。我们首先需要找到一个观察问题的好方法。目标foo(X)
确实产生了答案,实际上只有X = root
。那么也许你只是不耐烦等待Prolog完成?通过询问查询foo(X), false
,我们摆脱了这些令人恼火的眼睛紧张的答案,我们将等待false
作为回答。
我们仍无法确定程序的非终止属性。但我们可以通过插入目标false
(以及(=)/2
)来缩小实际原因。在上面的故障片中,我插入了最大值。如果您刚刚开始,只需添加一个false,然后重新加载该程序并重试该查询。凭借一些经验,您很快就能够快速识别出这些零件。所以现在我们只需要了解
foo(root) :- edge(root,root), % always true black(root), % always true foo(root), false.
甚至更短
foo(root) :- foo(root).
Prolog非常简单但有效的执行机制不能检测到这样的循环。基本上有几种方法:
手动添加循环检测。这通常很容易出错
使用closure0/3
- 见上文
编写自己的元解释器来检测一般的循环
使用B-Prolog或XSB-Prolog提供的类似表格的语言扩展。