2

我正在尝试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).

然而,这给了我不一致的结果。怎么了?先感谢您。

4

2 回答 2

2

您的实施存在几个问题。

  1. 一开始没有清理。retractall(temp(_))在之前添加T,

  2. obtain/2将获得许多不同的答案,因为retract(temp(A))将给出许多答案,并且因为第二个子句obtain(S,S)将始终是一个解决方案。这可以通过在之后添加剪切来保存retract

    | ?- 获得([],S)。
    S = [2,1] ? ;
    S = [1] ? ;
    S = [2] ? ;
    S = [] ? ;
    不
    

  3. 您可能希望通过使用asserta/1或重新定义来更改顺序obtain/2

  4. 您的定义不可重入。这不能轻易解决。您将需要一些gensym类似的功能或更高级的功能。

  5. 有关assert/1vs.的详细说明,assertz/1请参阅此答案

于 2015-03-23T22:05:52.377 回答
0

试试这个,我们在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]].

警告:具有逻辑更新语义的 Prolog 系统
在重复收回/1 期间可能效率不高。

于 2020-11-08T20:45:40.160 回答