2016-03-05 69 views
11

给定一个列表[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

这是一个遍历,如果你有'sequenceA',它将是'sequenceA [Some 1;一些2;一些3] |> Option.map List.sum'查找sequenceA的代码,FsControl拥有它。 – Gustavo

+0

@Gustavo;)你应该让这个答案 - 尽管我认为'FsControl'对于* common#F#er有点重要......'纯粹'Haskell嫉妒如果你愿意^^ – Carsten

+0

是@Carsten,I对于这段代码,同意过于沉重,这就是我作为评论的原因。这是一种单线性解决方案,它可以被推广并进一步简化为'sequenceA [大约1;一些2;一些3] | >> sum'。 – Gustavo

回答

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

+0

这或多或少是Functional_S的答案 - 他只是称之为'lift' – Carsten

+0

@Carsten奇怪的是,这个答案几分钟前不可见... –

+0

是的 - 我第一次删除了另一个答案然后编辑它并取消删除它 – Carsten

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 
+0

但是,这只适用于假设列表包含“'一些”'的条目。如果你的条目是“None”,那么这个缩减​​应该是“None”。 –

+0

好的,这是一个额外的规范;-)你没有告诉你真正想要什么,你只给了一个样例。 –

+0

杀死我的答案,所以尽量将其删除。 –

3

这里有一个天真的实现sequence古斯塔沃谈到:

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

(please请注意,这并不是尾递归,而不是优化可言,所以你应该改变它,如果你会需要它的大名单)

为古斯塔沃告诉你,这将只是工作:

> 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