我有一個列表,我想刪除一個匹配某個條件的元素,但只刪除一個元素。F#只從列表中首次出現
let items = [1;2;3]
let predicate x =
x >= 2
let result = items |> List.fold ...
// result = [1;3]
如何用[1; 3]實現返回列表的方法?
我有一個列表,我想刪除一個匹配某個條件的元素,但只刪除一個元素。F#只從列表中首次出現
let items = [1;2;3]
let predicate x =
x >= 2
let result = items |> List.fold ...
// result = [1;3]
如何用[1; 3]實現返回列表的方法?
您可以使用一個通用的遞歸函數
let rec removeFirst predicate = function
| [] -> []
| h :: t when predicate h -> t
| h :: t -> h :: removeFirst predicate t
或尾遞歸一個(如果你怕一個堆棧溢出)
let removeFirst predicate list =
let rec loop acc = function
| [] -> List.rev acc
| h :: t when predicate h -> (List.rev acc) @ t
| h :: t -> loop (h :: acc) t
loop [] list
let result =
items
|>List.scan (fun (removed, _) item ->
if removed then true, Some(item) //If already removed, just propagate
elif predicate item then true, None //If not removed but predicate matches, don't propagate
else false, Some(item)) //If not removed and predicate doesn't match, propagate
(false, None)
|>List.choose snd
該狀態是一個元組。第一個元素是一個布爾標誌,指示我們是否已經從列表中刪除了某個項目。第二個元素是一個選項:一些是我們想要發射的物品,另外一個是無。
最後一行從狀態中獲取第二個元素,併爲它們中的每一個發出包裝值(在Some的情況下)或者什麼也不做(無的情況下)。
這在我的測試中跑得最慢。 – Soldalma
下面是一個簡短的替代,這在我的測試是快比迄今爲止提出的其他方案要好:
let removeFirst p xs =
match List.tryFindIndex p xs with
| Some i -> List.take i xs @ List.skip (i+1) xs
| None -> xs
針對直觀的解決方案。
let removeAt index list =
let left, right = List.splitAt index list
left @ (List.skip 1 right)
let removeFirst predicate list =
match List.tryFindIndex predicate list with
| Some index -> removeAt index list
| None -> list
對於性能(長列表)。
let removeFirst predicate list =
let rec finish acc rem =
match rem with
| [] -> acc
| x::xs -> finish (x::acc) xs
and find l p acc rem =
match rem with
| [] -> l
| x::xs ->
if p x then finish xs acc
else find l p (x::acc) xs
find list predicate [] list
守衛子句可以很好,但在這種情況下,我認爲他們會讓它更加混亂。我認爲只是'| h :: t - >如果謂詞h那麼(List.rev acc)@t else循環(h :: acc)t'更清楚。 – mydogisbox
這個答案的一個優點是,當達到第一個匹配值時它會停止處理,這樣可以節省大量工作。 – TheQuickBrownFox
在尾遞歸版本中,您可以更改'[]'大小寫以返回輸入'list',而不是反轉累加器,反正只是反轉列表。 – TheQuickBrownFox