Почему отдельный запрос возвращает повторяющиеся значения? - PullRequest
4 голосов
/ 20 июня 2019

Вот программа:

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

Вот запрос:

person(P).

Я ожидаю получить 3 имени, вместо этого я получу 4.

1 Ответ

3 голосов
/ 20 июня 2019

Короче говоря : Prolog отслеживает все переменные, которые выходят из цели, включая все равно (_).

Проблема в том, что вы вводите свободные переменныездесь, которые не различны.Действительно, как указано в документации distinct/1 [swi-doc] .Выше функционально эквивалентно:

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

Теперь, если мы сделаем простой вызов с sibling(P, _), мы получим:

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

или с цельюс логическим «или»:

?- 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.

Итак, как вы можете видеть, Goal объединяется дважды: один раз с sibling(joe, mary) и один раз с sibling(joe, bob).Фильтр уникальности не будет иметь никакого эффекта, так как mary - это не то же самое, что bob.

Однако мы можем определить здесь предикат-помощник, чтобы избавиться от этих переменных, например:

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

и затем мы можем запросить с помощью:

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

или без использования переменной Goal:

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