避免在Prolog中无限循环

时间:2012-12-29 01:52:23

标签: prolog infinite-loop

我在知识库中有以下事实:

line(a,b). -- denotes the line determined by point a and b
line(c,d). -- denotes the line determined by point c and d
lineEqual(line(a,b),line(c,d)) -- denotes the length of two lines are equal

我想要另一个可以交换lineEqual / 2的两个参数的规则:

lineEqual(line(C, D), line(A, B)):-
    lineEqual(line(A,B),line(C,D)). 

不幸的是,该规则将在Prolog中创建一个无限循环。还有其他想法吗?

感谢您的更新。不确定我是否理解你的最后一条规则:

transitiveSymmetricRelPath(L1, L2, IntermediateNodes) :- symmetricRel(L1, L3),
                 \+member(L3, IntermediateNodes),
                 transitiveSymmetricRelPath(L1, L2, [L3 | IntermediateNodes]). 

我可以想象,每当它试图剥离头节点时,如果它恰好与L1和L3相连,对吧?因此,如果我们最终得到一个空列表,那么我们可以使用此规则:

transitiveSymmetricRel(L1, L2) :- transitiveSymmetricRelPath(L1, L2, []). 

但我真的没有得到的是你从哪里获得transitiveSymmetricRelPath / 3的中间节点的非空列表。我实际上用给定的事实rel(a,b)尝试了你的代码。相对(A,C)。并且它不返回transitiveSymmetricRel(b,c),也不返回transitiveSymmetricRel(c,b)。你能看一下吗?

非常感谢!

编辑: 我通过修改你的规则来实现它:

transitiveSymmetricRelPath(L2, L3, IntermediateNodes) :- symmetricRel(L1, L3),
     \+member(L3, IntermediateNodes),
     transitiveSymmetricRelPath(L1, L2, [L3 | IntermediateNodes]). 

无论如何,谢谢你的建议。

1 个答案:

答案 0 :(得分:1)

您应避免在单个名称下引入类似数据的语句(如第一个示例中的linelineEqual)和代码(如第二个示例中的lineEqual)。相反,请将您的数据库事实保存在其他名称下。然后,您可以例如定义:

areLinesEqual(L1, L2) :- linesEqual(L1, L2).
areLinesEqual(L1, L2) :- linesEqual(L2, L1).

通常,如果你有一个关系rel,并且你想构建一个对称的传递闭包,你应该一次引入一个概念。例如:

symmetricRel(L1, L2) :- rel(L1, L2).
symmetricRel(L1, L2) :- rel(L2, L1).

transitiveSymmetricRel(L1, L2) :-
    transitiveSymmetricRelPath(L1, L2, []).

transitiveSymmetricRelPath(L1, L2, _) :-
    symmetricRel(L1, L2).

transitiveSymmetricRelPath(L1, L2, IntermediateNodes) :-
    symmetricRel(L1, L3),
    \+ member(L3, IntermediateNodes),
    transitiveSymmetricRelPath(L1, L2, [L3 | IntermediateNodes]).

(请注意,在这里我们必须基本上在无向图中找到路径,并且必须小心以避免循环)。在您的情况下,也可能需要将line(A, B)line(B, A)视为相同。为此,您应该再引入另一个间接层:

% to check two lines for identity
same(line(A, B), line(A, B)).
same(line(A, B), line(B, A)).

linesEqual2(L1, L2) :-
    same(L1, LI1),
    same(L2, LI2),
    (linesEqual(LI1, LI2); linesEqual(LI2, LI1)).

...并在对称关系的定义中使用linesEqual2代替linesEqual

现在困难的部分是一个命名方案,这样你就不会混淆所有这些谓词......

你也可以用另一种方式来做。鉴于您寻求对称关系的传递闭包,它基本上是所有节点集合(此处为:行)的分区为可分离的子集。这种见解将帮助您编写上述更有效的代码......但这已经超出了这个问题的范围。