Me doy cuenta de que hay varias preguntas aquí sobre qué son las funciones de currización y parcialmente aplicadas, pero estoy preguntando en qué se diferencian. Como ejemplo simple, aquí hay una función al curry para encontrar números pares:
def filter(xs: List[Int], p: Int => Boolean): List[Int] =
if (xs.isEmpty) xs
else if (p(xs.head)) xs.head :: filter(xs.tail, p)
else filter(xs.tail, p)
def modN(n: Int)(x: Int) = ((x % n) == 0)
Entonces podrías escribir lo siguiente para usar esto:
val nums = List(1,2,3,4,5,6,7,8)
println(filter(nums, modN(2))
que devuelve: List(2,4,6,8)
. Pero descubrí que puedo hacer lo mismo de esta manera:
def modN(n: Int, x: Int) = ((x % n) == 0)
val p = modN(2, _: Int)
println(filter(nums, p))
que también devuelve: List(2,4,6,8)
.
Entonces, mi pregunta es, ¿cuál es la principal diferencia entre los dos y cuándo usaría uno sobre el otro? ¿Es este un ejemplo demasiado simplista para mostrar por qué se usaría uno sobre el otro?
Respuestas:
La diferencia semántica se ha explicado bastante bien en la respuesta vinculada por Plasty Grove .
Sin embargo, en términos de funcionalidad, no parece haber mucha diferencia. Veamos algunos ejemplos para verificarlo. Primero, una función normal:
scala> def modN(n: Int, x: Int): Boolean = ((x % n) == 0) scala> modN(5, _ : Int) res0: Int => Boolean = <function1>
Entonces obtenemos un parcialmente aplicado
<function1>
que toma anInt
, porque ya le hemos dado el primer número entero. Hasta ahora tan bueno. Ahora al curry:scala> def modNCurried(n: Int)(x: Int): Boolean = ((x % n) == 0)
Con esta notación, ingenuamente esperaría que funcione lo siguiente:
scala> modNCurried(5) <console>:9: error: missing arguments for method modN; follow this method with `_' if you want to treat it as a partially applied function modNCurried(5)
Por lo tanto, la notación de lista de parámetros múltiples realmente no parece crear una función curry de inmediato (supuestamente para evitar gastos generales innecesarios), sino que espera a que indique explícitamente que desea curry (la notación también tiene otras ventajas ):
scala> modNCurried(5) _ res24: Int => Boolean = <function1>
Que es exactamente lo mismo que obtuvimos antes, así que no hay diferencia aquí, excepto por la notación. Otro ejemplo:
scala> modN _ res35: (Int, Int) => Boolean = <function2> scala> modNCurried _ res36: Int => (Int => Boolean) = <function1>
Esto demuestra cómo la aplicación parcial de una función "normal" da como resultado una función que toma todos los parámetros, mientras que la aplicación parcial de una función con múltiples listas de parámetros crea una cadena de funciones, una por lista de parámetros que, todas devuelven una nueva función:
scala> def foo(a:Int, b:Int)(x:Int)(y:Int): Int = a * b + x - y scala> foo _ res42: (Int, Int) => Int => (Int => Int) = <function2> scala> res42(5) <console>:10: error: not enough arguments for method apply: (v1: Int, v2: Int)Int => (Int => Int) in trait Function2. Unspecified value parameter v2.
Como puede ver, debido a que la primera lista de parámetros de
foo
tiene dos parámetros, la primera función en la cadena de curry tiene dos parámetros.En resumen, las funciones parcialmente aplicadas no son realmente diferentes de las funciones curry en términos de funcionalidad. Esto se verifica fácilmente dado que puede convertir cualquier función a una curry:
scala> (modN _).curried res45: Int => (Int => Boolean) = <function1 scala> modNCurried _ res46: Int => (Int => Boolean) = <function1>
Post Scriptum
Nota: La razón por la que su ejemplo
println(filter(nums, modN(2))
funciona sin el subrayado despuésmodN(2)
parece ser que el compilador de Scala simplemente asume ese subrayado como una conveniencia para el programador.Además: como @asflierl ha señalado correctamente, Scala no parece ser capaz de inferir el tipo cuando aplica parcialmente funciones "normales":
scala> modN(5, _) <console>:9: error: missing parameter type for expanded function ((x$1) => modN(5, x$1))
Considerando que esa información está disponible para funciones escritas usando notación de lista de parámetros múltiples:
scala> modNCurried(5) _ res3: Int => Boolean = <function1>
Esta respuesta muestra cómo esto puede ser muy útil.
fuente
Curry tiene que ver con tuplas: convertir una función que toma un argumento de tupla en una que toma n argumentos separados, y viceversa . Recordar esto es la clave para distinguir el curry de la aplicación parcial, incluso en idiomas que no admiten el curry.
La aplicación parcial es la capacidad de aplicar una función a algunos argumentos, produciendo una nueva función para los argumentos restantes .
Es fácil de recordar si solo piensa que curry es la transformación que tiene que ver con las tuplas.
En los idiomas que se currizan de forma predeterminada (como Haskell), la diferencia es clara: tienes que hacer algo para pasar argumentos en una tupla. Pero la mayoría de los otros lenguajes, incluido Scala, no están curvados de forma predeterminada: todos los argumentos se pasan como tuplas, por lo que curry / uncurry es mucho menos útil y menos obvio. Y la gente incluso termina pensando que la aplicación parcial y el curry son lo mismo, ¡solo porque no pueden representar funciones de curry fácilmente!
fuente
Función multivariable:
def modN(n: Int, x: Int) = ((x % n) == 0)
Curry (o la función de curry):
def modNCurried(n: Int)(x: Int) = ((x % n) == 0)
Por lo tanto, no es una función aplicada parcialmente que sea comparable al curry. Es la función multivariable. Lo que es comparable a la función parcialmente aplicada es el resultado de la invocación de una función curry, que es una función con la misma lista de parámetros que tiene la función parcialmente aplicada.
fuente
Solo para aclarar el último punto.
Scala puede inferir tipos si todos los parámetros son comodines, pero no cuando algunos de ellos están especificados y otros no.
scala> modN(_,_) res38: (Int, Int) => Boolean = <function2> scala> modN(1,_) <console>:13: error: missing parameter type for expanded function ((x$1) => modN(1, x$1)) modN(1,_) ^
fuente
La mejor explicación que pude encontrar hasta ahora: https://dzone.com/articles/difference-between-currying-amp-partially-applied
fuente