breakOut – ein kleiner Durchbruch


Heute ist mir ein nützliches Feature der Scala-Collections untergekommen: breakOut.

Angenommen, ich habe eine Liste von Paaren, und will sie eine Map umwandeln. „Früher“ (in Scala 2.7) gab es verschiedene Tricks dafür, z.B.

val list = List(1,2,3,4,5) zip List("one","two","three","four","five")
println(list)
//--> List((1,one), (2,two), (3,three), (4,four), (5,five))

val map0 = Map[Int, String]() ++ list
println(map0)
//--> Map(5 -> five, 1 -> one, 2 -> two, 3 -> three, 4 -> four)

Abgesehen davon, dass Listen jetzt eine toMap-Methode haben, gibt es auch die Möglichkeit, breakOut zu benutzen:

...
import collection._

val map1:Map[Int,String] = list.map(identity)(breakOut)
println(map1)
//--> Map(5 -> five, 1 -> one, 2 -> two, 3 -> three, 4 -> four)

Wie funktionert das? Schauen wir uns die Typ-Signatur von List.map an. Davon gibt es zwei Versionen, und natürlich ist „unsere“ die mit den zwei Argumentlisten:

def map [B, That] (f: (A) ⇒ B)(implicit bf: CanBuildFrom[List[A], B, That]) : That 

Aha! In der zweiten Argumentliste wird normalerweise stillschweigend und implizit ein CanBuildFrom übergeben. Diese Objekte bilden sozusagen das Rückgrat für alle Konvertierungen der Collection-Bibliothek. Anstelle dieses „normalen“ CanBuildFrom-Objekts liefern wir nun explizit eine spezielle Version, die von der Methode breakOut (die im package object collection in package.scala definiert ist).

Der breakOut-Trick funktionert nicht nur mit map, sonder bei allen Methoden, die eine zweite „CanBuildFrom-Argumentliste“ haben, z.B. bei zip:

...
val map2:Map[Int,String] = (List(1,2,3,4,5) zip List("one","two","three","four","five"))(breakOut)
println(map2)
//--> Map(5 -> five, 1 -> one, 2 -> two, 3 -> three, 4 -> four)

Eine andere nützliche Anwendung ist die Konvertierung zwischen veränderlichen und unveränderlichen Collections:

...
val map3: collection.mutable.Map[Int,String] = map2.map(identity)(breakOut)
println(map3)
//--> Map(2 -> two, 4 -> four, 1 -> one, 3 -> three, 5 -> five)

Also falls einmal irgendwo mittendrin in einer Berechnung der Typ nicht passen sollte, hat man gute Chancen, dass ein angehängtes (breakOut) das Problem kuriert.

Wer noch mehr Details wissen will: Auf Stackoverflow gibt es eine sehr ausführlichere Antwort zu diesem Thema.

Advertisements

Ein Gedanke zu “breakOut – ein kleiner Durchbruch

  1. Sehr nett heute morgen den Post gelesen und ein paar Minuten später direkt anwenden können. Wie bereits erwähnt können ja mit toX in 2.8 schon viele Konvertierungen durchgeführt werden. Folgendes klappte aber nur mit deinem Hinweis:

    val pl:LinkedHashSet[Int] = List(1,2,3).map(p => 2*p)(breakOut) // klappt
    val pl:LinkedHashSet[Int] = List(1,2,3).map(p => 2*p).toSet // klappt nicht

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s