[英]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.