Альтернатива найти все

Я пытаюсь создать альтернативу findall в Prolog.

Что у меня есть:

solutions(A,T,S) :- 
   T,
   assert(temp(A)),
   fail.
solutions(A,T,S) :-
   obtain([],S).

obtain(X,S) :-
   retract(temp(A)),
   obtain([A|X],S).
obtain(S,S).

Однако это дает мне противоречивые результаты. Что не так? Заранее спасибо.


person PablodeAcero    schedule 23.03.2015    source источник
comment
Можете ли вы описать, что несовместимо в ваших результатах?   -  person lurker    schedule 23.03.2015
comment
Возможно, использование assertz вместо assert решит это загадочное несоответствие.   -  person Eugene Sh.    schedule 23.03.2015
comment
@ЕвгенийШ. Я считаю, что assert и assertz делают одно и то же. :)   -  person lurker    schedule 23.03.2015
comment
@lurker Будет ли гарантия добавляться в конец? не могу найти упоминания об этом..   -  person Eugene Sh.    schedule 23.03.2015
comment
@ЕвгенийШ. Если это SWI Prolog, то я полагаю, что да, поскольку assert/1 устарел в пользу assertz/1. Я не знаю, почему это не будет последовательно. Я все еще жду, пока ОП объяснит, что несовместимо. :)   -  person lurker    schedule 23.03.2015


Ответы (2)


Есть несколько проблем с вашей реализацией.

  1. В начале уборки нет. Добавьте retractall(temp(_)) перед T,

  2. obtain/2 will succeed with many different answers, because retract(temp(A)) will give many answers, and because the second clause obtain(S,S) will always be a solution. This can be saved by adding a cut after retract.

    | ?- obtain([],S).
    S = [2,1] ? ;
    S = [1] ? ;
    S = [2] ? ;
    S = [] ? ;
    no
    

  3. Вы можете изменить порядок, используя asserta/1 или переопределив obtain/2.

  4. Ваше определение не является повторным. Это не может быть решено легко. Вам понадобятся либо некоторые gensym функции, либо еще более продвинутые функции.

  5. Мелкий шрифт assert/1 против assertz/1 см. в этом ответе.

person false    schedule 23.03.2015

Попробуйте это, используя сокращение (!) после retract/1 и явно assertz/1:

solutions(A,T,_) :- 
   T,
   assertz(temp(A)),
   fail.
solutions(_,_,S) :-
   obtain(S).

obtain([A|S]) :-
   retract(temp(A)), !,
   obtain(S).
obtain([]).

Работает нормально, но не реентерабельно, второй результат запроса неверен:

?- solutions(X,between(1,3,X),L).
L = [1, 2, 3].

?- solutions(X-R,(between(1,3,X),solutions(Y,between(1,X,Y),R)),L).
L = [3-[2-[1-[1], 1, 2], 1, 2, 3]].

Редактировать 08.11.2020:
Вот реентерабельное решение с использованием gensym/2:

solutions(A,T,L) :-
   setup_call_cleanup(
      gensym('bag',B),
      solutions(B,A,T,L),
      retractall(temp(B,_))).

solutions(B,A,T,_) :- 
   T,
   assertz(temp(B,A)),
   fail.
solutions(B,_,_,S) :-
   obtain(B,S).

obtain(B,[A|S]) :-
   retract(temp(B,A)), !,
   obtain(B,S).
obtain(_,[]).

Теперь оба запроса работают нормально:

?- solutions(X,between(1,3,X),L).
L = [1, 2, 3].

?- solutions(X-R,(between(1,3,X),solutions(Y,between(1,X,Y),R)),L).
L = [1-[1], 2-[1, 2], 3-[1, 2, 3]].

Предупреждение. Пролог-система с логической семантикой обновления
может быть неэффективной при повторном выполнении retract/1.

person Mostowski Collapse    schedule 08.11.2020