Prolog 序言:如何避免没有削减的回溯?

Prolog 序言:如何避免没有削减的回溯?,prolog,backtracking,prolog-dif,logical-purity,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(). n

因此,我试图在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,在相同的两个节点之间进入了一个无限循环,可以到达彼此。我知道这很容易通过添加削减来解决,比如:

% 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时,我只能得到一个可能的答案? (这是学校作业,部分作业是不使用任何内置谓词或控制运算符)

编辑:

更具体地说,赋值的限制是:它应该由纯事实和规则组成,我们不允许使用任何内置谓词或控制结构(包括但不限于算术、切割或否定作为失败)。分号可以。我们需要定义自己所需的任何实用谓词


感谢所有的答案,但我开始认为,我用于查找图形中两个节点之间的路径的方法可能更麻烦,因为从答案来看,似乎没有简单的解决方法。

因为你不能使用内置或控制结构或剪切,也许问题的关键是没有答案。(也就是说,问题的重点可能是强调需要以某种方式来表达否定,例如将否定视为失败或不合格。)


(顺便说一句,您对myappendelem的定义实际上在元素前面。)

使用支持
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


不需要切割来表示术语质量。简单、迂回的方法:

?- setof(M, ( member(M, L1), \+ member(M, L2) ), Ms).
这正是:

对所有
M
进行设置,使
M
L1
的成员,但不是
L2
的成员

如果不想创建有序集合,可以使用
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].
然而,与
\+成员(M,L2)
相比,显示了一种逻辑上更合理的表达“元素不在列表中”的方式

还有一些库谓词可以完成这项工作。处理集合的一种更有效的方法是将它们表示为无重复项的排序列表,如:


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

如果您不能使用
dif
,或
findall
setof
\+
->
甚至
则可以在围绕
\=/code>构建控件的情况下使用以下命令。这仍然是失败的否定(就像dif有一个内部不可见的切口)但是,在其他答案和内置系统谓词中使用方法是更好的方法

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.
但它会给你重复的答案(但至少它们是一样的)


甚至分号也是一个控制运算符。请说明哪些是允许的,然后告诉您的讲师使用一组谓词教授Prolog,这些谓词实际上允许您解决他们给出的任务。要以纯粹的关系方式表示这两个术语不同,请使用
dif/2
。特别是,使用
dif/2
可以表示w所需内容:列表中不出现的元素。这不需要算术、控制结构(除了
(,)/2
),cuts或negation-as-failure。这可能会起作用,会有一些重复结果的冗余检查,但至少不会卡在循环中,但我们也不允许使用negation作为failure。也许我在图形中查找两个节点之间的路径的方法是个问题……:/我认为这是不可能的。在某一点上,您需要检查元素是否不是列表的一部分。这意味着您需要否定,这在prolog中意味着否定失败。my_member2(Key,[Key | T],true)。my_member2(_Key,[],false)。my_member2(Key,[| Tail],X):-my_member(Key,Tail,X)。这将返回true,然后返回false。my_member(a,[a,b,c],TF)。TF=true;TF=false;false。
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.