В чем разница между несколькими списками параметров и несколькими параметрами в списке в Scala?

В Scala можно написать (curry?) Такие функции

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 ответа

Решение

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

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

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
}

Или за гром

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 предоставляет как несколько списков параметров, так и несколько параметров для каждого списка?, Я просто скопирую их здесь, но заслуга Кнута Арне Ведаа, Кевина Райта и экспромта.

Во-первых: вы можете иметь несколько аргументов 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

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

Например:

def f(x: Int, y: Int = x * 2) = x + y // not valid
def g(x: Int)(y: Int = x * 2) = x + y // valid

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

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

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

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

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

Другие вопросы по тегам