f# functional-programming currying

F#curried function



functional-programming currying (6)

Alguien tiene un ejemplo decente, preferiblemente práctico / útil, ¿podrían publicar demostrando el concepto?


(Editar: un pequeño Ocaml FP Koan para comenzar las cosas)

El Koan de Currying (Un koan sobre la comida, no se trata de comida)

Un estudiante vino a Jacques Garrigue y dijo: "No entiendo para qué sirve el curry". Jacques respondió: "Dime tu comida favorita y tu postre favorito". El desconcertado estudiante respondió que le gustaban los okonomiyaki y los kanten, pero mientras que su restaurante favorito servía excelente okonomiyaki, sus kanten siempre le producían dolor de estómago a la mañana siguiente. Así que Jacques llevó al estudiante a comer en un restaurante que servía okonomiyaki tan bueno como el favorito del alumno, y luego lo llevó al otro lado de la ciudad a una tienda que hacía excelentes kanten donde el estudiante aplicaba alegremente el resto de su apetito. El estudiante estaba saciado, pero no estaba iluminado ... hasta la mañana siguiente cuando se despertó y su estómago se sentía bien.

Mis ejemplos cubrirán su uso para la reutilización y encapsulación del código. Esto es bastante obvio una vez que los veas y debería darte un ejemplo concreto y simple que puedas aplicar en numerosas situaciones.

Queremos hacer un mapa sobre un árbol. Esta función podría curry y aplicarse a cada nodo si necesita más de un argumento, ya que estaríamos aplicando uno en el nodo ya que es el argumento final. No tiene que curry, pero escribir otra función (suponiendo que esta función se use en otras instancias con otras variables) sería un desperdicio.

type ''a tree = E of ''a | N of ''a * ''a tree * ''a tree let rec tree_map f tree = match tree with | N(x,left,right) -> N(f x, tree_map f left, tree_map f right) | E(x) -> E(f x) let sample_tree = N(1,E(3),E(4) let multiply x y = x * y let sample_tree2 = tree_map (multiply 3) sample_tree

pero esto es lo mismo que:

let sample_tree2 = tree_map (fun x -> x * 3) sample_tree

Entonces este simple caso no es convincente. Sin embargo, realmente es así, y poderoso una vez que usas el lenguaje más y te encuentras naturalmente con estas situaciones. El otro ejemplo con algunos códigos de reutilización como currying. Una relación de recurrencia para crear números primos . Horrible cantidad de similitudes allí:

let rec f_recurrence f a seed n = match n with | a -> seed | _ -> let prev = f_recurrence f a seed (n-1) in prev + (f n prev) let rowland = f_recurrence gcd 1 7 let cloitre = f_recurrence lcm 1 1 let rowland_prime n = (rowland (n+1)) - (rowland n) let cloitre_prime n = ((cloitre (n+1))/(cloitre n)) - 1

Ok, ahora rowland y cloitre son funciones curried, ya que tienen variables libres, y podemos obtener cualquier índice de su secuencia sin saber o preocuparse por recurrencia.


¿Sabes que puedes mapear una función sobre una lista? Por ejemplo, asignar una función para agregar una a cada elemento de una lista:

> List.map ((+) 1) [1; 2; 3];; val it : int list = [2; 3; 4]

Esto ya está usando currying porque el operador (+) se usó para crear una función para agregar uno a su argumento, pero puede exprimir un poco más de este ejemplo alterándolo para mapear la misma función de una lista de listas:

> List.map (List.map ((+) 1)) [[1; 2]; [3]];; val it : int list = [[2; 3]; [4]]

Sin currying no podría aplicar parcialmente estas funciones y tendría que escribir algo como esto en su lugar:

> List.map((fun xs -> List.map((fun n -> n + 1), xs)), [[1; 2]; [3]]);; val it : int list = [[2; 3]; [4]]


Currying describe el proceso de transformar una función con múltiples argumentos en una cadena de funciones de argumento único. Ejemplo en C #, para una función de tres argumentos:

Func<T1, Func<T2, Func<T3, T4>>> Curry<T1, T2, T3, T4>(Func<T1, T2, T3, T4> f) { return a => b => c => f(a, b, c); } void UseACurriedFunction() { var curryCompare = Curry<string, string, bool, int>(String.Compare); var a = "SomeString"; var b = "SOMESTRING"; Console.WriteLine(String.Compare(a, b, true)); Console.WriteLine(curryCompare(a)(b)(true)); //partial application var compareAWithB = curryCompare(a)(b); Console.WriteLine(compareAWithB(true)); Console.WriteLine(compareAWithB(false)); }

Ahora, el argumento booleano probablemente no sea el argumento que probablemente querría dejar abierto con una aplicación parcial. Esta es una razón por la cual el orden de los argumentos en las funciones F # puede parecer un poco extraño al principio. Vamos a definir una función de curry C # diferente:

Func<T3, Func<T2, Func<T1, T4>>> BackwardsCurry<T1, T2, T3, T4>(Func<T1, T2, T3, T4> f) { return a => b => c => f(c, b, a); }

Ahora, podemos hacer algo un poco más útil:

void UseADifferentlyCurriedFunction() { var curryCompare = BackwardsCurry<string, string, bool, int>(String.Compare); var caseSensitiveCompare = curryCompare(false); var caseInsensitiveCompare = curryCompare(true); var format = Curry<string, string, string, string>(String.Format)("Results of comparing {0} with {1}:"); var strings = new[] {"Hello", "HELLO", "Greetings", "GREETINGS"}; foreach (var s in strings) { var caseSensitiveCompareWithS = caseSensitiveCompare(s); var caseInsensitiveCompareWithS = caseInsensitiveCompare(s); var formatWithS = format(s); foreach (var t in strings) { Console.WriteLine(formatWithS(t)); Console.WriteLine(caseSensitiveCompareWithS(t)); Console.WriteLine(caseInsensitiveCompareWithS(t)); } } }

¿Por qué estos ejemplos están en C #? Porque en F #, las declaraciones de función están currificadas de forma predeterminada. Usualmente no necesita curry funciones; ya están curry. La principal excepción a esto son los métodos de framework y otras funciones sobrecargadas, que toman una tupla que contiene sus múltiples argumentos. Por lo tanto, es posible que desee curry tales funciones, y, de hecho, me encontré con esta pregunta cuando estaba buscando una función de biblioteca que haría esto. Supongo que falta (si es que lo es) porque es bastante trivial implementar:

let curry f a b c = f(a, b, c) //overload resolution failure: there are two overloads with three arguments. //let curryCompare = curry String.Compare //This one might be more useful; it works because there''s only one 3-argument overload let backCurry f a b c = f(c, b, a) let intParse = backCurry Int32.Parse let intParseCurrentCultureAnyStyle = intParse CultureInfo.CurrentCulture NumberStyles.Any let myInt = intParseCurrentCultureAnyStyle "23" let myOtherInt = intParseCurrentCultureAnyStyle "42"

Para solucionar el error con String.Compare, ya que, por lo que puedo ver, no hay forma de especificar qué sobrecarga de 3 argumentos elegir, puede usar una solución no general:

let curryCompare s1 s2 (b:bool) = String.Compare(s1, s2, b) let backwardsCurryCompare (b:bool) s1 s2 = String.Compare(s1, s2, b)

No entraré en detalles sobre los usos de la aplicación de función parcial en F # porque las otras respuestas ya lo han cubierto.


Di un buen ejemplo de cómo simular el currículum en C # en mi blog . Lo esencial es que puede crear una función que se cierre sobre un parámetro (en mi ejemplo, crear una función para calcular el impuesto a las ventas cerrado sobre el valor de un municipio dado) a partir de una función multiparamétrica existente.

Lo que es atractivo aquí es que en lugar de tener que hacer una función separada específicamente para calcular el impuesto a las ventas en el condado de Cook, puede crear (y reutilizar) la función dinámicamente en tiempo de ejecución.


Es un proceso bastante simple. Tome una función, enlace uno de sus argumentos y devuelva una nueva función. Por ejemplo:

let concatStrings left right = left + right let makeCommandPrompt= appendString "c:/> "

Ahora, currying la función simple concatStrings, puede agregar fácilmente un símbolo del sistema estilo DOS al frente de cualquier cadena. ¡Realmente util!

De acuerdo, no realmente. Un caso más útil que encuentro es cuando quiero tener una función make que me devuelva datos de la misma manera.

let readDWORD array i = array[i] | array[i + 1] << 8 | array[i + 2] << 16 | array[i + 3] << 24 //I''ve actually used this function in Python.

Lo conveniente de esto es que en lugar de crear una clase completa para este tipo de cosas, llamar al constructor, llamar a obj.readDWORD (), solo tienes una función que no se puede mutar debajo de ti.


Si bien los ejemplos anteriores respondieron la pregunta, aquí hay dos ejemplos más simples de cómo Currying puede ser beneficioso para la programación F #.

open System.IO let appendFile (fileName : string) (text : string) = let file = new StreamWriter(fileName, true) file.WriteLine(text) file.Close() // Call it normally appendFile @"D:/Log.txt" "Processing Event X..." // If you curry the function, you don''t need to keep specifying the // log file name. let curriedAppendFile = appendFile @"D:/Log.txt" // Adds data to "Log.txt" curriedAppendFile "Processing Event Y..."

¡Y no olvides que puedes curry con la familia de funciones de Printf! En la versión curried, fíjate en la distinta falta de una lambda.

// Non curried, Prints 1 2 3 List.iter (fun i -> printf "%d " i) [1 .. 3];; // Curried, Prints 1 2 3 List.iter (printfn "%d ") [1 .. 3];;