2010-04-26 17 views
26

Quiero poder aplicar una operación f: (T,T) => T a Option[T] valores en Scala. Quiero que el resultado sea None si alguno de los dos valores es None.Cómo combinar valores de opción en Scala?

Más específicamente, quiero saber si hay una manera más corta de hacer lo siguiente:

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 
    } 
} 

He tryied (x zip y) map {case (u,v) => f(u,v)} pero el resultado es un Iterator[T] no un Option[T].

Cualquier ayuda será apreciada. Gracias.

Respuesta

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, esto debería actualizarse correctamente http://docs.scala-lang.org/style/control-structures.html como debería ser para {x0 <- x; y0 <- y)}, creo, ¿verdad? –

+0

@DeanHiller, hmm, sí. La guía de estilo no existía en el momento en que pienso. :) – missingfaktor

17

respuesta @RahulG 's explota el hecho de que Option es una mónada (a pesar de que no hay ningún tipo de representar esto en la biblioteca Scala). El compilador expande la comprensión for a lo siguiente:

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

También puede tratarlo como un funtor aplicativo, con la ayuda de Scalaz:

import scalaz._ 
import Scalaz._ 

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

Una diferencia clave es que en el cálculo monádico, una falla (es decir, None) del cálculo a cortocircuita la evaluación. En el estilo aplicativo, se evalúan a y b, y si ambos son Some s, se llama a la función pura. También puede ver que en el cálculo monádico, el valor aa podría haberse utilizado en el cálculo b; en la versión aplicativa, b no puede depender del resultado de a.

+0

¿Es el equivalente ASCII '<|*|>' para este método? –

+1

'<*>' le permite proporcionar la función 'pura', en este caso '(a, b) => a + b'. '<|*|>' es una conveniencia para usar 'Tuple2.apply' como la función pura. '⊛' es en realidad un poco más general que arity-2, podrías escribir' (a ⊛ b a ⊛ b) {_ + _ + _ + _} '. Es un poco experimental, y como tal, todavía no tiene un alias ASCII. – retronym

+0

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

3

tengo una versión ligeramente mayor de scalaz que retronym pero las siguientes obras para mí como un ejemplo y es generalizable para el caso en el que usted tiene 3 tipos T, U, V y no sólo uno:

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]) 
} 

a continuación, puedo añadir:

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

Reemplace' <|*|> 'con' <*> 'para evitar la creación de la tupla temporal, y use' f' directamente. – retronym

+0

No funciona con diferentes tipos paramétricos, creo que –

+0

Oops, quise decir '<**>' – retronym

1

Se puede utilizar para comprensiones:

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)) 

que es el azúcar para:

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

Esto también es posible debido a la opción de ser un Mónada

+3

Eso es raro. No vi la respuesta de @ RahulG cuando publiqué esto. – 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) no resultar en un Iterable [(A, B)] (con, porque es de Opciones, como máximo un elemento). headOption luego devuelve el primer elemento como una Opción.

Cuestiones relacionadas