Pregunta

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

¿Fue útil?

Solución

El curry es cuando se divide 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 suma.Ahora 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 declaración devuelve 7, como la declaració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 llaman un cierre.La tercera declaración utiliza la operación add3 para sumar 3 a 4, produciendo nuevamente 7 como resultado.

Otros consejos

En un álgebra de funciones, tratar con funciones que toman múltiples argumentos (o un argumento equivalente que sea una N-tupla) es algo poco elegante, pero, como demostraron Moses Schönfinkel (y, de forma independiente, Haskell Curry), no es necesario:todo lo que necesitas son funciones que tomen un argumento.

Entonces, ¿cómo manejas algo que naturalmente expresarías como, digamos, f(x,y)?Bueno, tomas 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, sólo tienes funciones que toman un argumento, pero algunas de esas funciones devuelven otras funciones (que TAMBIÉN toman un argumento ;-).

Como siempre, Wikipedia tiene una buena entrada resumida sobre esto, con muchos consejos útiles (probablemente incluidos algunos relacionados con sus idiomas favoritos;-), así como un tratamiento matemático ligeramente más riguroso.

He aquí un ejemplo concreto:

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

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

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

Por ejemplo, en F# puedes 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 modo que:

f 1 2 3

Devuelve 6.

Por lo tanto, a partir de nuestra definición podemos definir la función curry para f:-

let curry f = fun x -> f x

Donde 'fun x -> f x' 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

Luego podemos hacer lo siguiente: -

let f1 = curryf 1

Lo que nos proporciona una función f1 que es equivalente a f1 y z = 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 menos y, como muestran los dos ejemplos anteriores, se implementa directamente en la definición de función estándar de F# para que podamos lograr el resultado anterior de la siguiente manera:

let f1 = f 1
f1 2 3

Lo que arrojará un resultado de 6.

En conclusión:-

La diferencia entre curry y aplicación de función parcial es que: -

Currying toma una función y proporciona una nueva función que acepta un solo 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 un solo argumento..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 utilizar funciones para crear 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 ejecuta el 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 suma diez a todo lo que pasamos.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...

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

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 use la concatenación a través de + para evitar distracciones para los programadores que no usan Python).

Editando para agregar:

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

Si entiendes partial estás a mitad de camino.La idea de partial es aplicar previamente 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 que estés consciente de que el inc La función simplemente suma 1 a cualquier número que se pase.

(inc 7) # => 8

Construyámoslo 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 el nuevo inc La función sólo quiere el b argumento: no 2 argumentos como antes, ya que 1 ya ha sido parcialmente aplicado.De este modo partial es una herramienta desde la que crear nuevas funciones con valores predeterminados presuministrados.Es por eso que en un lenguaje funcional las funciones suelen ordenar los argumentos de lo general a lo específico.Esto hace que sea más fácil reutilizar dichas funciones para construir otras funciones.

Ahora imagina si el lenguaje fuera lo suficientemente inteligente como para entender introspectivamente que add Quería dos argumentos.Cuando le pasamos un argumento, en lugar de resistirnos, ¿qué pasaría si la función aplicara parcialmente el argumento? Le pasamos en nuestro nombre, entendiendo que probablemente queríamos proporcionar el otro argumento más adelante.Entonces podríamos definir inc sin usar explícitamente partial.

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

Así es como 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.

Este artículo, y el artículo al que hace referencia, me parecieron útiles para comprender mejor el curry:http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx

Como mencionaron los demás, 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.

Curry puede simplificar su código.Esta es una de las principales razones para utilizarlo.Currying 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, 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 un parámetro a la vez, lo que también puede mejorar la flexibilidad y legibilidad del programa.Esto también hace que el programa sea más manejable.Además, dividir el código en partes más pequeñas facilitaría su reutilización.

Por ejemplo:

function curryMinus(x) 
{
  return function(y) 
  {
    return x - y;
  }
}

var minus5 = curryMinus(1);
minus5(3);
minus5(5);

Yo 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 para manejar métodos no sincronizados, etc.

Currying es traducir una función de invocable como f(a, b, c) en invocable como f(a)(b)(c).

De lo contrario, curry es cuando se divide 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 manera de llamar a otra.En JavaScript, normalmente creamos 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 f(a, b) lo traduce a f(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.

  • El resultado de curry(func) es un envoltorio function(a).
  • Cuando se llama así sum(1), el argumento se guarda en el entorno léxico y se devuelve un nuevo contenedor function(b).
  • Entonces sum(1)(2) finalmente llama function(b) proporcionando 2, y pasa la llamada a la suma de múltiples argumentos original.

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

Aquí hay una función regular y no corredera, 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 al curry.En lugar de una lista de dos parámetros INT, aplica esta función a dos listas de un parámetro int cada uno:

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 solo parámetro INT nombrado 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 la primera función de la función tradicional de la invocación de curriedSum haría:

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 en 1, anima la segunda función:

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

Aplicando 2 a la segunda función se obtiene el resultado:

scala> second(2)
res6: Int = 3

Un ejemplo de curry sería cuando tienes funciones y solo conoces 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 cuando lo envía a performAsyncRequest(_:) Tendrías que crear otro lambda/cierre para enviarlo a la función.

Como todas las demás respuestas, el curry ayuda a crear funciones parcialmente aplicadas.Javascript no proporciona soporte nativo para el curry automático.Por lo tanto, es posible que los ejemplos proporcionados anteriormente no ayuden en la codificación práctica.Hay un ejemplo excelente en livescript (que esencialmente se compila en 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 proporcionado menos argumentos, Livescript genera una nueva función de curry para usted (doble)

Aquí puede encontrar una explicación sencilla de la implementación del curry en C#.En los comentarios, he intentado mostrar lo útil que puede ser el curry:

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);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top