我无法理解为什么序言中的代码根据我放置规则的顺序执行某些操作。
这是我的数据库:
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.
这就是我想要的。
到目前为止,我只看到在进行递归时规则的顺序很重要。 所以我不明白为什么鲍勃仍然是他自己的兄弟姐妹只是因为我把差异检查放在第一位。
感谢您的帮助!
问题的实际原因是
(\==)/2
内的 different/2
。它经常成功。将其替换为 dif/2
,您将获得预期的行为。 dif/2
适用于许多 Prolog 系统,例如 SICStus、YAP、B、SWI。您还可以在 ISO-Prolog 中定义安全近似,如下所示:
dif_si(X, Y) :-
X \== Y,
( X \= Y -> true
; throw(error(instantiation_error,dif_si/2))
).
(也可以在 Scryer 的
library(si)
中找到。)现在,如果参数没有充分实例化,您将收到实例化错误。 Prolog 中止计算并说:我不知道!这比假装自己有想法但实际上没有想法要好得多。
使用
dif_si/2
(以前称为 iso_dif/2
),您仍然需要将其放在规则的末尾。不过这一次,Prolog 会留意它的正确用法。
?- dif_si(a,b).
true.
?- dif_si([a,_],[b,_]).
true.
?- dif_si([a,X],[X,b]).
true.
?- dif_si([a,X],[a,X]).
false.
?- dif_si([a,X],[X,X]).
error(instantiation_error,dif_si/2).
这是因为统一的运作方式。如果将差异放在首位,则 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都已经统一了,如果它们是相同的值,差异就会失败。然后就会发生回溯。
使用 prolog 环境的跟踪功能来查看执行过程中一步一步发生的情况。