2017-09-04 76 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