Why does a distinct query return duplicate values?

Viewed 134

Here is the program:

sibling(joe, mary).
sibling(joe, bob).
person(P) :- distinct(sibling(P, _); sibling(_, P)).

Here is the query:

person(P).

I would expect to get 3 names, instead I get 4.

1 Answers

In short: Prolog keeps track of all variables that come out of the goal, including don't cares (_).

The problem is that you introduce free variables here, that are not distinct. Indeed, as is specified in the documentation of distinct/1 [swi-doc]. The above is functionally equivalent to:

distinct(Goal) :-
    findall(Goal, Goal, List),
    list_to_set(List, Set),
    member(Goal, Set).

Now if we make a simple call with sibling(P, _), we get:

?- Goal = sibling(P, _), distinct(Goal).
Goal = sibling(joe, mary),
P = joe ;
Goal = sibling(joe, bob),
P = joe.

or with a goal with a logical "or":

?- Goal = (sibling(P, _); sibling(_, P)), distinct(Goal).
Goal =  (sibling(joe, mary);sibling(_4908, joe)),
P = joe ;
Goal =  (sibling(joe, bob);sibling(_4908, joe)),
P = joe ;
Goal =  (sibling(mary, _4904);sibling(joe, mary)),
P = mary ;
Goal =  (sibling(bob, _4904);sibling(joe, bob)),
P = bob.

So as you can see, Goal is unified twice: once with sibling(joe, mary), and once with sibling(joe, bob). The uniqness filter will not have any effect, since mary is not the same as bob.

We can however define a helper predicate here, to get rid of these variables, like:

person(P) :-
    sibling(P, _).
person(P) :-
    sibling(_, P).

and then we can query with:

?- Goal = person(P), distinct(Goal).
Goal = person(joe),
P = joe ;
Goal = person(mary),
P = mary ;
Goal = person(bob),
P = bob.

or without using a Goal variable:

?- distinct(person(P)).
P = joe ;
P = mary ;
P = bob.
Related