Methoden mit mehreren Parameterlisten
Für Typinferenz
Methoden mit mehreren Parameterabschnitten können verwendet werden, um die lokale Typinferenz zu unterstützen, indem Parameter im ersten Abschnitt verwendet werden, um Typargumente abzuleiten, die im nachfolgenden Abschnitt einen erwarteten Typ für ein Argument bereitstellen. foldLeft
in der Standardbibliothek ist das kanonische Beispiel dafür.
def foldLeft[B](z: B)(op: (B, A) => B): B
List("").foldLeft(0)(_ + _.length)
Wenn dies so geschrieben wäre als:
def foldLeft[B](z: B, op: (B, A) => B): B
Man müsste explizitere Typen angeben:
List("").foldLeft(0, (b: Int, a: String) => a + b.length)
List("").foldLeft[Int](0, _ + _.length)
Für fließende API
Eine andere Verwendung für Methoden mit mehreren Parameterabschnitten besteht darin, eine API zu erstellen, die wie ein Sprachkonstrukt aussieht. Der Anrufer kann anstelle von Klammern geschweifte Klammern verwenden.
def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)
loop(2) {
println("hello!")
}
Die Anwendung von N Argumentlisten auf Methoden mit M Parameterabschnitten, wobei N <M ist, kann explizit mit einem _
oder implizit mit einem erwarteten Typ von in eine Funktion konvertiert werden FunctionN[..]
. Dies ist eine Sicherheitsfunktion. Hintergrundinformationen finden Sie in den Änderungshinweisen für Scala 2.0 in den Scala-Referenzen.
Curry-Funktionen
Curry-Funktionen (oder einfach Funktionen, die Funktionen zurückgeben) lassen sich leichter auf N Argumentlisten anwenden.
val f = (a: Int) => (b: Int) => (c: Int) => a + b + c
val g = f(1)(2)
Diese kleine Bequemlichkeit lohnt sich manchmal. Beachten Sie, dass Funktionen nicht typparametrisch sein können. In einigen Fällen ist daher eine Methode erforderlich.
Ihr zweites Beispiel ist ein Hybrid: eine Ein-Parameter-Abschnittsmethode, die eine Funktion zurückgibt.
Mehrstufige Berechnung
Wo sonst sind Curry-Funktionen nützlich? Hier ist ein Muster, das ständig auftaucht:
def v(t: Double, k: Double): Double = {
// expensive computation based only on t
val ft = f(t)
g(ft, k)
}
v(1, 1); v(1, 2);
Wie können wir das Ergebnis teilen f(t)
? Eine übliche Lösung besteht darin, eine vektorisierte Version von v
:
def v(t: Double, ks: Seq[Double]: Seq[Double] = {
val ft = f(t)
ks map {k => g(ft, k)}
}
Hässlich! Wir haben nicht verwandte Bedenken verwickelt - Berechnung g(f(t), k)
und Abbildung über eine Folge von ks
.
val v = { (t: Double) =>
val ft = f(t)
(k: Double) => g(ft, k)
}
val t = 1
val ks = Seq(1, 2)
val vs = ks map (v(t))
Wir könnten auch eine Methode verwenden, die eine Funktion zurückgibt. In diesem Fall ist es etwas besser lesbar:
def v(t:Double): Double => Double = {
val ft = f(t)
(k: Double) => g(ft, k)
}
Wenn wir jedoch versuchen, dasselbe mit einer Methode mit mehreren Parameterabschnitten zu tun, bleiben wir stecken:
def v(t: Double)(k: Double): Double = {
^
`-- Can't insert computation here!
}
def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)
val f: (a: Int) => (b: Int) => (c: Int) = a + b + c
Sie können nur Funktionen curry, keine Methoden.
add
ist eine Methode, daher müssen Sie die_
Konvertierung in eine Funktion erzwingen.add2
gibt eine Funktion zurück, so dass dies_
nicht nur unnötig ist, sondern hier keinen Sinn ergibt.In Anbetracht der unterschiedlichen Methoden und Funktionen (z. B. aus Sicht der JVM) macht Scala in den meisten Fällen einen ziemlich guten Job, indem sie die Grenze zwischen ihnen verwischt und "The Right Thing" ausführt, aber es gibt einen Unterschied, und manchmal müssen Sie nur darüber Bescheid wissen.
quelle
Ich denke, es hilft, die Unterschiede zu erfassen, wenn ich hinzufüge, dass
def add(a: Int)(b: Int): Int
Sie bei Ihnen so ziemlich nur eine Methode mit zwei Parametern definieren, nur diese beiden Parameter werden in zwei Parameterlisten gruppiert (siehe die Konsequenzen davon in anderen Kommentaren). Tatsächlichint add(int a, int a)
betrifft diese Methode Java (nicht Scala!). Wenn Sie schreibenadd(5)_
, ist das nur ein Funktionsliteral, eine kürzere Form von{ b: Int => add(1)(b) }
. Auf der anderen Seiteadd2(a: Int) = { b: Int => a + b }
definieren Sie mit Ihnen eine Methode, die nur einen Parameter hat, und für Java wird es seinscala.Function add2(int a)
. Wenn Sieadd2(1)
in Scala schreiben , ist dies nur ein einfacher Methodenaufruf (im Gegensatz zu einem Funktionsliteral).Beachten Sie auch, dass dies
add
(möglicherweise) weniger Overhead verursacht alsadd2
wenn Sie sofort alle Parameter angeben. Wieadd(5)(6)
nuradd(5, 6)
auf der JVM-Ebene übersetzt, wird keinFunction
Objekt erstellt. Auf der anderen Seiteadd2(5)(6)
wird zuerst einFunction
Objekt erstellt, das einschließt5
, und dann dieses aufgerufenapply(6)
.quelle