Prolog - 如何从列表中删除N个成员

时间:2017-10-10 21:10:06

标签: list prolog

所以我做了一个名为removeN(List1,N,List2)的谓词。它基本上应该是这样的:

removeN([o, o, o, o], 3, List2).

List2 = [o].

第一个参数是一个列表,其中包含许多相同的成员([o,o,o]或[x,x,x])。第二个参数是您要删除的成员数,第三个参数是包含已删除成员的列表。

我应该怎么做,我正在考虑使用某种长度。

提前致谢。

5 个答案:

答案 0 :(得分:3)

考虑谓词应该描述的内容。它是列表,数字和列表之间的关系,它与第一个元素相等或者缺少指定数量的第一个元素。让我们为它选择一个描述性名称,比如list_n_removed / 3。由于你想要删除许多相同的元素,为了比较的原因,让我们保持列表的头部,所以list_n_removed / 3只是调用谓词和另一个带有和附加参数的谓词,让我们调用它list_n_removed_head / 4,描述了实际的关系:

list_n_removed([X|Xs],N,R) :-
   list_n_removed_head([X|Xs],N,R,X).

谓词list_n_removed_head / 4必须处理两个不同的情况:N=0,然后第一个和第三个参数是相同的列表或N>0,那么第一个列表的头部必须等于参考元素(第四个参数),并且关系也必须保持尾部:

list_n_removed_head(L,0,L,_X).
list_n_removed_head([X|Xs],N,R,X) :-
   N>0,
   N0 is N-1,
   list_n_removed_head(Xs,N0,R,X).

现在让我们看看它是如何运作的。您的示例查询会产生所需的结果:

?- list_n_removed([o,o,o,o],3,R).
R = [o] ;
false.

如果前三个元素不相等,则谓词失败:

?- list_n_removed([o,b,o,o],3,R).
false.

如果列表的长度等于N,则结果为空列表:

?- list_n_removed([o,o,o],3,R).
R = [].

如果列表的长度小于N,则谓词失败:

?- list_n_removed([o,o],3,R).
false.

如果N=0两个列表相同:

?- list_n_removed([o,o,o,o],0,R).
R = [o, o, o, o] ;
false.

如果N<0谓词失败:

?- list_n_removed([o,o,o,o],-1,R).
false.

谓词也可用于其他方向:

?- list_n_removed(L,0,[o]).
L = [o] ;
false.

?- list_n_removed(L,3,[o]).
L = [_G275, _G275, _G275, o] ;
false.

但是,如果第二个参数是可变的:

?- list_n_removed([o,o,o,o],N,[o]).
ERROR: >/2: Arguments are not sufficiently instantiated

使用CLP(FD)可以避免这种情况。请考虑以下更改:

:- use_module(library(clpfd)).              % <- new

list_n_removed([X|Xs],N,R) :-
   list_n_removed_head([X|Xs],N,R,X).

list_n_removed_head(L,0,L,_X).
list_n_removed_head([X|Xs],N,R,X) :-
   N #> 0,                                  % <- change
   N0 #= N-1,                               % <- change
   list_n_removed_head(Xs,N0,R,X).

现在上面的查询提供了预期的结果:

?- list_n_removed([o,o,o,o],N,[o]).
N = 3 ;
false.

与最常见的查询一样:

?- list_n_removed(L,N,R).
L = R, R = [_G653|_G654],
N = 0 ;
L = [_G653|R],
N = 1 ;
L = [_G26, _G26|R],
N = 2 ;
L = [_G26, _G26, _G26|R],
N = 3 ;
.
.
.

上述其他查询与CLP(FD)版本产生相同的答案。

答案 1 :(得分:2)

另一种方法是使用append/3length/2

remove_n(List, N, ShorterList) :-
    length(Prefix, N),
    append(Prefix, ShorterList, List).

答案 2 :(得分:2)

使用foldl/4的替代解决方案:

remove_step(N, _Item, Idx:Tail, IdxPlusOne:Tail) :-
    Idx < N, succ(Idx, IdxPlusOne).
remove_step(N, Item, Idx:Tail, IdxPlusOne:NewTail) :-
    Idx >= N, succ(Idx, IdxPlusOne),
    Tail = [Item|NewTail].

remove_n(List1, N, List2) :-
    foldl(remove_step(N), List1, 0:List2, _:[]).

这里的想法是在跟踪当前元素的索引时浏览列表。虽然元素索引低于指定的数N,但我们基本上什么都不做。在索引变为N之后,我们通过附加源列表中的所有剩余元素来开始构建输出列表。

无效,但您仍然可能对该解决方案感兴趣,因为它演示了一个非常强大的foldl谓词的用法,它可以用来解决各种列表处理问题。

答案 3 :(得分:1)

倒计时应该可以正常工作

removeN([],K,[]) :- K>=0.
removeN(X,0,X).
removeN([_|R],K,Y) :- K2 is K-1, removeN(R,K2,Y).

答案 4 :(得分:0)

这对我有用。 我认为这是最简单的方法。 trim(L,N,L2)L是列表,N是元素数。

trim(_,0,[]).
trim([H|T],N,[H|T1]):-N1 is N-1,trim(T,N1,T1).
相关问题