2016-08-12 3 views
0

Warum ist die folgende Aussage gültig für .map(), aber nicht für .flatMap()?Scala: flatMap mit Tupeln

val tupled = input.map(x => (x*2, x*3)) 

//Compilation error: cannot resolve reference flatMap with such signature 
val tupled = input.flatMap(x => (x*2, x*3)) 

Diese Aussage hat kein Problem, aber:

val tupled = input.flatMap(x => List(x*2, x*3)) 
+0

Was ist die Art von 'Eingang'? Überprüfen Sie die map- und flatMap-Methodensignaturen. Es hat verschiedene Parameter, deshalb hat Ihr Code einen Kompilierungsfehler. –

+0

Weil 'flatMap' zum Abflachen der Struktur verwendet wird. Wenn Sie ein 'M [M [A]]' mit 'map' erhalten, erhalten Sie' M [A] 'mit' flatMap'. –

+0

Das verschachtelte 'M' kann manchmal anders sein,' N' .. Zum Beispiel 'List [Option [String]]' => 'List [String]' –

Antwort

3

Unter der Annahme, input wenn vom Typ List[Int], nimmt map eine Funktion Int-A, während flatMap eine Funktion von Int zu List[A] nimmt.

Je nach Anwendungsfall können Sie eines oder das andere wählen, aber sie sind definitiv nicht austauschbar.

Zum Beispiel, wenn Sie nur sind die Elemente eines List verwandeln möchten Sie in der Regel map verwenden:

List(1, 2, 3).map(x => x * 2) // List(2, 4, 6) 

aber Sie wollen die Struktur des List ändern und - zum Beispiel - „explodieren“ jeder Element in einer anderen Liste sie dann Abflachen flatMap ist dein Freund:

List(1, 2, 3).flatMap(x => List.fill(x)(x)) // List(1, 2, 2, 3, 3, 3) 

Mit map Siegehabt hätten 10 stattdessen.

+1

@NathanielFord yep, du hast Recht. Aktualisiert, danke –

1

Um zu verstehen, wie dies funktioniert, kann es nützlich sein, die Funktionen explizit zu entpacken, die Sie an map und flatMap senden und ihre Signaturen untersuchen. Ich habe sie hier umgeschrieben, so dass Sie sehen können, dass f eine Funktionszuordnung von Int zu einem (Int, Int) Tupel ist und g eine Funktion ist, die von Int auf eine List[Int] abbildet.

val f: (Int) => (Int, Int) = x => (x*2, x*3) 
val g: (Int) => List[Int] = x => List(x*2, x*3) 

List(1,2,3).map(f) 
//res0: List[(Int, Int)] = List((2,3), (4,6), (6,9)) 
List(1,2,3).map(g) 
//res1: List[List[Int]] = List(List(2, 3), List(4, 6), List(6, 9)) 
//List(1,2,3).flatMap(f) // This won't compile 
List(1,2,3).flatMap(g) 
//res2: List[Int] = List(2, 3, 4, 6, 6, 9) 

Also warum wird flatMap(f) nicht kompilieren? Lassen Sie sich für flatMap, in diesem Fall auf der Unterschrift sieht von der List Implementierung gezogen:

final override def flatMap[B, That](f : scala.Function1[A, scala.collection.GenTraversableOnce[B]])(...) 

Dies ist ein wenig schwierig zu entpacken, und ich habe einige davon elided, aber der Schlüssel ist der GenTraversableOnce Typ. List, wenn Sie seine Kette der Vererbung nach oben folgen, hat dies eine Eigenschaft, mit der es gebaut wird, und damit eine Funktion, die von einem Typ A zu einem List (oder einem Objekt mit der GenTraversableOnce Trait) abbildet wird eine gültige Funktion sein. Bemerkenswerterweise haben Tupel diese Eigenschaft nicht.

Dies ist die In-the-Unkraut-Erklärung, warum die Eingabe falsch ist, und ist erklärungswürdig, weil jeder Fehler, der besagt, kann nicht mit einer solchen Signatur auflösen 'bedeutet, dass es keine Funktion finden kann, die die explizite nimmt Geben Sie an, was Sie anbieten. In Scala werden sehr oft Typen abgeleitet, sodass Sie sicherstellen können, dass der von Ihnen angegebene Typ der Typ ist, der von der aufgerufenen Methode erwartet wird.

Beachten Sie, dass flatMap eine Standardbedeutung in der funktionalen Programmierung hat, was grob gesagt jede Zuordnungsfunktion ist, die ein einzelnes Element konsumiert und n Elemente erzeugt, aber Ihr Endergebnis ist die Verkettung all dieser Listen.Daher wird von der Funktion, die Sie an flatMap übergeben, immer erwartet, dass eine Liste erstellt wird, und es wird erwartet, dass die Funktion flatMap nicht weiß, wie sie auf einzelne Elemente einwirken soll.

Verwandte Themen