简单的prolog程序太早返回false

时间:2013-12-01 21:26:03

标签: prolog

自从我在Prolog中编程以来已经有一段时间了。今天,我试着做一个简单的程序。它列出了属于同一家庭的一些事实。如果两个人属于同一个家庭,他们就不能互赠礼物。我想让所有允许某人送礼的人(或至少一个人)。

family(john, jack).
family(matt, ann).
family(ann, jack).
family(jordan, michael).
family(michael, liz).

sameFamily(X, Y) :-
  family(X, Y).
sameFamily(X, X) :-
  false.
sameFamilySym(X, Y) :-
  sameFamily(X, Y).
sameFamilySym(X, Y) :-
  sameFamily(Y, X).
sameFamilyTrans(X, Z) :-
  sameFamilySym(X, Y),
  sameFamilySym(Y, Z).

gift(X, Y) :-
  not(sameFamilyTrans(X, Y)).

如果sameFamilyTrans/2返回false,实际上应该返回true,则会有一些查询。

sameFamilyTrans/2显然是错误的。我想我需要保留一份中间过渡点清单。像这样:

sameFamilyTrans(X, Z, [Y|Ys]) :-
  sameFamilySym(X, Y, []),
  sameFamilyTrans(Y, Z, Ys).

但后来我不知道怎么称呼它。

P.S。我正在使用SWI-Prolog,如果这有任何区别。

2 个答案:

答案 0 :(得分:4)

是的,你走在正确的轨道上。诀窍是使用空累加器调用传递闭包,并检查每个步骤是否找到一个循环(即,我们之前是否已经看过该家族成员。正如“假”指出的那样,这些人需要是但是,在进入not之前已经实例化了。

总而言之,这有效:

family(john, jack).
family(matt, ann).
family(ann, jack).
family(jordan, michael).
family(michael, liz).

sameFamily(X, Y) :-
  family(X, Y).
sameFamilySym(X, Y) :-
  sameFamily(X, Y).
sameFamilySym(X, Y) :-
  sameFamily(Y, X).

sameFamilyTrans(X, Y, Acc) :-
  sameFamilySym(X, Y),
  not(member(Y,Acc)).

sameFamilyTrans(X, Z, Acc) :-
  sameFamilySym(X, Y),
  not(member(Y,Acc)),
  sameFamilyTrans(Y, Z, [X|Acc]).

person(X) :- family(X, _).
person(X) :- family(_, X).

gift(X, Y) :-
  person(X),
  person(Y),
  X \= Y,
  not(sameFamilyTrans(X, Y, [])).

一些背景知识:传递闭包实际上不是一阶可定义的(参见https://en.wikipedia.org/wiki/Transitive_closure#In_logic_and_computational_complexity)。所以可以预期这会有点棘手。

答案 1 :(得分:1)

在Prolog中以非常简陋的方式实施否定。只有在充分实例化否定查询时,您才能获得有用的答案。为此,请定义描述您正在考虑的所有人的关系person/1。然后你可以写:

gift(X,Y) :-
   person(X),
   person(Y),
   \+ sameFamily(X,Y).

sameFamily/2的定义还有另一个问题。