¿Qué es ‘Currying’?

He visto referencias a funciones con curry en varios artículos y blogs, pero no puedo encontrar una buena explicación (¡o al menos una que tenga sentido!)

Currying es cuando descompone una función que toma múltiples argumentos en una serie de funciones que forman parte de los argumentos. Aquí hay un ejemplo en JavaScript:

 function add (a, b) { return a + b; } add(3, 4); // returns 7 

Esta es una función que toma dos argumentos, a y b, y devuelve su sum. Ahora vamos a curry esta función:

 function add (a) { return function (b) { return a + b; } } 

Esta es una función que toma un argumento, a, y devuelve una función que toma otro argumento, b, y esa función devuelve su sum.

 add(3)(4); var add3 = add(3); add3(4); 

La primera instrucción devuelve 7, como la instrucción add (3, 4). La segunda statement define una nueva función llamada add3 que agregará 3 a su argumento. Esto es lo que algunas personas pueden llamar cierre. La tercera instrucción usa la operación add3 para agregar 3 a 4, nuevamente produciendo 7 como resultado.

En un álgebra de funciones, tratar con funciones que toman múltiples argumentos (o un argumento equivalente que es una N-tupla) es algo poco elegante, pero, como lo demostró Moses Schönfinkel (e, independientemente, Haskell Curry), no es necesario: todos ustedes necesidad son funciones que toman un argumento.

Entonces, ¿cómo manejas algo que expressías naturalmente como, por ejemplo, f(x,y) ? Bien, toma eso como equivalente a f(x)(y)f(x) , llámalo g , es una función, y aplicas esa función a y . En otras palabras, solo tienes funciones que toman un argumento, pero algunas de esas funciones devuelven otras funciones (que TAMBIÉN toman un argumento ;-).

Como de costumbre, wikipedia tiene una buena entrada de resumen acerca de esto, con muchos consejos útiles (probablemente incluyendo aquellos con respecto a tus idiomas favoritos 😉 así como un tratamiento matemático un poco más riguroso.

Aquí hay un ejemplo concreto:

Supongamos que tiene una función que calcula la fuerza gravitatoria que actúa sobre un objeto. Si no conoce la fórmula, puede encontrarla aquí . Esta función toma los tres parámetros necesarios como argumentos.

Ahora, estando en la tierra, solo quieres calcular las fuerzas para los objetos en este planeta. En un lenguaje funcional, podría pasar la masa de la tierra a la función y luego evaluarla parcialmente. Lo que obtendrías es otra función que toma solo dos argumentos y calcula la fuerza gravitacional de los objetos en la tierra. Esto se llama currying.

Currying es una transformación que se puede aplicar a las funciones para permitirles tomar un argumento menos que anteriormente.

Por ejemplo, en F # puede definir una función así:

 let fxyz = x + y + z 

Aquí la función f toma los parámetros x, y y z y los sum de manera que:

 f 1 2 3 

Devuelve 6.

Desde nuestra definición, podemos por lo tanto definir la función de curry para f:

 let curry f = fun x -> fx 

Donde ‘diversión x -> fx’ es una función lambda equivalente a x => f (x) en C #. Esta función ingresa la función que desea curry y devuelve una función que toma un único argumento y devuelve la función especificada con el primer argumento establecido en el argumento de entrada.

Usando nuestro ejemplo anterior, podemos obtener un curry de f así:

 let curryf = curry f 

Entonces podemos hacer lo siguiente:

 let f1 = curryf 1 

Lo que nos proporciona una función f1 que es equivalente a f1 yz = 1 + y + z. Esto significa que podemos hacer lo siguiente:

 f1 2 3 

Que devuelve 6.

Este proceso a menudo se confunde con la ‘aplicación de función parcial’ que se puede definir así:

 let papply fx = fx 

Aunque podemos extenderlo a más de un parámetro, es decir:

 let papply2 fxy = fxy let papply3 fxyz = fxyz etc. 

Una aplicación parcial tomará la función y los parámetros y devolverá una función que requiere uno o más parámetros menores, y como se muestra en los dos ejemplos anteriores, se implementa directamente en la definición de función F # estándar, por lo que podríamos lograr el resultado anterior así:

 let f1 = f 1 f1 2 3 

Que devolverá un resultado de 6.

En conclusión:-

La diferencia entre currying y la aplicación de función parcial es que:

Currying toma una función y proporciona una nueva función que acepta un único argumento y devuelve la función especificada con su primer argumento establecido para ese argumento. Esto nos permite representar funciones con múltiples parámetros como una serie de funciones de argumento único . Ejemplo:-

 let fxyz = x + y + z let curryf = curry f let f1 = curryf 1 let f2 = curryf 2 f1 2 3 6 f2 1 3 6 

La aplicación de función parcial es más directa: toma una función y uno o más argumentos y devuelve una función con los primeros n argumentos establecidos a los n argumentos especificados. Ejemplo:-

 let fxyz = x + y + z let f1 = f 1 let f2 = f 2 f1 2 3 6 f2 1 3 6 

Una función al curry es una función de varios argumentos reescritos, de modo que acepta el primer argumento y devuelve una función que acepta el segundo argumento, y así sucesivamente. Esto permite que las funciones de varios argumentos tengan parcialmente aplicados algunos de sus argumentos iniciales.

Puede ser una forma de usar funciones para realizar otras funciones.

En javascript:

 let add = function(x){ return function(y){ return x + y }; }; 

Nos permitiría llamarlo así:

 let addTen = add(10); 

Cuando esto se ejecuta, el 10 pasa como x ;

 let add = function(10){ return function(y){ return 10 + y }; }; 

lo que significa que se devuelve esta función:

 function(y) { 10 + y }; 

Entonces cuando llamas

  addTen(); 

realmente estás llamando:

  function(y) { 10 + y }; 

Entonces, si haces esto:

  addTen(4) 

es lo mismo que:

 function(4) { 10 + 4} // 14 

Entonces, addTen() siempre agrega diez a lo que pase. Podemos hacer funciones similares de la misma manera:

 let addTwo = add(2) // addTwo(); will add two to whatever you pass in let addSeventy = add(70) // ... and so on... 

Aquí hay un ejemplo de juguete en Python:

 >>> from functools import partial as curry >>> # Original function taking three parameters: >>> def display_quote(who, subject, quote): print who, 'said regarding', subject + ':' print '"' + quote + '"' >>> display_quote("hoohoo", "functional languages", "I like Erlang, not sure yet about Haskell.") hoohoo said regarding functional languages: "I like Erlang, not sure yet about Haskell." >>> # Let's curry the function to get another that always quotes Alex... >>> am_quote = curry(display_quote, "Alex Martelli") >>> am_quote("currying", "As usual, wikipedia has a nice summary...") Alex Martelli said regarding currying: "As usual, wikipedia has a nice summary..." 

(Simplemente usando concatenación a través de + para evitar distracciones para progtwigdores que no sean Python).

Edición para agregar:

Consulte http://docs.python.org/library/functools.html?highlight=partial#functools.partial , que también muestra la distinción objeto a objeto parcial en la forma en que Python implementa esto.

Encontré este artículo, y el artículo al que hace referencia, útil, para comprender mejor el currying: http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx

Como los otros mencionaron, es solo una forma de tener una función de parámetro.

Esto es útil porque no tiene que suponer cuántos parámetros se pasarán, por lo que no necesita un parámetro 2, 3 parámetros y 4 funciones de parámetros.

Si entiendes partial estarás a mitad de camino. La idea de partial es preelentar argumentos a una función y devolver una nueva función que solo desea los argumentos restantes. Cuando se llama a esta nueva función, incluye los argumentos precargados junto con los argumentos que se le proporcionaron.

En Clojure + es una función, pero para dejar las cosas absolutamente claras:

 (defn add [ab] (+ ab)) 

Puede ser consciente de que la función inc simplemente agrega 1 al número que haya pasado.

 (inc 7) # => 8 

Vamos a construirlo nosotros mismos usando partial :

 (def inc (partial add 1)) 

Aquí devolvemos otra función que tiene 1 cargada en el primer argumento de add . Como add toma dos argumentos, la nueva función inc solo quiere el argumento b , no 2 argumentos como antes, ya que 1 ya se ha aplicado parcialmente . Por lo tanto, partial es una herramienta a partir de la cual crear nuevas funciones con los valores por defecto presupuestados. Es por eso que en un lenguaje funcional las funciones a menudo ordenan argumentos de general a específico. Esto hace que sea más fácil reutilizar tales funciones para construir otras funciones.

Ahora imagine si el lenguaje fue lo suficientemente inteligente como para entender introspectivamente que add quería dos argumentos. Cuando le pasamos un argumento, en lugar de rehusar, ¿qué pasaría si la función aplicara parcialmente el argumento que lo aprobásemos en nuestro nombre entendiendo que probablemente pensamos proporcionar el otro argumento más adelante? Podríamos entonces definir inc sin usar explícitamente partial .

 (def inc (add 1)) #partial is implied 

Esta es la forma en que se comportan algunos idiomas. Es excepcionalmente útil cuando uno desea componer funciones en transformaciones más grandes. Esto llevaría a uno a los transductores.

Una función al curry se aplica a múltiples listas de argumentos, en lugar de solo una.

Aquí hay una función regular, no currículum, que agrega dos parámetros Int, xey:

 scala> def plainOldSum(x: Int, y: Int) = x + y plainOldSum: (x: Int,y: Int)Int scala> plainOldSum(1, 2) res4: Int = 3 

Aquí hay una función similar que está curried. En lugar de una lista de dos parámetros Int, aplica esta función a dos listas de un parámetro Int cada una:

 scala> def curriedSum(x: Int)(y: Int) = x + y curriedSum: (x: Int)(y: Int)Intscala> second(2) res6: Int = 3 scala> curriedSum(1)(2) res5: Int = 3 

Lo que sucede aquí es que cuando curriedSum , en realidad obtienes dos invocaciones de funciones tradicionales curriedSum . La primera invocación de función toma un único parámetro Int llamado x , y devuelve un valor de función para la segunda función. Esta segunda función toma el parámetro Int y .

Aquí hay una función llamada first que hace en espíritu lo que haría la primera invocación de función tradicional de curriedSum :

 scala> def first(x: Int) = (y: Int) => x + y first: (x: Int)(Int) => Int 

Al aplicar 1 a la primera función, es decir, al invocar la primera función y al pasar a 1, se produce la segunda función:

 scala> val second = first(1) second: (Int) => Int =  

Aplicar 2 a la segunda función produce el resultado:

 scala> second(2) res6: Int = 3 

Un ejemplo de currying sería cuando tienes funciones, solo conoces uno de los parámetros en el momento:

Por ejemplo:

 func aFunction(str: String) { let callback = callback(str) // signature now is `NSData -> ()` performAsyncRequest(callback) } func callback(str: String, data: NSData) { // Callback code } func performAsyncRequest(callback: NSData -> ()) { // Async code that will call callback with NSData as parameter } 

Aquí, dado que no conoce el segundo parámetro para la callback al enviarlo a performAsyncRequest(_:) , tendría que crear otra lambda / closure para enviarlo a la función.

Para dar un ejemplo del mundo real (y potencialmente útil) de currying mira cómo puedes hacer llamadas al servidor en javascript con la biblioteca de búsqueda

  Get(url) { let fullUrl = toFullUrl(url); let promise = getPromiseForFetchWithToken((token) => { let headers = Object.assign( getDefaultHeaders(token), jsonHeaders); let config = { method: "GET", headers: headers }; return fetch(fullUrl, config); }); return promise; } 

Donde getPromiseForFetchWithToken es una función getPromiseForFetchWithToken que devuelve una Promise con el resultado de la búsqueda, que se muestra a continuación:

 function getPromiseForFetchWithToken(tokenConsumingFetch) { function resolver(resolve, reject) { let token = localStorage.getItem("token"); tokenConsumingFetch(token) .then(checkForError) .then((response) => { if (response) resolve(response); }) .catch(reject); } var promise = new Promise(resolver); return promise; } 

Esto le permite esperar la llamada de la función Get y luego manejar adecuadamente el valor de retorno independientemente de lo que sea, puede volver a utilizar la función getPromiseForFetchWithToken cualquier lugar que necesite para hacer una llamada al servidor que necesite incluir un token de portador. (Poner, Eliminar, Publicar, etc.)

Como todas las otras respuestas, currying ayuda a crear funciones parcialmente aplicadas. Javascript no proporciona soporte nativo para el currying automático. Por lo tanto, los ejemplos proporcionados anteriormente pueden no ser útiles en la encoding práctica. Hay un excelente ejemplo en livescript (que esencialmente comstack a js) http://livescript.net/

 times = (x, y) --> x * y times 2, 3 #=> 6 (normal use works as expected) double = times 2 double 5 #=> 10 

En el ejemplo anterior cuando has dado menos argumentos, livescript genera una nueva función curried para ti (doble)

Curry puede simplificar tu código. Esta es una de las principales razones para usar esto. Currying es un proceso de conversión de una función que acepta n argumentos en n funciones que aceptan solo un argumento.

El principio es pasar los argumentos de la función pasada, usando la propiedad de cierre (cierre), almacenarlos en otra función y tratarlos como un valor de retorno, y estas funciones forman una cadena, y los argumentos finales se pasan para completar la operacion.

El beneficio de esto es que puede simplificar el procesamiento de parámetros al tratar con un parámetro a la vez, lo que también puede mejorar la flexibilidad y legibilidad del progtwig. Esto también hace que el progtwig sea más manejable. También dividir el código en piezas más pequeñas lo haría reutilizable.

Por ejemplo:

 function curryMinus(x) { return function(y) { return x - y; } } var minus5 = curryMinus(1); minus5(3); minus5(5); 

También puedo hacer …

 var minus7 = curryMinus(7); minus7(3); minus7(5); 

Esto es muy bueno para hacer código complejo y manejo de métodos no sincronizados, etc.