没有终止谓词

时间:2015-01-27 00:06:16

标签: prolog failure-slice transitive-closure

我有一些关于带有黑色和白色顶点的图形的程序:

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。 为什么会这样?

1 个答案:

答案 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非常简单但有效的执行机制不能检测到这样的循环。基本上有几种方法:

  1. 手动添加循环检测。这通常很容易出错

  2. 使用closure0/3 - 见上文

  3. 编写自己的元解释器来检测一般的循环

  4. 使用B-Prolog或XSB-Prolog提供的类似表格的语言扩展。