2016-03-05 25 views
11

Dato un elenco [Some 1; Some 2; Some 3] Desidero un'uscita Some 6. Dato un elenco [Some 1; None] dovrebbe produrre None.Lista di opzioni a scomparsa

Ma lo trovo un po 'più difficile di quanto avessi immaginato di ottenere in modo pulito.

il meglio che potevo venire con era presente

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

Questa è una traversa, se si avesse 'sequenzaA' sarebbe' sequenzaA [Alcuni 1; Alcuni 2; Alcuni 3] |> Option.map List.sum' cercano il codice per sequenceA, FsControl lo ha. – Gustavo

+0

@Gustavo;) dovresti fare una risposta - anche se penso che 'FsControl' sia un po 'pesante per il * common * F # er ...' pure' Haskell invidia se vuoi ^^ – Carsten

+0

Sì @Carsten, I d'accordo è troppo pesante solo per questo codice, è per questo che ho fatto come un commento. È una soluzione a un solo liner, potrebbe essere generalizzata e ulteriormente ridotta a 'sequenceA [Some 1; Alcuni 2; Alcuni 3] | >> somma'. – Gustavo

risposta

6

si può realizzare questo definendo una funzione map2 per i valori di opzione:

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

Ciò consentirebbe di scrivere la funzione che si desidera:

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

Esempio:

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

Al momento, la funzione optionMap2 non è disponibile nella libreria F # core, ma probably will be part of the Option module in the future.

+0

questa è più o meno la risposta di Functional_S però - ha appena chiamato 'lift' – Carsten

+0

@Carsten Strange, quella risposta non era visibile un paio di minuti fa ... –

+0

sì - ho prima cancellato un'altra risposta poi lo ha modificato e lo ha annullato – 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 

con piega

[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

Questo funziona solo supponendo che la lista contenga voci che sono '' Alcuni '' tuttavia. Non se si hanno voci '' None'' a quel punto la riduzione dovrebbe essere '' None''. –

+0

ok, questa è una specifica aggiuntiva ;-) Non ti viene detto cosa vuoi esattamente, hai dato solo un caso campione. –

+0

Questo uccide la mia risposta, quindi prova ad eliminarlo. –

3

Ecco un'implementazione ingenua del sequence Gustavo parlato:

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

(per favore notare che questo non è ricorsiva in coda e non ottimizzato per niente così si dovrebbe trasformarlo, se hai intenzione di bisogno per grandi liste)

che avrebbe funzionato altrettanto Gustavo ti ha detto:

> 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