programacion - ¿Cuál es la diferencia entre múltiples listas de parámetros y múltiples parámetros por lista en Scala?
scala ejemplos (4)
¡Ese es el punto, es que las formas al curry y sin currar son equivalentes! Como han señalado otros, 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 una sobre la otra.
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, imagine que la sintaxis def foo(a)(b)(c) = {...}
no existe. Entonces aún podrías lograr exactamente lo mismo que antes: 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.
En Scala, uno puede escribir (¿curry?) Funciones como esta
def curriedFunc(arg1: Int) (arg2: String) = { ... }
¿Cuál es la diferencia entre la definición de función curriedFunc
anterior 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?
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, ya que 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
Hay otra diferencia que no fue cubierta por la answer excelente de answer : parámetros por defecto. 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
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.