折叠选项列表

时间:2016-03-05 10:09:20

标签: f# optional fold

给定一个列表[Some 1; Some 2; Some 3]我希望输出Some 6。如果列表[Some 1; None]应该产生None

但是我发现它比我想象的要干净得多。

我能想到的最好的就是这个

let someNums = [Some 1; Some 2; Some 3]
someNums 
|> List.reduce (fun st v ->
  Option.bind (fun x ->
    Option.map (fun y -> x + y) st) v )

3 个答案:

答案 0 :(得分:10)

let lift op a b =
    match a, b with
    | Some av, Some bv  -> Some(op av bv)
    | _, _ -> None

let plus = lift (+)

[Some 1; Some 2; Some 3]
|> List.reduce plus
// val it : int option = Some 6


[Some 1; None]
|> List.reduce plus
// val it : int option = None

带折叠

[Some 1; None]
|> List.fold plus (Some 0)
// val it : int option = None

[Some 1; Some 2; Some 3]
|> List.fold plus (Some 0)
// val it : int option = Some 6

[Some 1; None; Some 2] 
|> List.fold plus (Some 0)
// val it : int option = None

答案 1 :(得分:6)

您可以通过为选项值定义map2函数来完成此操作:

let optionMap2 f x y =
    match x, y with
    | (Some x', Some y') -> Some (f x' y')
    | _ -> None

这将使您能够编写所需的功能:

let sumSome = List.fold (optionMap2 (+)) (Some 0)

示例:

> [Some 1; Some 2; Some 3] |> sumSome;;
val it : int option = Some 6
> [Some 1; None; Some 3] |> sumSome;;
val it : int option = None

目前,optionMap2功能在F#核心库中不可用,但是probably will be part of the Option module in the future

答案 2 :(得分:3)

以下是sequence Gustavo谈到的一个天真的实现:

let rec sequence = 
   function 
   | [] -> Some [] 
   | (Some o :: os) -> 
      sequence os 
      |> Option.map (fun os' -> o::os') 
   | _ -> None

(请注意,这不是尾递归的,根本没有优化,因此如果您需要大型列表,您应该对其进行转换)

这就像古斯塔沃告诉你的那样:

> sequence [Some 1; Some 2; Some 2] |> Option.map List.sum;;
val it : int option = Some 5

> sequence [Some 1; None; Some 2] |> Option.map List.sum;;
val it : int option = None