Пролог: человек является родным братом?

У меня возникли проблемы с пониманием того, почему мой код в прологе делает что-то, основанное на порядке, в который я помещаю свои правила.

Вот моя база данных:

parent(tom, bob).
parent(tom, liz).
parent(mary, bob).
parent(mary, liz).

male(tom).
male(bob).
female(mary).
female(liz).

И вот правила:

%difference(X, Y) ==> Predicate to check if two people X and Y are not the same person.
difference(X, Y) :- \==(X, Y).
father(X, Y) :- male(X), parent(X, Y), difference(X, Y).
mother(X, Y) :- female(X), parent(X, Y), difference(X, Y).
sibling(X, Y) :-
    difference(X, Y),
    mother(M, X), mother(M, Y),
    father(F, X), father(F, Y).

Проблема в том, что когда я это делаю,

?- sibling(bob, X).

Я получаю

X = bob ;
X = liz ;
false.

Но когда я меняю порядок (положим разницу (X, Y) в последней части)

sibling(X, Y) :-
    mother(M, X), mother(M, Y),
    father(F, X), father(F, Y),
    difference(X, Y).

и я вызываю

?- sibling(bob, X).

Я получаю

X = liz;
false.

что я хочу.

До сих пор я только видел, что упорядочение правил имеет значение при выполнении рекурсии. Поэтому я не понимаю, как боб все еще является родным братом самого себя, потому что я сначала поставил разницу.

Спасибо за любую помощь!

Ответ 1

Это связано с тем, как работает унификация. Если сначала положить разницу, значения X и Y еще не объединены ни с какими значениями. Рассмотрим трассировку:

 goal list: [sibling(bob, Z)]
 goal: sibling(bob, Z).
 X-> bob, Y -> Z
 goal list: [difference(bob, Y), mother(M, bob), mother(M, Y), father(F, bob), father(F, Y).]
 goal: difference(bob, Y) --SUCCESS
 goal list: [mother(M, bob), mother(M, Y), father(F, bob), father(F, Y).]
 goal: mother(M, bob)
 ...

Когда вы поместили разный вызов последним, как X, так и Y были унифицированы, и разница будет терпеть неудачу, если они будут одинаковыми. Затем произойдет обратное отслеживание.

Используйте функцию трассировки вашей прологовой среды, чтобы увидеть, что происходит шаг за шагом во время выполнения.

Ответ 2

Фактическая причина вашей проблемы - (\==)/2 в пределах different/2. Это удается слишком часто. Замените его на dif/2, и вы получите ожидаемое поведение. dif/2 доступен во многих системах Prolog, таких как SICStus, YAP, B, SWI. Вы также можете определить безопасное приближение в ISO-Prolog:

iso_dif(X, Y) :-
   X \== Y,
   ( X \= Y -> true
   ; throw(error(instantiation_error,iso_dif/2))
   ).

Теперь, если аргументы не будут достаточно инстанцированы, вы получите сообщение об ошибке создания. Prolog прерывает вычисления и говорит: я понятия не имею! Это намного лучше, чем притворяться, что у него есть идея, пока она не имеет.

Используя iso_dif/2, вам все равно придется поместить его в конце правила. Но на этот раз Prolog будет следить за правильным использованием.

| ?- iso_dif(a,b).

yes
| ?- iso_dif([a,_],[b,_]).

(8 ms) yes
| ?- iso_dif([a,X],[X,b]).

yes
| ?- iso_dif([a,X],[a,X]).

no
| ?- iso_dif([a,X],[X,X]).
uncaught exception: error(instantiation_error,iso_dif/2)