Gezippt und zugenäht


In diesem Blog kommen regelmäßig Tupel vor, und normalerweise wird um diese einfach zu verstehenden, nützlichen Klassen nicht allzuviel Aufhebens gemacht. Heute will ich einmal näher beleuchten, wie Tupel und die Scala-Collections so zusammenpassen – nämlich prächtig.

Zunächst wären da Methoden, die eine Collection in zwei Teile zerlegen:

List(1,2,3,4,5).partition(_ % 3 == 1)
res6: (List[Int], List[Int]) = (List(1, 4),List(2, 3, 5))

List(1,2,3,4,5).span(_ % 3 == 1)
//--> res1: (List[Int], List[Int]) = (List(1),List(2, 3, 4, 5))

List(1,2,3,4,5).splitAt(2)
//--> res2: (List[Int], List[Int]) = (List(1, 2),List(3, 4, 5))

Die Methode partition teilt die Collection nach einem gegeben Kriterium auf, während span nur solange Werte in die erste Liste packt, bis das Kriterium das erste mal falsch wird. Die Methode splitAt teilt die Collection an einem gegebenen Index. Natürlich kann man das Tupel schon bei der Zuweisung gleich wieder aufdröseln:

val (rest1,rest02) = List(1,2,3,4,5).partition(_ % 3 == 1)
//--> rest1: List[Int] = List(1, 4)
//--> rest02: List[Int] = List(2, 3, 5)

Nützlich sind auch die zip-Methoden:

List(1,2,3,4) zip List("A","B","C")
//--> res7: List[(Int, java.lang.String)] = List((1,A), (2,B), (3,C))

List("null","eins","zwei","drei").zipWithIndex
//--> res8: List[(java.lang.String, Int)] = List((null,0), (eins,1), (zwei,2), (drei,3))

List((1,"A"), (2,"B"), (3,"C")) unzip
res9: (List[Int], List[java.lang.String]) = (List(1, 2, 3),List(A, B, C))

Die Methode zip packt zwei Collections paarweise zusammen. Aber Achtung: Wie man im obigen Beispiel sieht, werden überzählige Elemente (hier die 4) stillschweigend weggeworfen.
In Java hat man oft das Problem, dass man statt der erweiterten for-Schleife doch wieder zur altgewohnten Schreibweise zurückkehren muss, weil man einen Index braucht. Scalas Antwort auf dieses Problem ist so simpel wie elegant: zipWithIndex packt die benötigten Indizes einfach zu den Elementen dazu.
Eine Liste aus Paaren bekommt man mit unzip wieder getrennt, aus einer Liste mit Paaren wird also ein Listen-Paar.

Stellt sich zu guter Letzt die Frage nach dem elegantesten Zugriff auf so eine gepaarte Liste. Natürlich kann man einfach das Tupel nehmen und mit den Unterstrich-Methoden auf dessen Elemente zugreifen:

List("null","eins","zwei","drei").zipWithIndex.map(t => t._1 + "=" + t._2)
//--> res10: List[java.lang.String] = List(null=0, eins=1, zwei=2, drei=3)

Eleganter ist die Verwendung von case, das allerdings geschweifte Klammern erfordert:

List("null","eins","zwei","drei").zipWithIndex.map{case(s, i) => s + "=" +  i}
//--> res11: List[java.lang.String] = List(null=0, eins=1, zwei=2, drei=3)

Will man mit den beiden Elementen des Tupels eine Funktion aufrufen, kann man sich sogar das Zerlegen sparen – man wandelt stattdessen die zweielementige Funktion in eine Funktion um, die ein Tupel als Argument nimmt. Berechnen wir einmal so die Hypothenuse von ein paar rechtwinkligen Dreiecken:

 
List((3.0,4.0),(5.0,12.0)).map(math.hypot _ tupled)
//--> res14: List[Double] = List(5.0, 13.0)

Die Methode hypot im math-Package nimmt als Parameter die beiden Katheden. Durch den Aufruf der Methode tupled an hypot wird daraus eine Funktion, die ein Tupel von zwei Doubles erwartet. Der Unterstrich hier mag erst einmal etwas mysteriös erscheinen, aber die Erklärung ist ganz einfach: Es ist ein Hinweis, dass wir hypot an dieser Stelle nicht ausführen wollen, sondern „als Funktion an sich“ (Kant läßt grüßen) ansprechen. Woher soll auch sonst der Compiler wissen, dass es sich bei tupled nicht um ein Argument handelt, sondern eine Methode, die wir an hypot aufrufen wollen? Zur Verwirrung trägt sicher bei, dass man an dieser Stelle den Unterstrich eher für die Kurzschreibweise für Funktionen (also tuwas(_) anstatt x => tuwas(x)) erwarten würde.

Übrigens gibt es auch die „entgegengesetzte“ Methode untupled, mit der man aus einer Funktion func, die ein Tupel als Argument hat, eine macht, die die Argumente einzeln erwartet, allerdings muss man dazu Function.untupled(func _) schreiben.

So, genug herumgetupelt…

Advertisements

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