2010-04-26 13 views
26

Ich möchte eine Operation f: (T,T) => T zu Option[T] Werte in Scala anwenden können. Ich möchte, dass das Ergebnis None ist, wenn einer der beiden Werte None ist.Wie kombiniere ich Optionswerte in Scala?

Genauer gesagt, möchte ich, wenn wissen, gibt es einen kürzeren Weg folgendes zu tun:

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

ich (x zip y) map {case (u,v) => f(u,v)} haben tryied aber das Ergebnis ist ein Iterator[T] kein Option[T].

Jede Hilfe wird geschätzt. Vielen Dank.

Antwort

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, dies sollte richtig aktualisiert werden http://docs.scala-lang.org/style/control-structures.html wie es sein sollte für {x0 <- x; y0 <- y)}, denke ich, oder? –

+0

@DeanHiller, hmm ja. Styleguide gab es zu der Zeit, als ich denke, nicht. :) – missingfaktor

17

@RahulG ‚s Antwort nutzt die Tatsache aus, dass Option ist eine Monade (obwohl es keine Art ist dies in der Scala Bibliothek darzustellen). Der Compiler erweitert das for Verständnis auf die folgenden:

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

Sie können es auch als applicative Funktors behandeln, mit etwas Hilfe von Scalaz:

import scalaz._ 
import Scalaz._ 

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

Ein wesentlicher Unterschied besteht darin, dass in der monadischen Berechnung, Ein Fehler (None) der Berechnung a schließt die Auswertung kurz. Im anwendungsspezifischen Stil werden sowohl a als auch b ausgewertet, und wenn beide Some s sind, wird die reine Funktion aufgerufen. Sie können auch sehen, dass in der monadischen Berechnung der Wert aa in der Berechnung b hätte verwendet werden können; in der applikativen Version kann b nicht von dem Ergebnis a abhängen.

+0

Ist das ASCII-Äquivalent "<|*|>" für diese Methode? –

+1

'<*>' ermöglicht Ihnen, die 'reine' Funktion zu liefern, in diesem Fall '(a, b) => a + b'. '<|*|>' ist eine Annehmlichkeit für 'Tuple2.apply' als reine Funktion. '⊛' ist eigentlich etwas allgemeiner als arity-2, man könnte schreiben '(a ⊛ b a ⊛ b) {_ + _ + _ + _}'. Es ist ein wenig experimentell und hat als solches noch keinen ASCII-Alias. – retronym

+0

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

3

Ich habe eine etwas ältere Version von scalaz als retronym aber die folgenden Werke für mich als Beispiel und ist verallgemeinerbar für den Fall, dass Sie drei Typen haben T, U, V und nicht nur ein:

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

dann kann ich hinzufügen:

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

Ersetzen Sie' <|*|> 'durch' <*> ', um die Erstellung des temporären Tupels zu vermeiden, und verwenden Sie' f' direkt. – retronym

+0

Funktioniert nicht mit verschiedenen parametrischen Typen, denke ich –

+0

Hoppla, ich meinte '<**>' – retronym

1

können Sie für Comprehensions verwenden:

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

der Zucker ist für:

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

Dies auch aufgrund der Option möglich ist, einen Monad sind

+3

Das ist komisch. Ich habe @ RahulGs Antwort nicht gesehen, als ich das gepostet habe. – 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) in einem Iterable führt [(A, B)] (mit, weil es von Optionen ist, höchstens ein Element). headOption gibt dann das erste Element als Option zurück.

Verwandte Themen