У меня есть список, и я хочу удалить элемент, соответствующий некоторым критериям, но удалить только один элемент.

let items = [1;2;3]

let predicate x =
    x >= 2

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

Как добиться возврата метода методом с [1; 3]?

5
Krzysztof Morcinek 4 Сен 2017 в 17:08

4 ответа

Лучший ответ

Вы можете использовать универсальную рекурсивную функцию

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
6
gileCAD 4 Сен 2017 в 16:16
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

Государство кортеж. Первый элемент - это логический флаг, указывающий, удалили ли мы уже какой-либо элемент из списка. Второй элемент - это опция: некоторые, когда мы хотим испустить элемент, в противном случае - None.

Последняя строка берет вторые элементы из состояний и для каждого из них выдает обернутое значение (в случае Some) или ничего не делает (в случае None).

3
tearvisus 4 Сен 2017 в 21:02

Стремление к интуитивному решению.

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
0
cadull 6 Сен 2017 в 06:59

Вот короткая альтернатива, которая в моем тестировании была быстрее, чем другие, предложенные до сих пор:

let removeFirst p xs =
    match List.tryFindIndex p xs with
    | Some i -> List.take i xs @ List.skip (i+1) xs
    | None -> xs
2
Soldalma 6 Сен 2017 в 04:52