您可以通过比较消除一组:
brother(X, Y) :-
son(X, P),
son(Y, P),
X \= Y, X @< Y.
?- brother(X, Y).
X = a,
Y = b ;
X = a,
Y = b ;
false.
由于 X 和 Y 将以两种方式实例化,因此要求 X 小于 Y 是将解决方案减半的好方法。
您的第二个问题是 X 和 Y 是不止一位父母的兄弟。这里最简单的解决方案是使您的规则更加明确:
mother(a, d).
mother(b, d).
father(a, c).
father(b, c).
brother(X, Y) :-
mother(X, M), mother(Y, M),
father(X, F), father(Y, F),
X \= Y, X @< Y.
?- brother(X, Y).
X = a,
Y = b ;
false.
这种方法非常特定于这个特定问题,但根本原因不是:您有两个副本,因为a
和b
是“兄弟”,c
并且也是“兄弟” d
——Prolog 两次生成该解决方案是正确的,因为有一个隐藏变量被实例化为两个不同的价值观。
一个更优雅的解决方案可能是用来setof/3
获取解决方案。这甚至可以使用您的原始代码:
?- setof(X-Y, (brother(X, Y), X @< Y), Brothers).
Brothers = [a-b].
这种方法的缺点是您最终会得到一个列表,而不是 Prolog 生成不同的解决方案,尽管您可以使用member/2
.