2010-04-26 13 views
26

Desidero poter applicare un'operazione con valori f: (T,T) => T a Option[T] in Scala. Voglio che il risultato sia None se uno qualsiasi dei due valori è None.Come combinare i valori delle opzioni in Scala?

Più in particolare, voglio sapere se c'è un modo più breve per effettuare le seguenti operazioni:

def opt_apply[T](f: (T,T) => V, x: Option[T], y: Option[T]): Option[T] = { 
    (x,y) match { 
    case (Some(u),Some(v)) => Some(f(u,v)) 
    case _ => None 
    } 
} 

ho tryied (x zip y) map {case (u,v) => f(u,v)} ma il risultato è un non Iterator[T] un Option[T].

Qualsiasi aiuto sarà apprezzato. Grazie.

risposta

28
scala> val (x, y) = (Some(4), Some(9)) 
x: Some[Int] = Some(4) 
y: Some[Int] = Some(9) 

scala> def f(x: Int, y: Int) = Math.max(x, y) 
f: (x: Int,y: Int)Int 

scala> for { x0 <- x; y0 <- y } yield f(x0, y0) 
res26: Option[Int] = Some(9) 

scala> val x = None 
x: None.type = None 

scala> for { x0 <- x; y0 <- y } yield f(x0, y0) 
res27: Option[Int] = None 
+0

errrr, questo dovrebbe essere aggiornato a destra http://docs.scala-lang.org/style/control-structures.html come dovrebbe essere per {x0 <- x; y0 <- y)}, penso, giusto? –

+0

@DeanHiller, hmm si. La guida allo stile non esisteva al momento penso. :) – missingfaktor

17

risposta @RahulG s' sfrutta il fatto che Option è una monade (anche se non v'è alcun tipo di rappresentare questo nella libreria Scala). Il compilatore espande la for comprensione al seguente:

def a: Option[Int] 
def b: Option[Int] 
val calc: Option[Int] = a flatMap {aa => b map {bb => aa + bb}} 

È inoltre possibile trattarlo come un funtore applicativa, con qualche aiuto da Scalaz:

import scalaz._ 
import Scalaz._ 

def a: Option[Int] 
def b: Option[Int] 
val calc: Option[Int] = (a ⊛ b) {_ + _} 

Una differenza chiave è che nel calcolo della monade, un errore (ovvero None) del calcolo a cortocircuiti della valutazione. Nello stile applicativo, vengono valutati sia a sia b e se entrambi sono Some s, viene chiamata la funzione pura. È inoltre possibile vedere che nel calcolo monadico, il valore aa avrebbe potuto essere utilizzato nel calcolo b; nella versione applicativa, b non può dipendere dal risultato di a.

+0

L'equivalente ASCII '<|*|>' per questo metodo? –

+1

'<*>' consente di fornire la funzione 'pura', in questo caso '(a, b) => a + b'. '<|*|>' è una comodità per usare 'Tuple2.apply' come funzione pura. '⊛' è in realtà un po 'più generale di arity-2, potresti scrivere' (a ⊛ b a ⊛ b) {_ + _ + _ + _} '. È un po 'sperimentale e, come tale, non ha ancora un alias ASCII. – retronym

+0

Typo, intendevo: '(a ⊛ b ⊛ a ⊛ b) {_ + _ + _ + _}' – retronym

3

Ho una versione leggermente più vecchio di scalaz di retronym ma i seguenti lavori per me come un esempio ed è generalizzabile per il caso in cui si dispone di 3 tipi T, U, V e non uno solo:

def main(args: Array[String]) { 
    import scalaz._ 
    import Scalaz._ 

    val opt1 = some(4.0) //Option[Double] 
    val opt2 = some(3) //Option[Int] 

    val f: (Double, Int) => String = (d, i) => "[%d and %.2f]".format(i, d) 

    val res = (opt1 <|*|> opt2).map(f.tupled) 
    println(res) //Some([3 and 4.00]) 
} 

posso quindi aggiungere:

val opt3 = none[Int] 
val res2 = (opt1 <|*|> opt3).map(f.tupled) 
println(res2) //None 
+1

Sostituisci' <|*|> 'con' <*> 'per evitare la creazione della tupla temporanea e usare' f' direttamente. – retronym

+0

Non funziona con tipi parametrici diversi, penso che sia –

+0

Oops, intendevo "<**>" – retronym

1

È possibile utilizzare espressioni for:

def opt_apply[T](f: (T,T) => T, x: Option[T], y: Option[T]): Option[T] = 
    for (xp <- x; yp <- y) yield (f(xp,yp)) 

Quale è lo zucchero per:

x flatMap {xp => y map {yp => f(xp, yp)}} 

Ciò è possibile anche grazie alla opzione di essere un Monade

+3

Questo è strano. Non ho visto la risposta di @ RahulG quando ho postato questo. – user142435

0
def optApply[A,B,C](f: (A, B) => C, a: Option[A], b: Option[B]): Option[C] = 
    a.zip(b).headOption.map { tup => f.tupled(tup) } 

a.zip(b) non risultare in un Iterable [(A, B)] (con, perché proviene da Opzioni, al massimo un elemento). headOption quindi restituisce il primo elemento come un'opzione.

Problemi correlati