Pregunta ¿Cuál es la diferencia entre múltiples listas de parámetros y múltiples parámetros por lista en Scala?


En Scala, uno puede escribir (¿curry?) Funciones como esta

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

¿Cuál es la diferencia entre los anteriores curriedFunc definición de función con dos listas de parámetros y funciones con múltiples parámetros en una sola lista de parámetros:

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

Desde un punto de vista matemático esto es (curriedFunc(x))(y) y curriedFunc(x,y) pero puedo escribir def sum(x) (y) = x + y y lo mismo será def sum2(x, y) = x + y

Solo conozco una diferencia: se trata de funciones parcialmente aplicadas. Pero ambas formas son equivalentes para mí.

¿Hay otras diferencias?


76
2017-07-23 20:48


origen


Respuestas:


Estrictamente hablando, esto no es una función al curry, sino un método con múltiples listas de argumentos, aunque es cierto que parece una función.

Como dijiste, las listas de múltiples argumentos permiten que el método se use en lugar de una función parcialmente aplicada. (Perdón por los ejemplos generalmente tontos que uso)

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

Otra ventaja es que puede usar llaves en vez de paréntesis, lo que se ve bien si la segunda lista de argumentos consiste en una sola función, o thunk. P.ej.

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

versus

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

O por el golpe:

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

Otra ventaja es que puede consultar argumentos de una lista de argumentos previa para definir valores de argumento predeterminados (aunque también podría decirse que es una desventaja que no puede hacer eso en una sola lista :)

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

Finalmente, hay otras tres aplicaciones en una respuesta a una publicación relacionada ¿Por qué Scala proporciona listas de parámetros múltiples y múltiples parámetros por lista? . Los copiaré aquí, pero el mérito recae en Knut Arne Vedaa, Kevin Wright e improvisado.

Primero: puede tener múltiples var args:

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

... que no sería posible en una sola lista de argumentos.

En segundo lugar, ayuda a la inferencia tipográfica:

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

Y, por último, esta es la única forma en que puede tener args implícitos y no implícitos, como implicit es un modificador para una lista de argumentos completa:

def gaga [A](x: A)(implicit mf: Manifest[A]) = ???   // ok
def gaga2[A](x: A, implicit mf: Manifest[A]) = ???   // not possible

81
2017-07-23 23:21



Hay otra diferencia que no fue cubierta por 0 __ excelente responder: parámetros predeterminados. Se puede usar un parámetro de una lista de parámetros cuando se calcula el valor predeterminado en otra lista de parámetros, pero no en la misma.

Por ejemplo:

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

39
2017-07-26 02:57



¡Ese es el punto, es que las formas al curry y sin currar son equivalentes! Como otros han señalado, una u otra forma puede ser sintácticamente más conveniente para trabajar dependiendo de la situación, y esa es la única razón para preferir uno sobre el otro.

Es importante comprender que incluso si Scala no tiene una sintaxis especial para declarar funciones con curry, aún podría construirlas; esto es solo una inevitabilidad matemática una vez que tienes la capacidad de crear funciones que devuelven funciones.

Para demostrar esto, imagina que el def foo(a)(b)(c) = {...} la sintaxis no existía Entonces aún podrías lograr exactamente lo mismo así: def foo(a) = (b) => (c) => {...}.

Al igual que muchas características en Scala, esto es solo una conveniencia sintáctica para hacer algo que de todos modos sería posible, pero con un poco más de detalle.


18
2017-07-23 23:18



Las dos formas son isomorfas. La principal diferencia es que las funciones al curry son más fáciles de aplicar parcialmente, mientras que las funciones sin curry tienen una sintaxis ligeramente mejor, al menos en Scala.


4
2017-07-23 20:55