2013-10-20 15 views
22

Dеar Scala,Wie funktioniert eine "case" anonyme Funktion wirklich in Scala?

scala> val f1: ((Int, Int)) => Int = { case (a, b) => a + b } 
f1: ((Int, Int)) => Int = <function1> 

scala> val f2: (Int, Int) => Int = { case (a, b) => a + b } 
f2: (Int, Int) => Int = <function2> 

nicht wahr ?!

scala> f1(1, 2) 
res2: Int = 3 

Ok ...

scala> def takesIntInt2Int(fun: (Int, Int) => Int) = fun(100, 200) 
takesIntInt2Int: (fun: (Int, Int) => Int)Int 

scala> def takesTuple2Int(fun: ((Int, Int)) => Int) = fun(100, 200) 
takesTuple2Int: (fun: ((Int, Int)) => Int)Int 

scala> takesIntInt2Int(f2) 
res4: Int = 300 

scala> takesIntInt2Int(f1) 
<console>:10: error: type mismatch; 
found : ((Int, Int)) => Int 
required: (Int, Int) => Int 
       takesIntInt2Int(f1) 
          ^

scala> takesTuple2Int(f1) 
res6: Int = 300 

scala> takesTuple2Int(f2) 
<console>:10: error: type mismatch; 
found : (Int, Int) => Int 
required: ((Int, Int)) => Int 
       takesTuple2Int(f2) 

Rechts. Und nun, sieh dir das an!

scala> takesTuple2Int { case (a, b, c) => a + b + c } 
<console>:9: error: constructor cannot be instantiated to expected type; 
found : (T1, T2, T3) 
required: (Int, Int) 
       takesTuple2Int { case (a, b, c) => a + b + c } 
            ^

scala> takesIntInt2Int { case (a, b, c) => a + b + c } 
<console>:9: error: constructor cannot be instantiated to expected type; 
found : (T1, T2, T3) 
required: (Int, Int) 
       takesIntInt2Int { case (a, b, c) => a + b + c } 

Wie, srsly? o_O Beide führen zu required: (Int, Int) Fehler.

Warum verwenden Sie dann case überhaupt in solchen anonymen Funktionen?

Antwort

15

Siehe Abschnitt 8.5 der Scala-Referenz (http://www.scala-lang.org/files/archive/nightly/pdfs/ScalaReference.pdf). Der Ausdruck { case (a, b) => a + b } wird basierend auf dem erwarteten Typ unterschiedlich interpretiert. In Ihrer Definition von f1 wurde ein PartialFunction[(Int, Int), Int] erzeugt, der zu einem Function1[(Int, Int), Int], d. H. ((Int, Int)) => Int, gegossen wurde, wohingegen in der Definition von f2 ein Function2[Int, Int, Int], d. H. (Int, Int) => Int, erzeugt wurde.

Diese beiden Interpretationen beziehen sich auf die beiden Situationen, in denen Sie häufig Fall in einer anonymen Funktion verwenden würden.

Eine ist für das Schreiben anonymer Funktionen, die Tupel akzeptieren und an ihren Komponenten arbeiten, wie Sie es mit f1 getan haben. Ein Beispiel wäre die Funktion, die Sie an die foreach oder map-Methode auf einer Map, z. Map(1 -> 2, 3 -> 4) map { case (k, v) => k + v }.

Die zweite ist für das Schreiben einer anonymen Funktion, die einen match auf seinem einzigen Parameter macht. Ihre f2 tut dies, aber nicht in irgendeiner nützlichen Weise. Ein Beispiel wäre die an collect übergebene anonyme Funktion, z. List(1, -2, 3) collect { case x if x > 0 => -x }.

Beachten Sie, dass die beiden kombiniert werden können, das heißt, Funktionen wie f1 können komplexe Anpassung auch tun. Zum Beispiel: Map(1 -> 2, 3 -> 4) collect { case (k, v) if k < 2 => v }.

Edit: res2 funktioniert wegen der Tupling. Wenn eine Anwendung die Überprüfung nicht eingibt, versucht der Compiler, die Argumente in einem Tupel zu verpacken, bevor sie fehlschlagen.

Aber das versucht wird, nur für Anwendungen; Es ist keine allgemeine Umwandlung, wie Sie entdeckt haben. Es wird nicht versuchen, einen Wert Function2[A, B, C]-Function1[(A, B), C] zu aktualisieren.

+1

Es wäre toll, die Lage sein, all dies zu tun, ohne 'case' Schlüsselwort. Warum der Unterschied in der Syntax für Function und PartialFunction, aus der Sicht eines normalen Entwicklers? –

+2

@ MichałRus Um ehrlich zu sein, das störte mich immer ein wenig. Haskell und Clojure haben viel einfachere Syntaxen, um Mustervergleiche direkt auf Parameter einer Funktion auszudrücken. – wingedsubmariner

Verwandte Themen