简体   繁体   English

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

[英]Prolog - How to remove N number of members from a list

So I'm making a predicate called removeN(List1, N, List2).所以我正在创建一个名为 removeN(List1, N, List2) 的谓词。 It should basically function like this:它的基本功能应该是这样的:

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

List2 = [o].

The first argument is a list with a number of the same members ([o, o, o] or [x, x, x]).第一个参数是一个包含许多相同成员的列表([o, o, o] 或 [x, x, x])。 The second argument is the number of members you wanna remove, and the third argument is the list with the removed members.第二个参数是您要删除的成员数,第三个参数是已删除成员的列表。

How should I go about this, I was thinking about using length of some sort.我应该怎么做,我正在考虑使用某种长度。

Thanks in advance.提前致谢。

Another approach would be to use append/3 and length/2 :另一种方法是使用append/3length/2

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

Think about what the predicate should describe.想想谓词应该描述什么。 It's a relation between a list, a number and a list that is either equal to the first or is missing the specified number of the first elements.它是一个列表、一个数字和一个列表之间的关系,该列表要么等于第一个元素,要么缺少指定数量的第一个元素。 Let's pick a descriptive name for it, say list_n_removed/3.让我们为它选择一个描述性名称,比如 list_n_removed/3。 Since you want a number of identical elements to be removed, let's keep the head of the list for comparison reasons, so list_n_removed/3 is just the calling predicate and another predicate with and additional argument, let's call it list_n_removed_head/4, describes the actual relation:既然你想要删除一些相同的元素,为了比较的原因,让我们保留列表的头部,所以 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).

The predicate list_n_removed_head/4 has to deal with two distinct cases: either N=0 , then the first and the third argument are the same list or N>0 , then the head of the first list has to be equal to the reference element (4th argument) and the relation has to hold for the tail as well:谓词 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).

Now let's see how it works.现在让我们看看它是如何工作的。 Your example query yields the desired result:您的示例查询会产生所需的结果:

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

If the first three elements are not equal the predicate fails:如果前三个元素不相等,则谓词失败:

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

If the length of the list equals N the result is the empty list:如果列表的长度等于N则结果是空列表:

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

If the length of the list is smaller than N the predicate fails:如果列表的长度小于N则谓词失败:

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

If N=0 the two lists are identical:如果N=0这两个列表是相同的:

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

If N<0 the predicate fails:如果N<0谓词失败:

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

The predicate can be used in the other direction as well:谓词也可以用于另一个方向:

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

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

However, if the second argument is variable:但是,如果第二个参数是可变的:

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

This can be avoided by using CLP(FD).这可以通过使用 CLP(FD) 来避免。 Consider the following changes:考虑以下更改:

:- 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).

Now the above query delivers the expected result:现在上面的查询提供了预期的结果:

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

As does the most general query:与最一般的查询一样:

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

The other queries above yield the same answers with the CLP(FD) version.上面的其他查询与 CLP(FD) 版本产生相同的答案。

Alternative solution using foldl/4 :使用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, _:[]).

The idea here is to go through the list while tracking index of current element.这里的想法是在跟踪当前元素的索引的同时遍历列表。 While element index is below specified number N we essentially do nothing.虽然元素索引低于指定的数字 N,但我们基本上什么都不做。 After index becomes equal to N, we start building output list by appending all remaining elements from source list.在 index 等于 N 后,我们通过附加源列表中的所有剩余元素来开始构建输出列表。

Not effective, but you still might be interested in the solution, as it demonstrates usage of a very powerful foldl predicate, which can be used to solve wide range of list processing problems.无效,但您可能仍然对该解决方案感兴趣,因为它演示了非常强大的 foldl 谓词的用法,该谓词可用于解决范围广泛的列表处理问题。

Counting down should work fine倒计时应该可以正常工作

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

This works for me.这对我有用。 I think this is the easiest way to do this.我认为这是最简单的方法。 trim(L,N,L2) . trim(L,N,L2) L is the list and N is number of elements. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM