Список опций для списка опций в F # - PullRequest
0 голосов
/ 28 октября 2018

Дан список [Some 1; Некоторые 2; Some 3] Я хотел бы вывод Some [1; 2; 3]. Дан список [Некоторые 1; Нет] должно дать Нет

Я новичок в F # до сих пор, я сделал, что берет две опции и делает опцию списка. Как я могу составить список параметров, чтобы пройти через эту функцию, чтобы получить результат?

let lift a b =
    match a, b with
    | Some av, Some bv  -> Some[av;bv]
    | _, _ -> None

lift (Some 2) None

Ответы [ 3 ]

0 голосов
/ 28 октября 2018

Может быть, немного более идиоматический подход - использование сопоставления с образцом, последовательностей и функция идентификации :

let lift options =
    match options |> Seq.contains None with
    | true -> 
        None
    | false ->
        options
        |> Seq.choose id 
        |> Some
0 голосов
/ 28 октября 2018

В функциональном мире то, что вы описываете, обычно называется sequence.Это концепция переключения порядка общих типов:

  • с List<Option<int>> на Option<List<int>>

Вот общий способ сделать это:

module Option =
    let (>>=) r f = Option.bind f r
    let rtn v     = Some v

    let traverseList f ls = 
        let folder head tail = f head >>= (fun h -> tail >>= (fun t -> h::t |> rtn))
        List.foldBack folder ls (rtn List.empty)
    let sequenceList   ls = traverseList id ls

    // val traverseList : ('a -> 'b option) -> 'a list -> 'b list option
    // val sequenceList : 'a option list -> 'a list option

Здесь sequenceList имеет сигнатуру типа 'a option list -> 'a list option, которая также может быть выражена как List<Option<'a>> -> Option<List<'a>>.Это именно то, что вы хотели.

Но, но, но ... по сравнению с другими ответами этот кажется более сложным.В чем смысл?

Дело в том, что это решение является универсальным и может применяться к любому монадическому (или аппликативному) типу.Например, общий тип, аналогичный Option, равен Result, и решение почти такое же, как и раньше:

module Result =
    let (>>=) r f = Result.bind f r
    let rtn v     = Ok v

    let traverseList f ls = 
        let folder head tail = f head >>= (fun h -> tail >>= (fun t -> h::t |> rtn))
        List.foldBack folder ls (rtn List.empty)
    let sequenceList   ls = traverseList id ls

    // val traverseList : ('a -> Result<'b,'c>) -> 'a list -> Result<'b list,'c>
    // val sequenceList : Result<'a,'b> list -> Result<'a list,'b>

Видите?Помимо строк 2 и 3, остальные одинаковые.

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

Но нет необходимости заново изобретатьрулевое колесо.Вооружившись знаниями о том, как создать sequencetraverse), вы можете просто достать из своей сумки инструменты и вытаскивать ее всякий раз, когда это необходимо для любых монадических или аппликативных типов, будь то Async, Result, Option и т. Д.

Если вы хотите глубже изучить этот веб-сайт, где концепции поясняются наглядной графикой и примерами: https://fsharpforfunandprofit.com/posts/elevated-world-4/

0 голосов
/ 28 октября 2018
let lift l = 
    if List.contains None l then None
    else Some (List.map Option.get l)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...