Prolog:如何在没有削减的情况下避免回溯?

时间:2015-09-29 03:11:35

标签: prolog backtracking prolog-dif logical-purity

所以我试图在prolog中编写一个谓词,它可以获取列表L1和列表L2,并返回L1中不在L2中的所有元素的列表。这就是我到目前为止所做的:

% Append an element to a list.
myappendelem(X,L,[X|L]).

% True if input list contains X.
mycontains([H | T], X) :-
    H == X;
    mycontains(T,X).

% Does the work for notin().
nocontains([],_,A,A).
nocontains([H|T],L,A,R):-
    mycontains(L,H),
    nocontains(T,L,A,R);
    myappendelem(H,A,AR),
    nocontains(T,L,AR,R).

% Returns all elements in L1 not in L2.
notin(L1,L2,R):-
    nocontains(L1,L2,[],X).

这有效,但它提供了多个答案,例如:

notin([5,1],[4,3,2,1],X).
X = [5];
X = [5,1].

这是一个问题,因为我使用这个谓词来排序图中的路径(L1是我可能去的节点列表,L2是我已去过的节点)以确保我不会访问同一节点不止一次并陷入循环中。但是这个实现让我陷入了一个循环,因为它在尝试第一个X之后回溯并且它失败了,对于未改变的X,进入可以相互到达的相同两个节点之间的无限循环。我知道通过向nocontains添加剪切很容易解决这个问题:

% Does the work for notin().
nocontains([],_,A,A).
nocontains([H|T],L,A,R):-
    mycontains(L,H),!,
    nocontains(T,L,A,R);
    myappendelem(H,A,AR),!,
    nocontains(T,L,AR,R).

但是有没有办法在没有削减的情况下实现同样的目标?所以,当我使用notin时,我只得到一个可能的答案? (它用于学校,部分任务是不使用任何内置谓词或控制操作符)

修改

更具体地说明赋值的局限性:它应该包含纯粹的事实和规则,我们不允许使用任何内置的谓词或控制结构(包括但不限于算术,削减或否定 - 作为故障)。分号没问题。我们需要定义自己的任何实用程序谓词。

感谢所有的答案,但我开始认为这可能是我用于在图中的两个节点之间找到路径的方法的问题,因为答案看起来不像是轻松解决这个问题。

4 个答案:

答案 0 :(得分:5)

使用支持dif/2的Prolog系统。有许多这样的系统,甚至是免费的系统。

dif/2用于以纯粹的关系方式表达两个术语不同

例如,在您的情况下,描述一个元素是而不是列表的成员:

not_in(_, []).
not_in(L, [X|Xs]) :-
        dif(L, X),
        not_in(L, Xs).

或更短,使用maplist(dif(L), Ls)

你可以在你的例子中使用它,如下所示:

?- Ls1 = [5,1], Ls2 = [4,3,2,1], member(M, Ls1), not_in(M, Ls2).
Ls1 = [5, 1],
Ls2 = [4, 3, 2, 1],
M = 5 ;
false.

请注意,这会产生唯一的解决方案 M = 5

不需要削减来表达术语不平等。

答案 1 :(得分:2)

由于你不能使用内置或控制结构或剪切,可能问题的关键是没有答案。 (也就是说,问题的关键在于强调否定失败或某种等同的必要性。)

(顺便提一下,你对myappendelem的定义实际上是在元素之前。)

答案 2 :(得分:1)

琐碎,迂回的方式:

?- setof(M, ( member(M, L1), \+ member(M, L2) ), Ms).

正是:

  

制作一组所有M,使M成为L1的成员,但不是L2的成员。

?- L1 = [a,b,c,d,e,f,g],
   L2 = [c,f,x,y],
   setof(M, ( member(M, L1), \+ member(M, L2) ), Ms).
Ms = [a, b, d, e, g].

如果您不想制作有序集,可以使用bagof/3代替setof/3

?- L1 = [c,b,a,c,b,a],
   L2 = [c,y],
   setof(M, ( member(M, L1), \+ member(M, L2) ), Ms).
Ms = [a, b].

?- L1 = [c,b,a,c,b,a],
   L2 = [c,y],
   bagof(M, ( member(M, L1), \+ member(M, L2) ), Ms).
Ms = [b, a, b, a].

然而,answer by @mat显示了一种可以说是更合理的方式来表达“不在列表中的元素”而不是\+ member(M, L2)

还有一些库谓词可以完成这项任务。处理集合的另一种高效方法是将它们表示为没有重复的排序列表,如library(ordsets)中所示:

?- L1 = [a,b,c,d,e,f,g,a,a,a],
   L2 = [c,f,x,y],
   time(setof(M, ( member(M, L1), \+ member(M, L2) ), Ms)).
% 85 inferences, 0.000 CPU in 0.000 seconds (95% CPU, 1841262 Lips)
Ms = [a, b, d, e, g].

?- L1 = [a,b,c,d,e,f,g,a,a,a],
   L2 = [c,f,x,y],
   time(( sort(L1, S1),
          sort(L2, S2),
          ord_subtract(S1, S2, S) )).
% 28 inferences, 0.000 CPU in 0.000 seconds (90% CPU, 1066545 Lips)
S1 = [a, b, c, d, e, f, g],
S = [a, b, d, e, g].

(一般来说,较少的推论意味着更少的工作来证明查询。但是当涉及sort/2时,这有点误导,因为它总是算作一个推理。在SWI-Prolog中,它使用本机C实现,很难与纯Prolog代码进行比较。另外,请记住setof/3在内部使用sort/2

答案 3 :(得分:1)

如果您无法使用diffindallsetof\+->甚至;,则可以使用以下内容控件围绕\=构建。这仍然是作为失败的否定(并且就像有一个内部隐形切割那样)。使用其他答案和内置系统谓词中的方法是一种更好的方法。

my_member(I,[I|_]).
my_member(I,[_|T]):-
  my_member(I,T).

notin(Test,List,Result):-
  notin_(Test,List,[],[],Result).

notin_([],_,_,Result,Result).
notin_([H|T],List,AcIn,AcOut,Result):-
  my_member(H,List),
  notin_(T,List,[H|AcIn],AcOut,Result).
  notin_([H|T],List,AcIn,AcOut,Result):-
  not_member(H,List),
  notin_(T,List,AcIn,[H|AcOut],Result).

item_is_not_head(Item,[H|_]):-
  H \= Item.

not_member(_,[]).
not_member(Item,List):-
  List=[_|T],
  item_is_not_head(Item,List),
  not_member(Item,T).

查询:

?-notin([5,1],[4,3,2,1],X).
X =[5],
false.

但它会给你重复的答案。 (但至少他们是一样的。)

  ?- notin([a,b,q,x,x,y],[a,b,q,d,a,a],R).
  R = [y, x, x] ;
  R = [y, x, x] ;
  R = [y, x, x] ;
  false.