Методы с несколькими параметрами
Для вывода типа
Для облегчения вывода локального типа можно использовать методы с несколькими разделами параметров, используя параметры в первом разделе для вывода аргументов типа, которые предоставят ожидаемый тип для аргумента в следующем разделе. foldLeft
в стандартной библиотеке есть канонический пример этого.
def foldLeft[B](z: B)(op: (B, A) => B): B
List("").foldLeft(0)(_ + _.length)
Если бы это было написано как:
def foldLeft[B](z: B, op: (B, A) => B): B
Придется предоставить более явные типы:
List("").foldLeft(0, (b: Int, a: String) => a + b.length)
List("").foldLeft[Int](0, _ + _.length)
Для свободного API
Еще одно применение методов раздела с несколькими параметрами - создание API, который выглядит как языковая конструкция. Вызывающий может использовать фигурные скобки вместо круглых.
def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)
loop(2) {
println("hello!")
}
Применение N списков аргументов к методу с M разделами параметров, где N <M, может быть преобразовано в функцию явно с _
или неявно с ожидаемым типом FunctionN[..]
. Это функция безопасности, справочную информацию см. В примечаниях к изменениям для Scala 2.0 в Справочниках по Scala.
Карри-функции
Каррированные функции (или просто функции, возвращающие функции) легче применять к N спискам аргументов.
val f = (a: Int) => (b: Int) => (c: Int) => a + b + c
val g = f(1)(2)
Это незначительное удобство иногда имеет смысл. Обратите внимание, что функции не могут быть параметрическими, поэтому в некоторых случаях требуется метод.
Ваш второй пример - гибрид: метод раздела с одним параметром, который возвращает функцию.
Многоступенчатое вычисление
Где еще полезны каррированные функции? Вот шаблон, который возникает постоянно:
def v(t: Double, k: Double): Double = {
val ft = f(t)
g(ft, k)
}
v(1, 1); v(1, 2);
Как поделиться результатом f(t)
? Распространенное решение - предоставить векторизованную версию v
:
def v(t: Double, ks: Seq[Double]: Seq[Double] = {
val ft = f(t)
ks map {k => g(ft, k)}
}
Уродливо! Мы запутались в несвязанных задачах - вычислении g(f(t), k)
и отображении последовательности файлов 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))
Мы также могли бы использовать метод, возвращающий функцию. В этом случае его немного читабельнее:
def v(t:Double): Double => Double = {
val ft = f(t)
(k: Double) => g(ft, k)
}
Но если мы попытаемся сделать то же самое с методом с несколькими разделами параметров, мы застрянем:
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
Вы можете каррировать только функции, но не методы.
add
- это метод, поэтому вам нужно_
принудительно преобразовать его в функцию.add2
возвращает функцию, поэтому здесь_
не только не нужно, но и не имеет смысла.Учитывая то, как различные методы и функции (например , с точки зрения виртуальной машины Java), Scala делает очень хорошую работу стирая грань между ними и делать «правильно» в большинстве случаев, но есть разница, а иногда просто необходима знать об этом.
источник
Я думаю, что поможет понять различия, если я добавлю, что с
def add(a: Int)(b: Int): Int
вами в значительной степени просто определите метод с двумя параметрами, только эти два параметра сгруппированы в два списка параметров (см. Последствия этого в других комментариях). Фактически, этот метод применим толькоint add(int a, int a)
к Java (а не к Scala!). Когда вы пишетеadd(5)_
, это просто функциональный литерал, более короткая форма{ b: Int => add(1)(b) }
. С другой стороны,add2(a: Int) = { b: Int => a + b }
вы определяете метод, который имеет только один параметр, и для Java так и будетscala.Function add2(int a)
. Когда вы пишетеadd2(1)
на Scala, это просто вызов метода (в отличие от литерала функции).Также обратите внимание, что
add
накладные расходы (потенциально) меньше, чемadd2
если бы вы сразу указали все параметры. Какadd(5)(6)
просто переводитсяadd(5, 6)
на уровень JVM, никакойFunction
объект не создается. С другой стороны,add2(5)(6)
сначала создастFunction
объект, который включает5
, а затем вызовет егоapply(6)
.источник