2016-03-05 6 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 ...' pure' Haskell зависть, если вы будете ^^ – Carsten

+0

Да @Carsten, I Согласитесь, слишком тяжело для этого кода, поэтому я сделал это как комментарий. Это однолинейное решение, оно может быть обобщено и далее сокращено до 'sequenceA [Some 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

Это работает только при условии, что в списке содержатся записи, которые являются '' Некоторые '' однако. Нет, если у вас есть записи, которые являются «Нет», и в этот момент сокращение должно быть «Нет». –

+0

ОК, это дополнительная спецификация ;-) Вы не сказали, что именно хотите, вы дали только один пример. –

+0

Это убивает мой ответ, поэтому попробуйте удалить его. –

3

Вот это наивная реализация sequence Густаво говорил о:

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