Wiele metod listy parametrów
Dla wnioskowania o typie
Metody z wieloma sekcjami parametrów mogą służyć do wspomagania wnioskowania o typie lokalnym, używając parametrów w pierwszej sekcji do wywnioskowania argumentów typu, które zapewnią oczekiwany typ argumentu w kolejnej sekcji. foldLeft
w bibliotece standardowej jest to kanoniczny przykład.
def foldLeft[B](z: B)(op: (B, A) => B): B
List("").foldLeft(0)(_ + _.length)
Gdyby to było zapisane jako:
def foldLeft[B](z: B, op: (B, A) => B): B
Należałoby podać bardziej wyraźne typy:
List("").foldLeft(0, (b: Int, a: String) => a + b.length)
List("").foldLeft[Int](0, _ + _.length)
Dla płynnego API
Innym zastosowaniem metod sekcji z wieloma parametrami jest utworzenie interfejsu API, który wygląda jak konstrukcja językowa. Osoba wywołująca może użyć nawiasów klamrowych zamiast nawiasów.
def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)
loop(2) {
println("hello!")
}
Zastosowanie list N argumentów do metody z sekcjami parametrów M, gdzie N <M, można przekonwertować na funkcję jawnie za pomocą _
lub niejawnie z oczekiwanym typem FunctionN[..]
. Jest to funkcja bezpieczeństwa, zapoznaj się z uwagami o zmianach dla Scala 2.0, w Referencjach Scala, aby zapoznać się z tłem.
Funkcje Curried
Funkcje curried (lub po prostu funkcje, które zwracają funkcje) można łatwiej zastosować do list N argumentów.
val f = (a: Int) => (b: Int) => (c: Int) => a + b + c
val g = f(1)(2)
Ta niewielka wygoda czasami się opłaca. Zauważ, że funkcje nie mogą być parametryczne, więc w niektórych przypadkach wymagana jest metoda.
Twój drugi przykład to hybryda: jednoparametrowa metoda sekcji, która zwraca funkcję.
Obliczenia wieloetapowe
Gdzie jeszcze przydatne są funkcje curry? Oto wzór, który pojawia się cały czas:
def v(t: Double, k: Double): Double = {
val ft = f(t)
g(ft, k)
}
v(1, 1); v(1, 2);
Jak możemy podzielić się wynikiem f(t)
? Typowym rozwiązaniem jest zapewnienie zwektoryzowanej wersji v
:
def v(t: Double, ks: Seq[Double]: Seq[Double] = {
val ft = f(t)
ks map {k => g(ft, k)}
}
Brzydki! Poplątaliśmy niepowiązane problemy - obliczanie g(f(t), k)
i mapowanie w sekwencji 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))
Moglibyśmy również użyć metody, która zwraca funkcję. W tym przypadku jest nieco bardziej czytelny:
def v(t:Double): Double => Double = {
val ft = f(t)
(k: Double) => g(ft, k)
}
Ale jeśli spróbujemy zrobić to samo z metodą z wieloma sekcjami parametrów, utkniemy:
def v(t: Double)(k: Double): Double = {
^
`-- Can't insert computation here!
}