scala каррирование с помощью вложенных функций или нескольких списков параметров - PullRequest
6 голосов
/ 15 января 2011

В Scala я могу определить функцию с двумя списками параметров.

def myAdd(x :Int)(y :Int) = x + y

Это позволяет легко определить частично примененную функцию.

val plusFive = myAdd(5) _

Но я могу сделать нечто подобное, определив и вернув вложенную функцию.

  def myOtherAdd(x :Int) = {
    def f(y :Int) = x + y
    f _
  }

Косметически я переместил подчеркивание, но это все еще похоже на карри.

val otherPlusFive = myOtherAdd(5)

Какие критерии мне следует использовать, чтобы предпочесть один подход другому?

Ответы [ 3 ]

9 голосов
/ 15 января 2011

Существует как минимум четыре способа сделать то же самое:

def myAddA(x: Int, y: Int) = x + y
val plusFiveA: Int => Int = myAddA(5,_)

def myAddB(x: Int)(y : Int) = x + y
val plusFiveB = myAddB(5) _

def myAddC(x: Int) = (y: Int) => x + y
val plusFiveC = myAddC(5)

def myAddD(x: Int) = {
  def innerD(y: Int) = x + y
  innerD _
}
val plusFiveD = myAddD(5)

Возможно, вы захотите узнать , который наиболее эффективен или , который является лучшим стилем (для некоторой лучшей оценки, основанной на неэффективности).

Что касается эффективности, то оказывается, что все четыре по существу эквивалентны.Первые два случая фактически излучают один и тот же байт-код;JVM ничего не знает о множественных списках параметров, поэтому, как только компилятор выяснит это (вам нужно помочь с аннотацией типа в случае A), все будет скрыто.Третий случай также очень близок, но так как он обещает заблаговременно вернуть функцию и указывает ее на месте, он может избежать одного внутреннего поля.Четвертый случай почти такой же, как первые два с точки зрения проделанной работы;он просто выполняет преобразование в Function1 внутри метода, а не снаружи.

С точки зрения стиля, я предлагаю, чтобы B и C были лучшими способами, в зависимости от того, что вы делаете.Если ваш основной вариант использования заключается в создании функции, а не в вызове на месте с обоими списками параметров, тогда используйте C, потому что он говорит вам, что он собирается делать.(Эта версия также особенно знакома, например, людям, приезжающим из Хаскелла.) С другой стороны, если вы в основном собираетесь вызывать его на месте, но только изредка его карри, то используйте B. Опять же, он более четко говорит, чтоэто ожидается.

6 голосов
/ 15 января 2011

Вы также можете сделать это:

def yetAnotherAdd(x: Int) = x + (_: Int)

Вы должны выбрать API, основываясь на намерении.Основная причина в Scala иметь несколько списков параметров состоит в том, чтобы помочь выводу типа.Например:

def f[A](x: A)(f: A => A) = ...
f(5)(_ + 5)

Можно также использовать его, чтобы иметь несколько переменных, но я никогда не видел такого кода.И, конечно же, существует необходимость в неявном списке параметров, но это в значительной степени другое дело.

Теперь есть много способов, которыми вы можете иметь функции, возвращающие функции, что во многом и делает то, что делает каррирование.Вы должны использовать их, если API следует рассматривать как функцию, которая возвращает функцию.

Я думаю, что трудно получить более точную информацию, чем эта.

4 голосов
/ 24 января 2011

Еще одно преимущество метода, возвращающего функцию напрямую (вместо использования частичного приложения), заключается в том, что при использовании инфиксной нотации код становится намного чище, что позволяет избежать набора скобок и подчеркиваний в более сложных выражениях.

Рассмотрим:

val list = List(1,2,3,4)

def add1(a: Int)(b: Int) = a + b
list map { add1(5) _ }    

//versus

def add2(a: Int) = a + (_: Int)
list map add2(5)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...