You can eliminate one set with a comparison:
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.
Since X and Y will be instantiated both ways, requiring X be less than Y is a good way to cut the solutions in half.
Your second problem is that X and Y are brothers by more than one parent. The easiest solution here would be to make your rules more explicit:
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.
This method is very specific to this particular problem, but the underlying reasoning is not: you had two copies because a
and b
are "brothers" by c
and also by d
—Prolog was right to produce that solution twice because there was a hidden variable being instantiated to two different values.
A more elegant solution would probably be to use setof/3
to get the solutions. This can work even with your original code:
?- setof(X-Y, (brother(X, Y), X @< Y), Brothers).
Brothers = [a-b].
The downside to this approach is that you wind up with a list rather than Prolog generating different solutions, though you can recover that behavior with member/2
.