从列表中删除给定值并存储在累加器中

时间:2018-05-20 21:26:50

标签: list debugging prolog accumulator

到目前为止,我有:

remove(_,[],R,R).

remove(X,[H|T],[],R) :- X =\= H, remove(X,T,[_|H],R).

但我无法理解为什么它不起作用。第一个是列表用完时的基本情况,累加器被复制到R.

第二个定义头部不能等于X,然后递归到尾部并将头部添加到累加器。

1 个答案:

答案 0 :(得分:2)

首先,=\=是算术表达式值的不等式。它不是一般的“不平等”谓词。例如,这不起作用:

?- a =\= b.
ERROR: =\=/2: Arithmetic: `a/0' is not a function

所以你的谓词没有希望为任何不是数字列表或其他算术表达式的东西工作。如果您的Prolog系统有dif/2,请使用:

?- dif(a, b).
true.

仅当上述情况不起作用时,请使用\=进行不平等:

?- a \= b.
true.

(这两者之间的区别在于dif正确处理变量,而\=则没有。)

现在,代码中的一个直接问题是您将列表匹配为[H|T],然后尝试为递归调用构造新列表[_|H]。这会引发一个红色标记:如果H是第一个列表的 head ,那么通常它不会是一个列表本身。但是非列表不应该在列表构造函数中显示为 tail 。例如,如果我们匹配列表[1, 2, 3]

?- [H|T] = [1, 2, 3].
H = 1,
T = [2, 3].

...然后尝试使用此H作为尾部构建一个新列表:

?- [H|T] = [1, 2, 3], Xs = [42 | H].
H = 1,
T = [2, 3],
Xs = [42|1].

...结果不会是一个列表:

?- [H|T] = [1, 2, 3], Xs = [42 | H], is_list(Xs).
false.

您可能希望将H作为新累加器的 head ,如下所示:

remove(X, [H|T], R0, R) :-
    dif(X, H),
    remove(X, T, [H|R0], R).

我从第二个子句改变的另一件事是累加器R0不需要像[]那样,如您的定义。在您的定义中,递归调用将立即失败,因为新的非空累加器不会与[]统一。

这已经适用于某些情况:

?- remove(a, [b, c, d], [], Result).
Result = [d, c, b] ;
false.

但不适合其他人:

?- remove(a, [a], [], Result).
false.

到目前为止,您只有两个子句:一个用于空列表(此处不适用),另一个用于要删除的元素不等于列表头部(不适用)的情况在这里!)。对于列表的头部是要删除的元素的情况,您需要第三个子句:

remove(X, [X|T], R0, R) :-
   ... %  fill in here

编辑:正如评论中所指出的,上面描述的解决方案会反转结果列表中的元素列表。这通常用于基于累加器的递归列表谓词:以后列表元素成为之前的累加器元素。

但是,这里不需要累加器。结果可以直接构造。这是一个实现:

remove(_X, [], []).
remove(X, [Y|Xs], [Y|Ys]) :-
    dif(X, Y),
    remove(X, Xs, Ys).
remove(X, [X|Xs], Ys) :-
    remove(X, Xs, Ys).

这适用于正面和负面的测试用例:

?- remove(a, [b, a, c], Result).
Result = [b, c] ;
false.

?- remove(a, [b, c, d], Result).
Result = [b, c, d] ;
false.

它适用于一般情况:

?- remove(X, Xs, Ys).
Xs = Ys, Ys = [] ;
Xs = Ys, Ys = [_G4794505],
dif(X, _G4794505) ;
Xs = Ys, Ys = [_G4794601, _G4794604],
dif(X, _G4794604),
dif(X, _G4794601) ;
Xs = Ys, Ys = [_G4794697, _G4794700, _G4794703],
dif(X, _G4794703),
dif(X, _G4794700),
dif(X, _G4794697) ;
Xs = Ys, Ys = [_G4794793, _G4794796, _G4794799, _G4794802],
dif(X, _G4794802),
dif(X, _G4794799),
dif(X, _G4794796),
dif(X, _G4794793) ;
Xs = Ys, Ys = [_G4794889, _G4794892, _G4794895, _G4794898, _G4794901],
dif(X, _G4794901),
dif(X, _G4794898),
dif(X, _G4794895),
dif(X, _G4794892),
dif(X, _G4794889) ;
Xs = Ys, Ys = [_G4794985, _G4794988, _G4794991, _G4794994, _G4794997, _G4795000],
dif(X, _G4795000),
dif(X, _G4794997),
dif(X, _G4794994),
dif(X, _G4794991),
dif(X, _G4794988),
dif(X, _G4794985) .

但上面的列举并不公平!它只运用第二个条款,而不是第三个条款。我们可以通过Xs的长度分组来获得公平的枚举:

?- length(Xs, N), remove(X, Xs, Ys).
Xs = Ys, Ys = [],
N = 0 ;
Xs = Ys, Ys = [_G4794582],
N = 1,
dif(X, _G4794582) ;
Xs = [X],
N = 1,
Ys = [] ;
Xs = Ys, Ys = [_G4794678, _G4794681],
N = 2,
dif(X, _G4794678),
dif(X, _G4794681) ;
Xs = [_G4794585, X],
N = 2,
Ys = [_G4794585],
dif(X, _G4794585) ;
Xs = [X, _G4794588],
N = 2,
Ys = [_G4794588],
dif(X, _G4794588) ;
Xs = [X, X],
N = 2,
Ys = [] ;
Xs = Ys, Ys = [_G4794774, _G4794777, _G4794780],
N = 3,
dif(X, _G4794774),
dif(X, _G4794780),
dif(X, _G4794777) .

我们也可以以有限的方式使用“向后”:通过使第二个参数Xs自由并为第三个参数Ys指定具体列表,我们得到一个谓词插入在所有可能的位置将给定元素放入Ys,并提供Xs

?- remove(a, Xs, [b, c, d]).
Xs = [b, c, d] ;
Xs = [b, c, d, a] ;
Xs = [b, c, d, a, a] ;
Xs = [b, c, d, a, a, a] .

这再次不公平,但又可以修复:

?- length(Xs, N), remove(a, Xs, [b, c, d]).
Xs = [b, c, d],
N = 3 ;
Xs = [b, c, d, a],
N = 4 ;
Xs = [b, c, a, d],
N = 4 ;
Xs = [b, a, c, d],
N = 4 ;
Xs = [a, b, c, d],
N = 4 ;
Xs = [b, c, d, a, a],
N = 5 .  % etc.