2017-09-04 102 views
5

我有一個列表,我想刪除一個匹配某個條件的元素,但只刪除一個元素。F#只從列表中首次出現

let items = [1;2;3] 

let predicate x = 
    x >= 2 

let result = items |> List.fold ... 
// result = [1;3] 

如何用[1; 3]實現返回列表的方法?

回答

6

您可以使用一個通用的遞歸函數

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 
+0

守衛子句可以很好,但在這種情況下,我認爲他們會讓它更加混亂。我認爲只是'| h :: t - >如果謂詞h那麼(List.rev acc)@t else循環(h :: acc)t'更清楚。 – mydogisbox

+0

這個答案的一個優點是,當達到第一個匹配值時它會停止處理,這樣可以節省大量工作。 – TheQuickBrownFox

+0

在尾遞歸版本中,您可以更改'[]'大小寫以返回輸入'list',而不是反轉累加器,反正只是反轉列表。 – TheQuickBrownFox

3
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的情況下)或者什麼也不做(無的情況下)。

+0

這在我的測試中跑得最慢。 – Soldalma

2

下面是一個簡短的替代,這在我的測試是快比迄今爲止提出的其他方案要好:

let removeFirst p xs = 
    match List.tryFindIndex p xs with 
    | Some i -> List.take i xs @ List.skip (i+1) xs 
    | None -> xs 
0

針對直觀的解決方案。

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