Предикат Prolog возвращает только один результат

#prolog #trace

#prolog #трассировка

Вопрос:

У меня есть предикат с именем spider со следующим кодом:

 person(ada).
person(beda).
person(calle).
knows(ada,beda).
knows(ada,calle).
knows(beda,calle).

% Returns true for arguments X and Y if either knows(X,Y) or knows(Y,X) is true.
xknowsy(X,Y) :- knows(X,Y).
xknowsy(X,Y) :- knows(Y,X).

subsetsof([],[]).
subsetsof([A|B],[A|D]) :- subsetsof(B,D).
subsetsof([A|B],D) :- subsetsof(B,D).

dontknowlist([]).
dontknowlist([A]).
dontknowlist([A,B|C]) :- not(xknowsy(A,B)), dontknowlist([A|C]), dontknowlist([B|C]).

listknowsconspirator([],C).
listknowsconspirator([A|B],C) :- knowssomeone(A,C), listknowsconspirator(B,C).

knowssomeone(A,[]).
knowssomeone(A,[B|C]) :- xknowsy(A,B).
knowssomeone(A,[B,C|D]) :- knowssomeone(A,[C|D]).

spider(X) :- person(X), findall(A,xknowsy(X,A),B), subsetsof(B,C), dontknowlist(C),!,
findall(E,person(E),F), removespider(F,X,G), removeconspirators(G,C,L),!,
listknowsconspirator(L,C),!.

removespider([],X,L) :- L = [].
removespider([A|B],X,L) :- A = X, L = B.
removespider([A|B],X,L) :- not(A = X), removespider(B,X,M), L = [A|M].

removeconspirators([],D,L) :- L = [].
removeconspirators(E,[],L) :- L = E.
removeconspirators([A],[B],L) :- A = B, L = [].
removeconspirators([A],[B],L) :- not(A = B), L = [A].
removeconspirators([A,B|C],[D],L) :- A = D, L = [B|C].
removeconspirators([A,B|C],[D],L) :- not(A = D), removeconspirators([B|C],[D],M), L = [A|M].
removeconspirators([A|B],[S,T|U],L) :- removeconspirators([A|B],[S],M),
removeconspirators(M,[T|U],N), L = N.
  

Вызовы spider(ada), spider(beda) и spider(calle) по отдельности возвращают true. Но когда я вызываю spider(X), я не получаю все три решения для X. Я просто получаю первое решение, т. Е. X = ada. Я не понимаю, почему, потому что person(X) гарантирует, что я получу все три возможных значения X для выполнения остальной части предиката. Вызов spider(X) в режиме трассировки, похоже, не дает никаких очевидных объяснений, но мой компилятор SWI-Prolog, похоже, просто игнорирует другие случаи. Почему не все три решения распечатываются при вызове spider(X)?

Ответ №1:

Причиной вышеуказанного поведения являются сокращения, которые вы используете в предикате spider / 1. Если вы удалите их и напишите:

 spider(X) :- person(X), findall(A,xknowsy(X,A),B), subsetsof(B,C),dontknowlist(C),
findall(E,person(E),F), removespider(F,X,G), removeconspirators(G,C,L),
listknowsconspirator(L,C).
  

затем, если вы запрашиваете:

 ?- final_spider(X).
X = ada ;
X = ada ;
X = ada ;
X = beda ;
X = beda ;
X = beda ;
X = calle ;
X = calle ;
X = calle.
  

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

 final_spider(X):-findall(Y,spider(Y),L),sort(L,L1),member(X,L1).
  

Теперь, если вы запросите:

 ?- final_spider(X).
X = ada ;
X = beda ;
X = calle.
  

Он дает правильные решения только один раз.

Комментарии:

1. Вы также можете использовать setof/3 вместо findall/3 sort/2 .