Пролог: человек является родным братом?
У меня возникли проблемы с пониманием того, почему мой код в прологе делает что-то, основанное на порядке, в который я помещаю свои правила.
Вот моя база данных:
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)