删除元素的所有出现从列表(Deleting all occurrences of an elemen

2019-06-27 12:39发布

想写一个给定值和列表中的程序,它会删除列表中该值的所有的发生写道:

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(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,3,1,2,3,1,2,3]).

(返回true ,当正确的答案是false )。

我怎样才能使它工作在两种情况呢?

也许我可以检查X is not T在第三行代码,我想:

delMember(X, [T|Xs], Y) :- not(X = T), delMember(X, Xs, Y2), append([T], Y2, Y).

但它不工作。

Answer 1:

切的使用

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

在这里,你可以看到你用!/0的谓词的最后一个子句。 这不是必需的。 最后一句话,之后,没有选择离开(序言从左至右,从上到下记得选择点),因此切断(即删除选择),也不会因为你在你的列表的底部是已经做任何有用的选择。

为了说明,请参阅

a :- b; c.
a :- d.

在这里,要证明a ,Prolog的将首先尝试b ,然后c ,然后d (从左至右,再从上到下)。

顺便说一句,在Prolog的初学者,你应该去尽可能完全避免使用切。 它只是将添加到您的误解,只要你没有得到递归和逻辑编程的基础知识等。

Prolog的递归

那小纸条放在一边,你的问题是你没有正确的理解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这里。

注意,还是有我们不能用谓词一个办法,这就是, +, -, + ,如CTI告诉我们。 所以查询,如?- delMember(1, R, [2, 3]). 将循环使用我的版本。

我希望它是有用的。



Answer 2:

这不是一个真正的答案,只是一个扩展的照会莫古和thanosQR答案,太长,不适合在注释。 这样的答案是愉快的和有益的,但切口去除需要被rethinked。 考虑:

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.

失败在原莫古”的代码,因为在过去的事业的后卫。 这是值得大家注意的是与替换那里的守卫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].


Answer 3:

让我们做一点的改写的:既然你想使用一个以上的实例化模式谓语 “给定值和列表中的程序,它删除列表中该值的所有出现”没有定义应该如何表现在其他情况下。 所以,我们可能要像“如果第二个和第三个参数是列表L1,L2和L1是一样的清单,L2,如果我们忽略了第一个参数的所有出现的谓词是真正的”

现在,有写有多个可能的实例谓词的两种方式; 可以使用元逻辑谓词如var/1ground/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 = [] .

关于最后通话; 序言返回X,生长导致深度优先算法被用于的列表; 通过使用length/2 ,我们可以得到的结果广度优先(_G意味着该变量没有实例化(它可以是任何东西)):

24 ?- length(P,N), del(X, P, Y).
P = [],
N = 0,
Y = [] ;
P = [X],
N = 1,
Y = [] ;
P = [_G548],
N = 1,
Y = [_G548] ;
P = [X, X],
N = 2,
Y = [] ;
P = [X, _G551],
N = 2,
Y = [_G551] ;
P = [_G548, X],
N = 2,
Y = [_G548] ;
P = [_G548, _G551],
N = 2,
Y = [_G548, _G551] ;
P = [X, X, X],

编辑:如@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].

这是因为\==/2\=/2实际上并不强加给变量的限制。 这可以通过切换的第三子句中的规则顺序来解决:

    del(_, [], []).
    del(X, [X|L1], L2):-
        del(X,L1,L2).
    del(X, [H|L1], [H|L2]):-
        del(X,L1,L2),
        X\==H.


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

然而,这意味着该谓词不再尾递归。 修复,我们可以保持这种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]).

然而,根据以下,尾递归版本实际上是慢:

?-time(forall((between(1,50,N),length(X,N),del2(1,X,[2,3,2,3])),true)).
% 25,600,793 inferences, 5.468 CPU in 5.548 seconds (99% CPU, 4682134 Lips)
true.

?- time(forall((between(1,50,N),length(X,N),del_tr(1,X,[2,3,2,3])),true)).
% 37,346,143 inferences, 6.426 CPU in 6.428 seconds (100% CPU, 5811563 Lips)
true.

尽管如此,+ - +不工作(它属于一个无限循环)。 但为什么? 问题在于条款的顺序:德尔(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可以在一个包装谓词被结合,因为它不影响其它的实例的图案。



Answer 4:

这里去与未初始化的第一和第三个参数也适用一个片段:

delMember(X, Y, Z):-
  bagof(A, (setof(X, member(X, Y), L), member(X, L), member(A, Y), A\==X), Z).

我会在这里解释一下这个代码的作用。 我们的想法是:

  1. 构建输入列表的Y独特成员,其与输入构件X统一的列表
  2. 然后用于从建立在1列表)每个X丢弃从输入列表中该元素以获得输出列表Ž而不部件X.

步骤1用做setof(X, member(X, Y), L)它以两种方式工作。 当参数X被已经实例化然后L将或者列表[X]如果X被包含在输入参数Y ,如果它将失败X不包含Y 。 在另一方面,如果X是未初始化然后L将是该组输入参数的不同元件的Y

现在,在步骤2中,我们原路返回过的每个元素L ,以及用于该列表中的每个成员,我们过滤从输入列表此元素Y这产生的结果。 我们收集输出列表中的所有元素这Z

请注意,当时调用的过程沃斯参数X在非实例,在回溯的member(X, Y)我们会得到输入列表中的每个成员Y将被用于过滤。

测试用例:

?- 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].


文章来源: Deleting all occurrences of an element from a list