[英]Prolog - How to remove N number of members from a list
所以我正在创建一个名为 removeN(List1, N, List2) 的谓词。 它的基本功能应该是这样的:
removeN([o, o, o, o], 3, List2).
List2 = [o].
第一个参数是一个包含许多相同成员的列表([o, o, o] 或 [x, x, x])。 第二个参数是您要删除的成员数,第三个参数是已删除成员的列表。
我应该怎么做,我正在考虑使用某种长度。
提前致谢。
另一种方法是使用append/3
和length/2
:
remove_n(List, N, ShorterList) :-
length(Prefix, N),
append(Prefix, ShorterList, List).
想想谓词应该描述什么。 它是一个列表、一个数字和一个列表之间的关系,该列表要么等于第一个元素,要么缺少指定数量的第一个元素。 让我们为它选择一个描述性名称,比如 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
,则第一个列表的头部必须等于引用元素(第 4 个参数)并且该关系也必须适用于尾部:
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) 版本产生相同的答案。
使用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,但我们基本上什么都不做。 在 index 等于 N 后,我们通过附加源列表中的所有剩余元素来开始构建输出列表。
无效,但您可能仍然对该解决方案感兴趣,因为它演示了非常强大的 foldl 谓词的用法,该谓词可用于解决范围广泛的列表处理问题。
倒计时应该可以正常工作
removeN([],K,[]) :- K>=0.
removeN(X,0,X).
removeN([_|R],K,Y) :- K2 is K-1, removeN(R,K2,Y).
这对我有用。 我认为这是最简单的方法。 trim(L,N,L2)
。 L
是列表, N
是元素的数量。
trim(_,0,[]).
trim([H|T],N,[H|T1]):-N1 is N-1,trim(T,N1,T1).
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.