Prolog中对称关系的传递闭包

4

我是Prolog的初学者,我想创建“兄弟”关系。

这个关系应该是对称的,例如如果brother(alin,alex)是正确的,brother(alex,alin)也应该是正确的。

它还应该是可传递的,例如如果brother(alin,alex)brother(alex,claudiu)是正确的,brother(alin,claudiu)也应该是正确的。

结合这两个特性,如果brother(alex,alin)brother(alex,claudiu)都成立,则brother(alin,claudiu)也应该成立。

下面是我的代码:

r_brother(alin, alex).
r_brother(alin, ciprian).
r_brother(alex, claudiu).

s_brother(X, Y) :- r_brother(X, Y).
s_brother(X, Y) :- r_brother(Y, X).

brother(L1, L2) :-
    t_brother(L1, L2, []).

t_brother(L1, L2, _) :-
    s_brother(L1, L2).

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

r_brother - 是基本关系

s_brother - 是对称的兄弟关系(这个很好用

t_brother - 应该是传递和对称的关系,我保留中间节点以避免出现循环

问题在于:

?- brother(X, alin).

是:

X = alex ;
X = ciprian ;
X = alin ;
X = alin ;
X = alin ;
X = alin ;
X = alex ;
X = alex ;
X = alex ;
X = alex ;
X = ciprian ;
X = ciprian ;
X = claudiu ;
X = claudiu ;
false.

我查看了追踪记录,理解了问题所在,但是不知道如何解决。

alin 不应该是一个可能的答案,其他答案应该只出现一次。

2个回答

3

我认为基本问题在于您没有检查L2是否已经在t_brother/3的第一个子句中找到。并且应该将初始的L1添加到brother/2的列表中:

brother(L1, L2) :-
  t_brother(L1, L2, [L1]).                   % <-- [L1] instead of []

t_brother(L1, L2, IntermediateNodes) :-
  s_brother(L1, L2),
  \+ member(L2, IntermediateNodes).          % <-- added this check

t_brother(L1, L2, IntermediateNodes) :-      % <-- this clause is unchanged
  s_brother(L1, L3),
  \+ member(L3, IntermediateNodes),
  t_brother(L3, L2, [L3 | IntermediateNodes]).

您可以通过使用“或”语句仍然缩短解决方案:
t_brother(L1, L2, IntermediateNodes) :-
  s_brother(L1, L3),
  \+ member(L3, IntermediateNodes),
  ( L2=L3
  ; t_brother(L3, L2, [L3 | IntermediateNodes])).

只是为了我理解,在“(L2=L3; t_brother(L3, L2, [L3 | IntermediateNodes]))”中,这是否意味着如果第一个条件(L2=L3)成立,它就不需要验证第二个条件? - colegu
不,这只是一个简单的析取 (AlternativeA;AlternativeB)。要么 L2 是下一个兄弟节点 L3,要么我们通过递归调用 t_brother 找到一个 L2。也许你把它和 (Condition -> Then ; Else) 语句混淆了,后者意味着一个本地剪枝? - danielp

1
你可以像传递性定义一样编写兄弟关系,如下所示。
s_brother(X, Y) :- r_brother(X, Y);r_brother(Y, X).

brother(X,Y) :- s_brother(X, Y).
brother(X,Y) :- s_brother(X, Z),s_brother(Z, Y),X\=Y.

如果一个人是对称的兄弟或者他们有共同的兄弟,那么X是Y的兄弟,同时要添加他们不同的条件。
尝试在你的代码中添加X\=Y以排除"alin"作为解决方案。

谢谢,这个翻译更简洁了 :),但是问题在于如果你添加另一个事实,比如r_brother(claudiu, jack),你将不会得到“jack”作为答案。这必须是递归才能工作。 - colegu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接