В чем разница между несколькими списками параметров и несколькими параметрами в списке в Scala? - PullRequest
77 голосов
/ 24 июля 2011

В Scala можно писать (карри?) Функции, подобные этой

def curriedFunc(arg1: Int) (arg2: String) = { ... }

В чем разница между приведенным выше определением функции curriedFunc с двумя списками параметров и функциями с несколькими параметрами в одном списке параметров:

def curriedFunc(arg1: Int, arg2: String) = { ... }

С математической точки зрения это (curriedFunc(x))(y) и curriedFunc(x,y), но я могу написать def sum(x) (y) = x + y, и то же самое будет def sum2(x, y) = x + y

Я знаю только одно отличие - это частично применяемые функции. Но оба пути для меня эквивалентны.

Есть ли другие различия?

Ответы [ 4 ]

84 голосов
/ 24 июля 2011

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

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

object NonCurr {
  def tabulate[A](n: Int, fun: Int => A) = IndexedSeq.tabulate(n)(fun)
}

NonCurr.tabulate[Double](10, _)            // not possible
val x = IndexedSeq.tabulate[Double](10) _  // possible. x is Function1 now
x(math.exp(_))                             // complete the application

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

NonCurr.tabulate(10, { i => val j = util.Random.nextInt(i + 1); i - i % 2 })

против

IndexedSeq.tabulate(10) { i =>
  val j = util.Random.nextInt(i + 1)
  i - i % 2
}

Или для thunk:

IndexedSeq.fill(10) {
  println("debug: operating the random number generator")
  util.Random.nextInt(99)
}

Другое преимущество состоит в том, что вы можете ссылаться на аргументы из предыдущего списка аргументов для определениязначения аргументов по умолчанию (хотя можно также сказать, что недостатком является то, что вы не можете сделать это в одном списке:)

// again I'm not very creative with the example, so forgive me
def doSomething(f: java.io.File)(modDate: Long = f.lastModified) = ???

Наконец, есть три других приложения в ответе на соответствующий пост Почему Scalaпредоставить несколько списков параметров и несколько параметров для каждого списка? .Я просто скопирую их здесь, но заслуга Кнута Арне Ведаа, Кевина Райта и extempore.

Во-первых: у вас может быть несколько аргументов var:

def foo(as: Int*)(bs: Int*)(cs: Int*) = as.sum * bs.sum * cs.sum

..., которыебыло бы невозможно в одном списке аргументов.

Во-вторых, это помогает выводу типа:

def foo[T](a: T, b: T)(op: (T,T) => T) = op(a, b)
foo(1, 2){_ + _}   // compiler can infer the type of the op function

def foo2[T](a: T, b: T, op: (T,T) => T) = op(a, b)
foo2(1, 2, _ + _)  // compiler too stupid, unfortunately

И, наконец, это единственный способ, которым вы можете иметь неявные и неявные аргументы,implicit является модификатором для всего списка аргументов:

def gaga [A](x: A)(implicit mf: Manifest[A]) = ???   // ok
def gaga2[A](x: A, implicit mf: Manifest[A]) = ???   // not possible
.
41 голосов
/ 26 июля 2011

Есть еще одно отличие, которое не было охвачено 0 __ отлично ответ : параметры по умолчанию. Параметр из одного списка параметров можно использовать при вычислении значения по умолчанию в другом списке параметров, но не в том же.

Например:

def f(x: Int, y: Int = x * 2) = x + y // not valid
def g(x: Int)(y: Int = x * 2) = x + y // valid
19 голосов
/ 24 июля 2011

В том-то и дело, что карри и нетуширные формы эквивалентны!Как указывали другие, одна или другая форма может быть синтаксически более удобной для работы в зависимости от ситуации, и это единственная причина, по которой она предпочтительнее одной.

Этоважно понимать, что даже если в Scala не было специального синтаксиса для объявления каррированных функций, вы все равно можете их сконструировать;это просто математическая неизбежность, если у вас есть возможность создавать функции, которые возвращают функции.

Чтобы продемонстрировать это, представьте, что синтаксис def foo(a)(b)(c) = {...} не существует.Тогда вы все равно сможете достичь того же самого, что и так: def foo(a) = (b) => (c) => {...}.

Как и многие функции в Scala, это всего лишь синтаксическое удобство для выполнения чего-либо, что было бы возможно в любом случае, но с немного большей детализацией.

4 голосов
/ 24 июля 2011

Две формы изоморфны.Основное отличие состоит в том, что функции с карри легче применять частично, тогда как функции без каррирования имеют немного более приятный синтаксис, по крайней мере, в Scala.

...