Ściśle mówiąc, nie jest to funkcja curried, ale metoda z wieloma listami argumentów, chociaż wprawdzie wygląda jak funkcja.
Jak powiedziałeś, listy wielu argumentów pozwalają na użycie metody zamiast częściowo zastosowanej funkcji. (Przepraszam za ogólnie głupie przykłady, których używam)
object NonCurr {
def tabulate[A](n: Int, fun: Int => A) = IndexedSeq.tabulate(n)(fun)
}
NonCurr.tabulate[Double](10, _)
val x = IndexedSeq.tabulate[Double](10) _
x(math.exp(_))
Inną korzyścią jest to, że zamiast nawiasów można używać nawiasów klamrowych, co wygląda ładnie, jeśli druga lista argumentów składa się z pojedynczej funkcji lub thunk. Na przykład
NonCurr.tabulate(10, { i => val j = util.Random.nextInt(i + 1); i - i % 2 })
przeciw
IndexedSeq.tabulate(10) { i =>
val j = util.Random.nextInt(i + 1)
i - i % 2
}
Lub dla thunk:
IndexedSeq.fill(10) {
println("debug: operating the random number generator")
util.Random.nextInt(99)
}
Kolejną zaletą jest to, że możesz odwołać się do argumentów z poprzedniej listy argumentów w celu zdefiniowania domyślnych wartości argumentów (chociaż możesz również powiedzieć, że jest to wada, że nie możesz tego zrobić na pojedynczej liście :)
def doSomething(f: java.io.File)(modDate: Long = f.lastModified) = ???
Wreszcie w odpowiedzi na pokrewny post znajdują się trzy inne aplikacje. Dlaczego Scala udostępnia zarówno listy wielu parametrów, jak i wiele parametrów na listę? . Po prostu skopiuję je tutaj, ale zasługa Knuta Arne Vedaa, Kevina Wrighta i extempore.
Po pierwsze: możesz mieć wiele argumentów var:
def foo(as: Int*)(bs: Int*)(cs: Int*) = as.sum * bs.sum * cs.sum
... co nie byłoby możliwe w przypadku pojedynczej listy argumentów.
Po drugie, wspomaga wnioskowanie o typie:
def foo[T](a: T, b: T)(op: (T,T) => T) = op(a, b)
foo(1, 2){_ + _}
def foo2[T](a: T, b: T, op: (T,T) => T) = op(a, b)
foo2(1, 2, _ + _)
I na koniec, jest to jedyny sposób, w jaki możesz mieć niejawne i niejawne argumenty, podobnie jak implicit
modyfikator dla całej listy argumentów:
def gaga [A](x: A)(implicit mf: Manifest[A]) = ???
def gaga2[A](x: A, implicit mf: Manifest[A]) = ???
Jest jeszcze jedna różnica, która nie została uwzględniona w doskonałej odpowiedzi 0 __ : parametry domyślne. Parametru z jednej listy parametrów można użyć podczas obliczania wartości domyślnej na innej liście parametrów, ale nie na tej samej.
Na przykład:
def f(x: Int, y: Int = x * 2) = x + y // not valid def g(x: Int)(y: Int = x * 2) = x + y // valid
źródło
g(1)()
zwraca 3.g(1)(2)
zwraca 5.O to chodzi, w tym, że curry i nieuruszone formy są równoważne! Jak zauważyli inni, praca z jedną lub drugą formą może być wygodniejsza pod względem składniowym w zależności od sytuacji i jest to jedyny powód, dla którego należy preferować jedną z nich.
Ważne jest, aby zrozumieć, że nawet gdyby Scala nie miała specjalnej składni do deklarowania funkcji curried, nadal można by je skonstruować; jest to po prostu matematyczna nieuchronność, gdy masz już możliwość tworzenia funkcji zwracających funkcje.
Aby to zademonstrować, wyobraź sobie, że
def foo(a)(b)(c) = {...}
składnia nie istnieje. Następnie można jeszcze osiągnąć dokładnie to samo tak:def foo(a) = (b) => (c) => {...}
.Podobnie jak wiele funkcji w Scali, jest to tylko wygoda składniowa do robienia czegoś, co i tak byłoby możliwe, ale z nieco większą szczegółowością.
źródło
Te dwie formy są izomorficzne. Główną różnicą jest to, że funkcje curried są łatwiejsze do zastosowania częściowo, podczas gdy funkcje niezwiązane z curry mają nieco ładniejszą składnię, przynajmniej w Scali.
źródło