2016-06-27 15 views
2

Ich habe gesehen, wie man verschiedene Möglichkeiten in Scala benutzt, um einen Wert aus einem Wrapper wie Option oder einer Liste oder anderen Sammlungen zu ziehen. Wenn ich eine List[Int] aus einer Option ziehen und dann darauf iterieren muss, kann dies in einem für Block getan werden?Wie zwei Arten des Verständnisses in Scala zusammenführen?

z.

val l: Option[List[Int]] = Some(List(1,2,3,4)) 
l: Option[List[Int]] = Some(List(1, 2, 3, 4)) 
for{ 
    li <- l  // li taken out from Option wrapper 
    number <- li // numbers pulled from li 
} yield number*2 
cmd7.scala:3: type mismatch; 
found : List[Int] 
required: Option[?] 
number <- li 
    ^

Wenn ich das richtig verstanden, will es jeder Eintrag ein Option sein. Gibt es eine Möglichkeit, diesen Effekt ohne zwei for-Schleifen zu erreichen?

Antwort

3

Gibt es eine Möglichkeit, diesen Effekt ohne zwei for-Schleifen zu erreichen?

Sie können dies tun, indem toList auf den Option[List[Int]] Aufruf, es in ein List[List[Int]] drehen, die das for Begreifen flatMap über:

for { 
    | o <- l.toList 
    | num <- o 
    | } yield num * 2 
res8: List[Int] = List(2, 4, 6, 8) 

Dies wird eine leere Liste für den None Typen ergeben:

scala> val l: Option[List[Int]] = None 
scala> for { 
    | o <- l.toList 
    | num <- o 
    | } yield num * 2 
res3: List[Int] = List() 

Sie können auch Option[T].getOrElse mit einem leeren List[T] verwenden wenn die Option als Rückfall leer ist:

scala> for { 
    | o <- l.getOrElse(List()) 
    | } yield o * 2 
res13: List[Int] = List(2, 4, 6, 8) 

Persönlich mag ich den expliziten map Aufruf:

scala> l.map(_.map(_ * 2)) 
res7: Option[List[Int]] = Some(List(2, 4, 6, 8)) 
2

Für Comprehensions flatMap s erweitern, map s und faul Version von filter. flatMap für Option akzeptiert keine Funktion A => List[A]. Im Allgemeinen können Sie nur eine Art von Monade zum Verständnis verwenden.

Offensichtliche Lösung hier sind Monade Transformatoren. Wenn Sie zwei Monaden gleichzeitig haben, können Sie auf die Werte in der tieferen Monade zugreifen, indem Sie eine andere Monade erstellen, die das Verhalten von zwei kombiniert.

Beachten Sie, dass dies für zwei beliebige Monaden funktioniert, nicht nur Option und List.

Mit scalaz:

import scalaz._ 
import Scalaz._ 

val list: Option[List[Int]] = Some(List(1, 2, 3, 4)) 

(for { 
    number <- ListT(list) 
} yield number * 2).run 

res0: Option[List[Int]] = Some(List(2, 4, 6, 8)) 

Sie müssen run anrufen, um den Wert von Transformator auszupacken.

Sie können dann mehr als eine Liste wie folgt verwenden:

val xs = List(1, 2, 3).some 
val ys = List(0, 2).some 

(for { 
    x <- ListT(xs) 
    y <- ListT(ys) 
} yield x * y).run 

res0: Option[List[Int]] = Some(List(0, 2, 0, 4, 0, 6)) 

Wenn eine OptionNone wäre das Ergebnis wäre None auch

(for { 
    x <- ListT(xs) 
    y <- ListT(ys) 
    z <- ListT(none[List[Int]]) 
} yield x * y).run 

res0: Option[List[Int]] = None