He visto referencias a funciones curriculares en varios artículos y blogs, pero no puedo encontrar una buena explicación (¡o al menos una que tenga sentido!)
add x y = x+y
(curry) es diferente a add (x, y)=x+y
(sin curry)
He visto referencias a funciones curriculares en varios artículos y blogs, pero no puedo encontrar una buena explicación (¡o al menos una que tenga sentido!)
add x y = x+y
(curry) es diferente a add (x, y)=x+y
(sin curry)
Respuestas:
Curry es cuando desglosas una función que toma múltiples argumentos en una serie de funciones que cada uno toma solo un argumento. 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 suma. 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 suma.
add(3)(4);
var add3 = add(3);
add3(4);
La primera instrucción devuelve 7, como la instrucción add (3, 4). La segunda declaración define una nueva función llamada add3 que agregará 3 a su argumento. Esto es lo que algunas personas pueden llamar un cierre. La tercera declaración usa la operación add3 para agregar 3 a 4, produciendo nuevamente 7 como resultado.
[1, 2, 3, 4, 5]
que desea multiplicar por un número arbitrario. En Haskell, puedo escribir map (* 5) [1, 2, 3, 4, 5]
para multiplicar la lista completa por 5
, y así generar la lista [5, 10, 15, 20, 25]
.
map
debe ser una función que solo tome 1 argumento, un elemento de la lista. La multiplicación, como concepto matemático, es una operación binaria; Se necesitan 2 argumentos. Sin embargo, en Haskell *
es una función curry, similar a la segunda versión de add
en esta respuesta. El resultado de (* 5)
es una función que toma un solo argumento y lo multiplica por 5, y que nos permite usarlo con el mapa.
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: todo lo que necesita se necesitan funciones que tengan un argumento.
Entonces, ¿cómo lidias con algo que naturalmente expresarías como, por ejemplo f(x,y)
,? Bueno, lo tomas como equivalente a f(x)(y)
- f(x)
, llámalo g
, es una función, y le aplicas esa función y
. En otras palabras, solo tiene 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 resumida sobre esto, con muchos consejos útiles (probablemente incluyendo algunos con respecto a sus idiomas favoritos ;-), así como un tratamiento matemático un poco más riguroso.
div :: Integral a => a -> a -> a
, ¿notas esas flechas múltiples? "Mapear una función mapeando a a" es una lectura ;-) Usted podría utilizar un (único) argumento para tupla div
etc., pero eso sería muy anti-idiomática en Haskell.
Aquí hay un ejemplo concreto:
Suponga que tiene una función que calcula la fuerza gravitacional 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 de 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ía es otra función que toma solo dos argumentos y calcula la fuerza gravitacional de los objetos en la tierra. Esto se llama curry.
El curry es una transformación que se puede aplicar a las funciones para permitirles tomar un argumento menos que antes.
Por ejemplo, en F # puede definir una función así: -
let f x y z = x + y + z
Aquí la función f toma los parámetros x, y y z y los suma de manera que: -
f 1 2 3
Devuelve 6.
A partir de nuestra definición, podemos definir la función de curry para f: -
let curry f = fun x -> f x
Donde 'fun 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 solo 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
Lo que devuelve 6.
Este proceso a menudo se confunde con la 'aplicación de función parcial' que se puede definir así:
let papply f x = f x
Aunque podemos extenderlo a más de un parámetro, es decir: -
let papply2 f x y = f x y
let papply3 f x y z = f x y z
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, y como muestran los dos ejemplos anteriores, se implementa directamente en la definición de función F # estándar para que podamos lograr el resultado anterior así:
let f1 = f 1
f1 2 3
Lo que devolverá un resultado de 6.
En conclusión:-
La diferencia entre la aplicación de curry y la función parcial es que:
Curry 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 en ese argumento. Esto nos permite representar funciones con múltiples parámetros como una serie de funciones de argumento único . Ejemplo:-
let f x y z = 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 en los n argumentos especificados. Ejemplo:-
let f x y z = x + y + z
let f1 = f 1
let f2 = f 2
f1 2 3
6
f2 1 3
6
Puede ser una forma de usar funciones para hacer 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, 10
se pasa como x
;
let add = function(10){
return function(y){
return 10 + y
};
};
lo que significa que se nos devuelve esta función:
function(y) { return 10 + y };
Entonces cuando llamas
addTen();
realmente estás llamando:
function(y) { return 10 + y };
Entonces, si haces esto:
addTen(4)
es lo mismo que:
function(4) { return 10 + 4} // 14
Entonces nuestro addTen()
siempre agrega diez a lo que sea que pasemos. 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...
Ahora la pregunta obvia de seguimiento es ¿por qué querrías hacer eso? Convierte lo que era una operación entusiasta x + y
en una que se puede pasar perezosamente, lo que significa que podemos hacer al menos dos cosas: 1. operaciones costosas en caché 2. lograr abstracciones en el paradigma funcional.
Imagina que nuestra función curry se ve así:
let doTheHardStuff = function(x) {
let z = doSomethingComputationallyExpensive(x)
return function (y){
z + y
}
}
Podríamos llamar a esta función una vez, luego pasar el resultado para usarlo en muchos lugares, lo que significa que solo hacemos las cosas computacionalmente caras una vez:
let finishTheJob = doTheHardStuff(10)
finishTheJob(20)
finishTheJob(30)
Podemos obtener abstracciones de manera similar.
Una función currificada es una función de varios argumentos reescritos de tal manera 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 algunos de sus argumentos iniciales parcialmente aplicados.
map
una función f
sobre una lista de listas xss
, puede hacerlo map (map f) xss
.
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 la concatenación a través de + para evitar distracciones para los programadores que no son Python).
Edición para agregar:
Consulte http://docs.python.org/library/functools.html?highlight=partial#functools.partial , que también muestra la distinción de objeto parcial frente a función en la forma en que Python implementa esto.
Curry es traducir una función de invocable como f(a, b, c)
a invocable como f(a)(b)(c)
.
De lo contrario, curry es cuando desglosas una función que toma múltiples argumentos en una serie de funciones que forman parte de los argumentos.
Literalmente, el curry es una transformación de funciones: de una forma de llamar a otra. En JavaScript, generalmente hacemos un contenedor para mantener la función original.
El curry no llama a una función. Simplemente lo transforma.
Hagamos una función de curry que realice curry para funciones de dos argumentos. En otras palabras, curry(f)
para dos argumentos se f(a, b)
traduce enf(a)(b)
function curry(f) { // curry(f) does the currying transform
return function(a) {
return function(b) {
return f(a, b);
};
};
}
// usage
function sum(a, b) {
return a + b;
}
let carriedSum = curry(sum);
alert( carriedSum(1)(2) ); // 3
Como puede ver, la implementación es una serie de envoltorios.
curry(func)
es una envoltura function(a)
.sum(1)
, el argumento se guarda en el entorno léxico y se devuelve un nuevo contenedor function(b)
.sum(1)(2)
finalmente llama function(b)
proporcionando 2, y pasa la llamada a la suma original de múltiples argumentos.Si entiendes partial
que estás a medio camino. La idea departial
es preaplicar argumentos a una función y devolver una nueva función que solo quiere 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 muy claras:
(defn add [a b] (+ a b))
Puede saber que la inc
función simplemente agrega 1 a cualquier número que se pase.
(inc 7) # => 8
Vamos a construirlo nosotros mismos usando partial
:
(def inc (partial add 1))
Aquí devolvemos otra función que tiene 1 cargado en el primer argumento de add
. Como add
toma dos argumentos, la nueva inc
función solo quiere el b
argumento, no 2 argumentos como antes ya que 1 ya ha sido parcialmente aplicado . Por partial
lo tanto, es una herramienta para crear nuevas funciones con los valores predeterminados presupuestados. Es por eso que en un lenguaje funcional las funciones a menudo ordenan argumentos de general a específico. Esto facilita la reutilización de tales funciones a partir de las cuales construir otras funciones.
Ahora imagine si el lenguaje fuera lo suficientemente inteligente como para comprender introspectivamente que add
quisiera dos argumentos. Cuando le pasamos un argumento, en lugar de rechazarlo, ¿qué pasa si la función aplica parcialmente el argumento lo pasamos en nuestro nombre entendiendo que probablemente teníamos la intención de proporcionar el otro argumento más tarde? Entonces podríamos definir inc
sin usar explícitamente partial
.
(def inc (add 1)) #partial is implied
Así se comportan algunos idiomas. Es excepcionalmente útil cuando se desea componer funciones en transformaciones más grandes. Esto llevaría a uno a los transductores.
Encontré este artículo, y el artículo al que hace referencia, útil, para comprender mejor el curry: 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 un parámetro.
Esto es útil porque no tiene que asumir cuántos parámetros se pasarán, por lo que no necesita funciones de 2 parámetros, 3 parámetros y 4 parámetros.
Como todas las demás respuestas, curry ayuda a crear funciones parcialmente aplicadas. Javascript no proporciona soporte nativo para curry automático. Por lo tanto, los ejemplos proporcionados anteriormente pueden no ayudar en la codificación práctica. Hay un excelente ejemplo en Vivescript (que esencialmente compila 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 ha dado menos argumentos sin, el script en vivo genera una nueva función curry para usted (doble)
Curry puede simplificar tu código. Esta es una de las principales razones para usar esto. El curry es un proceso de convertir 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, utilizando la propiedad de cierre (cierre), para almacenarlos en otra función y tratarla 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 un parámetro a la vez, lo que también puede mejorar la flexibilidad y la legibilidad del programa. Esto también hace que el programa sea más manejable. También dividir el código en partes más pequeñas lo haría fácil de reutilizar.
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 que el código complejo sea ordenado y manejar métodos no sincronizados, etc.
Una función curry se aplica a múltiples listas de argumentos, en lugar de solo una.
Aquí hay una función regular, no curry, que agrega dos parámetros Int, x e y:
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 es curry. 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 invocas curriedSum
, en realidad obtienes dos invocaciones de funciones tradicionales consecutivas. La invocación de la primera 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 curriedSum
que haría la primera invocación de función tradicional :
scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int
Aplicar 1 a la primera función, en otras palabras, invocar la primera función y pasar 1, produce la segunda función:
scala> val second = first(1)
second: (Int) => Int = <function1>
Aplicar 2 a la segunda función produce el resultado:
scala> second(2)
res6: Int = 3
Un ejemplo de curry sería cuando tenga funciones que solo conoce uno de los parámetros en este 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 devolución de llamada al enviarlo, performAsyncRequest(_:)
tendrá que crear otro lambda / cierre para enviarlo a la función.
func callback
volviendo a sí mismo? Se llama @ callback(str)
entonces let callback = callback(str)
, la devolución de llamada es solo el valor de retorno defunc callback
func callback(_:data:)
acepta dos parámetros, aquí solo le doy uno, el String
, por lo que está esperando el siguiente ( NSData
), esta es la razón por la cual ahora let callback
hay otra función esperando que se pasen los datos
Aquí está el ejemplo de genérico y la versión más corta para la función de curry con n no. de params.
const add = a => b => b ? add(a + b) : a;
const add = a => b => b ? add(a + b) : a;
console.log(add(1)(2)(3)(4)());
Aquí puede encontrar una explicación simple de la implementación del currículum en C #. En los comentarios, he intentado mostrar cómo curry puede ser útil:
public static class FuncExtensions {
public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
{
return x1 => x2 => func(x1, x2);
}
}
//Usage
var add = new Func<int, int, int>((x, y) => x + y).Curry();
var func = add(1);
//Obtaining the next parameter here, calling later the func with next parameter.
//Or you can prepare some base calculations at the previous step and then
//use the result of those calculations when calling the func multiple times
//with different input parameters.
int result = func(1);
El curry es una de las funciones de orden superior de Java Script.
El curry es una función de muchos argumentos que se reescribe de tal manera que toma el primer argumento y devuelve una función que a su vez usa los argumentos restantes y devuelve el valor.
¿Confuso?
Veamos un ejemplo,
function add(a,b)
{
return a+b;
}
add(5,6);
Esto es similar a la siguiente función de curry,
function add(a)
{
return function(b){
return a+b;
}
}
var curryAdd = add(5);
curryAdd(6);
Entonces, ¿qué significa este código?
Ahora lea la definición nuevamente,
El curry es una función de muchos argumentos que se reescribe de tal manera que toma el primer argumento y devuelve una función que a su vez usa los argumentos restantes y devuelve el valor.
¿Sigo confundido? Déjame explicarte en profundidad!
Cuando llamas a esta función,
var curryAdd = add(5);
Te devolverá una función como esta,
curryAdd=function(y){return 5+y;}
Entonces, esto se llama funciones de orden superior. Es decir, invocar una función por turnos devuelve otra función es una definición exacta para la función de orden superior. Esta es la mayor ventaja para la leyenda, Java Script. Así que vuelve al curry
Esta línea pasará el segundo argumento a la función curryAdd.
curryAdd(6);
que a su vez resulta,
curryAdd=function(6){return 5+6;}
// Which results in 11
Espero que entiendas el uso de curry aquí. Entonces, llegando a las ventajas,
¿Por qué curry?
Hace uso de la reutilización del código. Menos código, menos error. ¿Puede preguntar cómo es menos código?
Puedo probarlo con las nuevas funciones de flecha de ECMA script 6.
¡Si! ECMA 6, nos proporciona la maravillosa característica llamada funciones de flecha,
function add(a)
{
return function(b){
return a+b;
}
}
Con la ayuda de la función de flecha, podemos escribir la función anterior de la siguiente manera,
x=>y=>x+y
¿Guay, verdad?
¡Entonces, menos código y menos errores!
Con la ayuda de estas funciones de orden superior, se puede desarrollar fácilmente un código libre de errores.
¡Te reto!
Espero que hayas entendido lo que es curry. No dude en comentar aquí si necesita alguna aclaración.
Gracias, que tengas un buen día!
Hay un ejemplo de "Curry en ReasonML".
let run = () => {
Js.log("Curryed function: ");
let sum = (x, y) => x + y;
Printf.printf("sum(2, 3) : %d\n", sum(2, 3));
let per2 = sum(2);
Printf.printf("per2(3) : %d\n", per2(3));
};
curry
yuncurry
de Haskell. Lo importante aquí es que estos isomorfismos se arreglan de antemano y, por lo tanto, están "incorporados" en el lenguaje.