language-agnostic terminology definition currying partial-application

language agnostic - ¿Cuál es la diferencia entre el curry y la aplicación parcial?



language-agnostic terminology (13)

Respuesta simple

Curry: le permite llamar a una función, dividirla en múltiples llamadas, proporcionando un argumento por llamada.

Parcial: le permite llamar a una función, dividirla en múltiples llamadas, proporcionando múltiples argumentos por llamada.

Consejos simples

Ambos le permiten llamar a una función proporcionando menos argumentos (o, mejor, proporcionándolos de forma acumulativa). En realidad, ambos vinculan (en cada llamada) un valor específico a argumentos específicos de la función.

La diferencia real se puede ver cuando la función tiene más de 2 argumentos.

Simple e (c) (muestra)

(en Javascript)

function process(context, success_callback, error_callback, subject) {...}

¿Por qué siempre se pasan los argumentos, como el contexto y las devoluciones de llamada, si siempre serán iguales? Solo une algunos valores para la función

processSubject = _.partial(process, my_context, my_success, my_error)

y llámalo al sujeto 1 y foobar con

processSubject(''subject1''); processSubject(''foobar'');

Cómodo, ¿no es así? 😉

Con currying necesitarías pasar un argumento por vez.

curriedProcess = _.curry(process); processWithBoundedContext = curriedProcess(my_context); processWithCallbacks = processWithBoundedContext(my_success)(my_error); // note: these are two sequential calls result1 = processWithCallbacks(''subject1''); // same as: process(my_context, my_success, my_error, ''subject1''); result2 = processWithCallbacks(''foobar''); // same as: process(my_context, my_success, my_error, ''foobar'');

Renuncia

Me salté toda la explicación académica / matemática. Porque no lo sé. Tal vez ayudó 🙃

A menudo veo en Internet varias quejas de que otros ejemplos de curry no son curry, pero en realidad son solo una aplicación parcial.

No he encontrado una explicación decente de qué es la aplicación parcial, o en qué se diferencia del curry. Parece que hay una confusión general, con ejemplos equivalentes que se describen como curry en algunos lugares y aplicación parcial en otros.

¿Podría alguien proporcionarme una definición de ambos términos y detalles de cómo difieren?


Al escribir esto, confundí curry y uncurrying. Son transformaciones inversas sobre funciones. Realmente no importa lo que llame, siempre que obtenga lo que representan la transformación y su inverso.

La falta de vigencia no está definida muy claramente (o, más bien, hay definiciones "en conflicto" que capturan el espíritu de la idea). Básicamente, significa convertir una función que toma múltiples argumentos en una función que toma un solo argumento. Por ejemplo,

(+) :: Int -> Int -> Int

Ahora, ¿cómo convertir esto en una función que toma un solo argumento? Haces trampa, por supuesto!

plus :: (Int, Int) -> Int

Observe que más ahora toma un solo argumento (que se compone de dos cosas). ¡Súper!

¿Cuál es el punto de esto? Bueno, si tiene una función que toma dos argumentos y tiene un par de argumentos, es bueno saber que puede aplicar la función a los argumentos y aún así obtener lo que espera. Y, de hecho, la plomería para hacerlo ya existe, por lo que no tiene que hacer cosas como la coincidencia explícita de patrones. Todo lo que tienes que hacer es:

(uncurry (+)) (1,2)

Entonces, ¿qué es la aplicación de función parcial? Es una forma diferente de convertir una función en dos argumentos en una función con un argumento. Aunque funciona de manera diferente. De nuevo, tomemos (+) como ejemplo. ¿Cómo podríamos convertirlo en una función que tome un solo Int como argumento? ¡Hacemos trampa!

((+) 0) :: Int -> Int

Esa es la función que agrega cero a cualquier Int.

((+) 1) :: Int -> Int

agrega 1 a cualquier Int. Etc. En cada uno de estos casos, (+) se "aplica parcialmente".


Currying es convertir una sola función de n argumentos en n funciones con un solo argumento cada uno. Dada la siguiente función:

function f(x,y,z) { z(x(y));}

Al curry, se convierte en:

function f(x) { lambda(y) { lambda(z) { z(x(y)); } } }

Para obtener la aplicación completa de f (x, y, z), necesita hacer esto:

f(x)(y)(z);

Muchos lenguajes funcionales te permiten escribir fxyz . Si solo llama a fxy o f (x) (y), entonces obtiene una función aplicada parcialmente; el valor de retorno es un cierre de lambda(z){z(x(y))} con los valores pasados ​​de x yy a f(x,y) .

Una forma de utilizar la aplicación parcial es definir funciones como aplicaciones parciales de funciones generalizadas, como fold :

function fold(combineFunction, accumalator, list) {/* ... */} function sum = curry(fold)(lambda(accum,e){e+accum}))(0); function length = curry(fold)(lambda(accum,_){1+accum})(empty-list); function reverse = curry(fold)(lambda(accum,e){concat(e,accum)})(empty-list); /* ... */ @list = [1, 2, 3, 4] sum(list) //returns 10 @f = fold(lambda(accum,e){e+accum}) //f = lambda(accumaltor,list) {/*...*/} f(0,list) //returns 10 @g = f(0) //same as sum g(list) //returns 10


El currying es una función de un argumento que toma una función f y devuelve una nueva función h . Tenga en cuenta que h toma un argumento de X y devuelve una función que mapea Y a Z :

curry(f) = h f: (X x Y) -> Z h: X -> (Y -> Z)

La aplicación parcial es una función de dos (o más) argumentos que toma una función f y uno o más argumentos adicionales en f y devuelve una nueva función g :

part(f, 2) = g f: (X x Y) -> Z g: Y -> Z

La confusión surge porque con una función de dos argumentos se cumple la siguiente igualdad:

partial(f, a) = curry(f)(a)

Ambos lados producirán la misma función de un argumento.

La igualdad no es cierta para las funciones de aridad superior porque en este caso, el currying devolverá una función de un solo argumento, mientras que la aplicación parcial devolverá una función de múltiples argumentos.

La diferencia también está en el comportamiento, mientras que el curry transforma toda la función original de forma recursiva (una vez para cada argumento), la aplicación parcial es solo un reemplazo de un paso.

Fuente: Wikipedia Currying .


Hay otras respuestas excelentes aquí, pero creo que este ejemplo (según mi entendimiento) en Java podría ser beneficioso para algunas personas:

public static <A,B,X> Function< B, X > partiallyApply( BiFunction< A, B, X > aBiFunction, A aValue ){ return b -> aBiFunction.apply( aValue, b ); } public static <A,X> Supplier< X > partiallyApply( Function< A, X > aFunction, A aValue ){ return () -> aFunction.apply( aValue ); } public static <A,B,X> Function< A, Function< B, X > > curry( BiFunction< A, B, X > bif ){ return a -> partiallyApply( bif, a ); }

Así que currying te da una función de un solo argumento para crear funciones, donde la aplicación parcial crea una función de envoltorio que codifica de forma rígida uno o más argumentos.

Si desea copiar y pegar, lo siguiente es más ruidoso pero más amigable para trabajar, ya que los tipos son más indulgentes:

public static <A,B,X> Function< ? super B, ? extends X > partiallyApply( final BiFunction< ? super A, ? super B, X > aBiFunction, final A aValue ){ return b -> aBiFunction.apply( aValue, b ); } public static <A,X> Supplier< ? extends X > partiallyApply( final Function< ? super A, X > aFunction, final A aValue ){ return () -> aFunction.apply( aValue ); } public static <A,B,X> Function< ? super A, Function< ? super B, ? extends X > > curry( final BiFunction< ? super A, ? super B, ? extends X > bif ){ return a -> partiallyApply( bif, a ); }


He respondido esto en otro hilo https://.com/a/12846865/1685865 . En resumen, la aplicación de función parcial consiste en arreglar algunos argumentos de una función multivariable dada para producir otra función con menos argumentos, mientras que Currying consiste en convertir una función de N argumentos en una función unaria que devuelve una función unaria ... [Un ejemplo de El curry se muestra al final de este post.]

El curry es principalmente de interés teórico: se pueden expresar cálculos utilizando solo funciones unarias (es decir, cada función es unaria). En la práctica y como un subproducto, es una técnica que puede hacer que muchas aplicaciones funcionales parciales útiles (pero no todas) sean triviales, si el lenguaje tiene funciones de curry. Nuevamente, no es el único medio para implementar aplicaciones parciales. Por lo tanto, podría encontrar situaciones en las que la aplicación parcial se realiza de otra manera, pero la gente la confunde con Currying.

(Ejemplo de Currying)

En la práctica uno no solo escribiría.

lambda x: lambda y: lambda z: x + y + z

o el javascript equivalente

function (x) { return function (y){ return function (z){ return x + y + z }}}

en lugar de

lambda x, y, z: x + y + z

por el bien de Currying.



La diferencia entre curry y aplicación parcial se puede ilustrar mejor a través de este siguiente ejemplo de JavaScript:

function f(x, y, z) { return x + y + z; } var partial = f.bind(null, 1); 6 === partial(2, 3);

La aplicación parcial resulta en una función de menor aridad; en el ejemplo anterior, f tiene una aridad de 3, mientras que partial solo tiene una aridad de 2. Más importante aún, una función parcialmente aplicada devolvería el resultado de inmediato al ser invocada , no otra función más abajo en la cadena de curry. Entonces, si está viendo algo como partial(2)(3) , no es una aplicación parcial en la actualidad.

Otras lecturas:


La forma más fácil de ver cómo difieren es considerar un ejemplo real . Supongamos que tenemos una función Add que toma 2 números como entrada y devuelve un número como salida, por ejemplo, Add(7, 5) devuelve 12 . En este caso:

  • La aplicación parcial de la función Add con un valor 7 nos dará una nueva función como salida. Esa función toma 1 número como entrada y genera un número. Como tal:

    Partial(Add, 7); // returns a function f2 as output // f2 takes 1 number as input and returns a number as output

    Así que podemos hacer esto:

    f2 = Partial(Add, 7); f2(5); // returns 12; // f2(7)(5) is just a syntactic shortcut

  • Currying la función Add nos dará una nueva función como salida. Esa función toma 1 número como entrada y genera otra función nueva. Esa tercera función toma 1 número como entrada y devuelve un número como salida. Como tal:

    Curry(Add); // returns a function f2 as output // f2 takes 1 number as input and returns a function f3 as output // i.e. f2(number) = f3 // f3 takes 1 number as input and returns a number as output // i.e. f3(number) = number

    Así que podemos hacer esto:

    f2 = Curry(Add); f3 = f2(7); f3(5); // returns 12

En otras palabras, "curry" y "aplicación parcial" son dos funciones totalmente diferentes. El currying toma exactamente 1 entrada, mientras que la aplicación parcial toma 2 (o más) entradas.

Aunque ambos devuelven una función como salida, las funciones devueltas son de formas totalmente diferentes como se demostró anteriormente.


Nota: esto fue tomado de F # Basics, un excelente artículo introductorio para los desarrolladores de .NET que entran en la programación funcional.

Currying significa dividir una función con muchos argumentos en una serie de funciones, cada una de las cuales toma un argumento y, en última instancia, produce el mismo resultado que la función original. El curry es probablemente el tema más desafiante para los programadores nuevos en la programación funcional, particularmente porque a menudo se confunde con la aplicación parcial. Puedes ver ambos en el trabajo en este ejemplo:

let multiply x y = x * y let double = multiply 2 let ten = double 5

De inmediato, debería ver un comportamiento diferente al de la mayoría de los idiomas imperativos. La segunda declaración crea una nueva función llamada doble al pasar un argumento a una función que toma dos. El resultado es una función que acepta un argumento int y produce el mismo resultado que si hubiera llamado a multiplicar con x igual a 2 e y igual a ese argumento. En términos de comportamiento, es lo mismo que este código:

let double2 z = multiply 2 z

A menudo, la gente dice erróneamente que la multiplicación se hace para formar el doble. Pero esto es solo algo cierto. La función de multiplicar está en el curry, pero eso sucede cuando se define porque las funciones en F # están en curry por defecto. Cuando se crea la función doble, es más exacto decir que la función de multiplicación se aplica parcialmente.

La función de multiplicación es en realidad una serie de dos funciones. La primera función toma un argumento int y devuelve otra función, vinculando efectivamente x a un valor específico. Esta función también acepta un argumento int que se puede considerar como el valor para vincular a y. Después de llamar a esta segunda función, tanto x como y están vinculados, por lo que el resultado es el producto de x e y como se define en el cuerpo del doble.

Para crear el doble, la primera función en la cadena de funciones de multiplicación se evalúa para aplicar parcialmente la multiplicación. La función resultante recibe el nombre de doble. Cuando se evalúa el doble, utiliza su argumento junto con el valor parcialmente aplicado para crear el resultado.


Para mí, la aplicación parcial debe crear una nueva función en la que los argumentos utilizados estén completamente integrados en la función resultante.

La mayoría de los lenguajes funcionales implementan el curry devolviendo un cierre: no evalúe bajo lambda cuando se aplica parcialmente. Por lo tanto, para que la aplicación parcial sea interesante, debemos hacer una diferencia entre el curry y la aplicación parcial y considerar la aplicación parcial como currying más evaluación bajo lambda.


Podría estar muy equivocado aquí, ya que no tengo una sólida formación en matemática teórica o programación funcional, pero de mi breve incursión en FP, parece que el curry tiende a convertir una función de N argumentos en N funciones de un argumento, mientras que la aplicación parcial [en la práctica] funciona mejor con funciones variad con un número indeterminado de argumentos. Sé que algunos de los ejemplos en respuestas anteriores desafían esta explicación, pero me ha ayudado más a separar los conceptos. Considere este ejemplo (escrito en CoffeeScript para ser conciso, le pido disculpas si se confunde más, pero pida una aclaración, si es necesario):

# partial application partial_apply = (func) -> args = [].slice.call arguments, 1 -> func.apply null, args.concat [].slice.call arguments sum_variadic = -> [].reduce.call arguments, (acc, num) -> acc + num add_to_7_and_5 = partial_apply sum_variadic, 7, 5 add_to_7_and_5 10 # returns 22 add_to_7_and_5 10, 11, 12 # returns 45 # currying curry = (func) -> num_args = func.length helper = (prev) -> -> args = prev.concat [].slice.call arguments return if args.length < num_args then helper args else func.apply null, args helper [] sum_of_three = (x, y, z) -> x + y + z curried_sum_of_three = curry sum_of_three curried_sum_of_three 4 # returns a function expecting more arguments curried_sum_of_three(4)(5) # still returns a function expecting more arguments curried_sum_of_three(4)(5)(6) # returns 15 curried_sum_of_three 4, 5, 6 # returns 15

Este es obviamente un ejemplo artificial, pero observe que la aplicación parcial de una función que acepta cualquier número de argumentos nos permite ejecutar una función pero con algunos datos preliminares. El curry de una función es similar pero nos permite ejecutar una función de N parámetros en partes hasta que, pero solo hasta, se tengan en cuenta todos los N parámetros.

Una vez más, esta es mi opinión de las cosas que he leído. Si alguien no está de acuerdo, agradecería un comentario sobre por qué en lugar de un voto directo inmediato. Además, si el CoffeeScript es difícil de leer, visite coffeescript.org, haga clic en "probar coffeescript" y pegue mi código para ver la versión compilada, lo que puede (con suerte) tener más sentido. ¡Gracias!


Tuve mucho esta pregunta mientras aprendía y desde entonces me la han preguntado muchas veces. La forma más sencilla en que puedo describir la diferencia es que ambos son iguales :) Permítanme explicarles ... obviamente hay diferencias.

Tanto la aplicación parcial como el curry implican proporcionar argumentos a una función, tal vez no todos al mismo tiempo. Un ejemplo bastante canónico es sumar dos números. En pseudocódigo (en realidad JS sin palabras clave), la función base puede ser la siguiente:

add = (x, y) => x + y

Si quisiera una función "addOne", podría aplicarlo parcialmente o curry:

addOneC = curry(add, 1) addOneP = partial(add, 1)

Ahora usarlos es claro:

addOneC(2) #=> 3 addOneP(2) #=> 3

Entonces, ¿cuál es la diferencia? Bueno, es sutil, pero la aplicación parcial implica proporcionar algunos argumentos y la función devuelta ejecutará la función principal en la siguiente invocación, mientras que Currying seguirá esperando hasta que tenga todos los argumentos necesarios:

curriedAdd = curry(add) # notice, no args are provided addOne = curriedAdd(1) # returns a function that can be used to provide the last argument addOne(2) #=> returns 3, as we want partialAdd = partial(add) # no args provided, but this still returns a function addOne = partialAdd(1) # oops! can only use a partially applied function once, so now we''re trying to add one to an undefined value (no second argument), and we get an error

En resumen, use la aplicación parcial para rellenar previamente algunos valores, sabiendo que la próxima vez que llame al método, se ejecutará, dejando indefinidos todos los argumentos no proporcionados; use el curry cuando desee devolver continuamente una función parcialmente aplicada tantas veces como sea necesario para cumplir con la firma de la función. Un último ejemplo artificial:

curriedAdd = curry(add) curriedAdd()()()()()(1)(2) # ugly and dumb, but it works partialAdd = partial(add) partialAdd()()()()()(1)(2) # second invocation of those 7 calls fires it off with undefined parameters

¡Espero que esto ayude!

ACTUALIZACIÓN: Algunos lenguajes o implementaciones de lib le permitirán pasar una aridad (número total de argumentos en la evaluación final) a la implementación parcial de la aplicación que puede combinar mis dos descripciones en un lío confuso ... pero en ese punto, las dos técnicas son Gran parte intercambiable.