F#仅从列表中筛选出第一个匹配项

时间:2017-09-04 14:08:22

标签: filter f# fold

我有一个列表,我想删除符合某些条件的元素,但只删除一个元素。

let items = [1;2;3]

let predicate x =
    x >= 2

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

如何使用[1; 3]实现方法返回列表?

4 个答案:

答案 0 :(得分: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

答案 1 :(得分: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)或什么都不执行(如果是None)。

答案 2 :(得分:2)

这是一个简短的替代方案,在我的测试中比目前提出的其他方案更快:

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

答案 3 :(得分: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