Prolog 从列表中删除元素的所有引用

Prolog 从列表中删除元素的所有引用,prolog,prolog-dif,Prolog,Prolog Dif,尝试编写给定值和列表的过程时,会删除所写入列表中该值出现的所有内容: delMember(X, [], []) :- !. delMember(X, [X|Xs], Y) :- !, delMember(X, Xs, Y). delMember(X, [T|Xs], Y) :- !, delMember(X, Xs, Y2), append([T], Y2, Y). 由于cut无法正确回答以下问题: delMember(Y, [1,2,3,1,2,3,1,2,3], [1, 2, 1, 2,

尝试编写给定值和列表的过程时,会删除所写入列表中该值出现的所有内容:

delMember(X, [], []) :- !.
delMember(X, [X|Xs], Y) :- !, delMember(X, Xs, Y).
delMember(X, [T|Xs], Y) :- !, delMember(X, Xs, Y2), append([T], Y2, Y).
由于
cut
无法正确回答以下问题:

delMember(Y, [1,2,3,1,2,3,1,2,3], [1, 2, 1, 2, 1, 2 ]).
delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,3,1,2,3,1,2,3]).
如果我删除削减:

delMember(X, [], []).
delMember(X, [X|Xs], Y) :- delMember(X, Xs, Y).
delMember(X, [T|Xs], Y) :- delMember(X, Xs, Y2), append([T], Y2, Y).
它在以下查询中失败:

delMember(Y, [1,2,3,1,2,3,1,2,3], [1, 2, 1, 2, 1, 2 ]).
delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,3,1,2,3,1,2,3]).
(当正确答案为
false
时,返回
true

如何使其在这两种情况下都有效

也许我可以检查第三行代码中的
X不是T
,我尝试了:

delMember(X, [T|Xs], Y) :- not(X = T), delMember(X, Xs, Y2), append([T], Y2, Y).
但它不起作用。

使用切割 在这里,您可以看到您使用了
/0
在谓词的最后一个子句中。没必要。在最后一个子句之后,没有选择了(Prolog从左到右、从上到下记住选择点),因此剪切(删除选择)不会做任何有用的事情,因为您已经在选择列表的底部

要进行说明,请参见

a :- b; c.
a :- d.
在这里,为了证明
a
,Prolog将首先尝试
b
,然后是
c
,然后是
d
(从左到右,从上到下)

顺便说一句,作为Prolog的初学者,您应该尽量避免使用cut。它只会增加你的误解,只要你没有得到递归和其他基本的逻辑编程

序言递归 撇开这点不谈,你的问题是你还没有正确理解Prolog递归。请看第一部分已经解决了这个问题

你的第三条错了:

delMember(X, [T|Xs], Y) :- delMember(X, Xs, Y2), append([T], Y2, Y).
应改为:

delMember(X, [T|Xs], [T|Y]) :- delMember(X, Xs, Y).
嗯,这不是真的错,只是真的不太理想。它不是尾部递归的,并且使用
append/3
,这将把线性谓词变成二次谓词。另外,正如您所注意到的,因为它不是尾部递归的,所以在某些情况下,终止更难获得

然后,取消使用切割
/0 ,可以考虑添加一个保护到最后一个子句:

delMember(_, [], []).
delMember(X, [X|Xs], Y) :-
    delMember(X, Xs, Y).
delMember(X, [T|Xs], [T|Y]) :-
    dif(X, T),
    delMember(X, Xs, Y).
守卫,
dif(X,T)
,指定如果我们在第三种情况下,我们不能同时在第二种情况下:
X
不能与
T
统一

注意,还有一种方法我们不能使用谓词,这就是,
+,-,+
,正如我们所说的。所以像
?-delMember(1,R,[2,3])这样的查询将与我的版本循环


我希望它是有用的。

让我们重新表述一下:因为您希望在多个实例化模式中使用谓词,“一个给定值和列表的过程,它会删除列表中所有出现的值”并没有定义它在其他情况下的行为。因此,我们可能希望类似“如果第二个和第三个参数是列表L1,则谓词为true,如果忽略第一个参数的所有出现,则L1与L2是同一列表”

现在,有两种方法可以编写具有多个可能实例化的谓词;您可以使用元逻辑谓词,例如
var/1
ground/1
,并为每一个谓词编写代码(这可能允许您编写针对特定实例化进行优化的代码),或者编写逻辑上定义属性的代码(这可能更具挑战性)

在这种情况下,我们可以这样做:

    del(_, [], []).
    del(X, [X|L1], L2):-
        del(X,L1,L2).
    del(X, [H|L1], [H|L2]):-
        X\==H,
        del(X,L1,L2).
具有以下行为:

19 ?- del(1, [1,2,3], X).
X = [2, 3] ;
false.
1,2,3,
20 ?- del(1, [1,2,3], [2,3]).
true ;
false.

21 ?- del(X, [1,2,3], [2,3]).
X = 1 ;
false.

22 ?- del(X, [1,2,3], Y).
X = 1,
Y = [2, 3] ;
X = 2,
Y = [1, 3] ;
X = 3,
Y = [1, 2] ;
Y = [1, 2, 3] ;
false.

23 ?- del(X, P, Y).
P = Y, Y = [] ;
P = [X],
Y = [] ;
P = [X, X],
Y = [] ;
P = [X, X, X],
Y = [] ;
P = [X, X, X, X],
Y = [] ;
P = [X, X, X, X, X],
Y = [] ;
P = [X, X, X, X, X, X],
Y = [] .
关于最后一次通话;prolog返回一个X列表,该列表因使用深度优先算法而增长;通过使用
length/2
我们可以得到宽度优先的结果(_G意味着变量没有实例化(它可以是任何东西)):

编辑:正如@chac所指出的,如果第一个列表(至少)有一个重复元素,则上述谓词的行为不正确:

?- del(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1] ;
X = 1,
Y = [1, 2] ;                  <----- wrong
Y = [1, 2, 1].
然而,这意味着谓词不再是尾部递归的。为了解决这个问题,我们可以保留一个X不应该是的值的列表:

del(X,L1,L2):-
    del(X,L1,L2,[]).

del(X, [], [], NotX):-
    \+ member(X,NotX).
del(X, [X|L1], L2, NotX):-
    del(X,L1,L2,NotX).
del(X, [H|L1], [H|L2], NotX):-
    X\==H,     % <--- optional; stops the execution earlier (saving time)
    del(X,L1,L2,[H|NotX]).
尽管如此,+-+仍然不起作用(它落在一个无限循环中)。但是为什么呢?问题在于条款的顺序: del(1,L1,[2])将首先应用将X“添加”到L1头部的规则,然后永远应用相同的规则。 可以(再次)使用
length/2

?- length(X,2), del(1,X,[2]).
X = [1, 2] ;
X = [2, 1] ;
false.
或者,我们可以更改条款的顺序:

del(_, [], []).
del(X, [H|L1], [H|L2]):-
    X\==H,
    del(X,L1,L2),
    X\==H.
del(X, [X|L1], L2):-
    del(X,L1,L2).
但是
length/2
可能再次有用,因为没有它,prolog会执行深度优先搜索:

?- del(1,X,[2]).
X = [2] ;
X = [2, 1] ;
X = [2, 1, 1] ;
X = [2, 1, 1, 1] ;
X = [2, 1, 1, 1, 1] ;
X = [2, 1, 1, 1, 1, 1] ;
X = [2, 1, 1, 1, 1, 1, 1] 

当然,
length/2
可以合并到包装谓词中,因为它不会影响其他实例化模式。

这不是一个真正的答案,只是对和答案的扩展注释,太长了,无法放入注释中。 这些答案是令人愉快和有启发性的,但割伤的去除需要重新考虑。考虑:

delMember(_, [], []).
delMember(X, [X|Xs], Y) :-
    delMember(X, Xs, Y), !.
delMember(X, [T|Xs], [T|Y]) :-
    delMember(X, Xs, Y).
这个定义允许

?- delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,1,2,1,2]).
Y = 3.
这在原始Mog代码中失败,因为最后一个原因是防护。值得注意的是,用
X\==T
(这将测试限制为匹配的实例化状态)替换那里的保护也解决了这个查询,正如thanosQR所指出的

但这些片段都不能解决一般情况:

?- del(X,[1,2,1],Y).
X = 1,
Y = [2] ;
X = 2,
Y = [1, 1] ;
X = 1,
Y = [1, 2] ;
Y = [1, 2, 1].

下面是一个代码片段,它也适用于未实例化的第一个和第三个参数:

delMember(X, Y, Z):-
  bagof(A, (setof(X, member(X, Y), L), member(X, L), member(A, Y), A\==X), Z).
我将在这里解释这段代码的作用。其想法是:

  • 构建一个输入列表Y的不同成员列表,该列表与输入成员X统一
  • 然后,对于构建在1)上的列表中的每个X,从输入列表中丢弃该元素,以获得不包含成员X的输出列表Z
  • 步骤1是用setof(X,member(X,Y),L)
    完成的,它有两种工作方式。当参数
    X
    已实例化时,如果
    X
    包含在输入参数
    Y
    中,则
    L
    将是列表
    [X]
    ,如果
    X
    未包含在
    Y
    中,则该列表将失败。另一方面,如果
    X
    未实例化,则
    L
    将是输入参数
    Y
    的不同元素集

    现在,在步骤2中,我们回溯
    L
    的每个元素,以及该元素的每个成员
    ?- del(X,[1,2,1],Y).
    X = 1,
    Y = [2] ;
    X = 2,
    Y = [1, 1] ;
    X = 1,
    Y = [1, 2] ;
    Y = [1, 2, 1].
    
    delMember(X, Y, Z):-
      bagof(A, (setof(X, member(X, Y), L), member(X, L), member(A, Y), A\==X), Z).
    
    ?- delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,3,1,2,3,1,2,3]).
    false.
    
    ?- delMember(Y, [1,2,3,1,2,3], X).
    Y = 1,
    X = [2, 3, 2, 3] ;
    Y = 2,
    X = [1, 3, 1, 3] ;
    Y = 3,
    X = [1, 2, 1, 2].
    
    ?- delMember(Y, [1,2,3,1,2,3,1,2,3], [1,2,1,2,1,2]).
    Y = 3.
    
    ?- delMember(X,[1,2,1],Y).
    X = 1,
    Y = [2] ;
    X = 2,
    Y = [1, 1].