2011-02-03 27 views
28

Hay un montón de tutoriales sobre cómo curry funciones, y tantas preguntas aquí en stackoverflow. Sin embargo, después de leer The Little Schemer, varios libros, tutoriales, publicaciones en blogs e hilos de stackoverflow, todavía no sé la respuesta a la simple pregunta: "¿Qué sentido tiene currying?" Entiendo cómo curry una función, simplemente no el "¿por qué?" Detrás de eso.¿Uso práctico de funciones al curry?

Podría alguien explicarme por los usos prácticos de funciones al curry (fuera de las lenguas que sólo permiten un argumento para cada función, donde la necesidad de utilizar currificación Por supuesto, es bastante evidente.)

edición: Tomando en cuenta algunos ejemplos de TLS, ¿cuál es el beneficio de

(define (action kind) 
    (lambda (a b) 
     (kind a b))) 

en contraposición a

(define (action kind a b) 
    (kind a b)) 

Solo puedo ver más código y ninguna flexibilidad añadida ...

+0

Use 4 sangrías al formatear su código. Consulte [Ayuda de edición] (http://stackoverflow.com/editing-help). –

+2

Dos definiciones, proporcionó, no utiliza currying, estamos hablando, estos son solo ejemplos de diferentes formas de definir una función. Ver el enlace a otra pregunta en el comentario a mi respuesta. –

+0

Interesante, porque 'currying' es exactamente como lo llama TLS ... Citando: "' (lambda (a) (lambda (x) (eq? X a))) '... ¿Es eso 'Curry-ing?' ... [Sí.] " –

Respuesta

23

Un uso efectivo de las funciones curried es la disminución de la cantidad de código.

considerar tres funciones, dos de los cuales son casi idénticos:

(define (add a b) 
    (action + a b)) 

(define (mul a b) 
    (action * a b)) 

(define (action kind a b) 
    (kind a b)) 

Si el código invoca add, que a su vez llama action con clase +. Lo mismo con mul.

Usted definió estas funciones como lo haría en muchos idiomas populares imprescindibles disponibles (algunos de ellos han incluido lambdas, currying y otras características que normalmente se encuentran en el mundo funcional, porque todo es terriblemente útil).

Todos add y sum hacen, está envolviendo la llamada a action con el kind apropiado. Ahora, considere las definiciones al curry de estas funciones:

(define add-curried 
    ((curry action) +)) 

(define mul-curried 
    ((curry action) *)) 

Se han reducido considerablemente. Simplemente pedimos la función action pasando solo un argumento, el kind, y obtuvimos la función curried que acepta los otros dos argumentos.

Este enfoque le permite escribir menos código, con un alto nivel de mantenimiento.

Imagínese que la función action se reescribirá inmediatamente para aceptar 3 argumentos más. Sin currying que tendría que reescribir sus implementaciones de add y mul:

(define (action kind a b c d e) 
    (kind a b c d e)) 

(define (add a b c d e) 
    (action + a b c d e)) 

(define (mul a b c d e) 
    (action * a b c d e)) 

Pero Currying se salvó de que el trabajo desagradable y propenso a errores; no es necesario volver a escribir ni siquiera un símbolo en las funciones add-curried y mul-curried, ya que la función de llamada proporcionaría la cantidad necesaria de argumentos pasados ​​a action.

+0

Teniendo en cuenta algunos ejemplos de TLS, y su ejemplo anterior, ¿cuál es el beneficio de '(define (tipo de acción) (lambda (ab) (kind ab)))' en oposición a '(define (action kind ab) (kind ab)) '? Solo puedo ver más código y ninguna flexibilidad añadida ... –

+0

@Philip: no hay ningún beneficio, excepto el gusto y el número de teclas :-) Consulte "[¿Por qué todas las lambdas en The Little Schemer?] (Http: // stackoverflow.com/q/4777865/298282) ". –

+0

No estoy hablando de lambdas ... Si lo entiendo bien, la primera función es al curry, y la segunda no. De nuevo, si no me equivoco, ambos hacen exactamente lo mismo, pero la versión curried es más larga tanto en su definición como cuando se llama - '((acción *) 5 5)' en oposición a '(acción * 5 5) ' –

0

Así que no tiene que aumentar el texto estándar con un poco de lambda.

0

Es muy fácil crear cierres. De vez en cuando uso SRFI-26. Es realmente lindo.

4

Puede ver currying como una especialización. Elija algunos valores predeterminados y deje al usuario (quizás usted mismo) con una función especializada expresiva.

11

Pueden hacer que el código sea más fácil de leer. Considere los siguientes dos fragmentos Haskell:

lengths :: [[a]] -> [Int] 
lengths xs = map length xs 

lengths' :: [[a]] -> [Int] 
lengths' = map length 

¿Por qué dar un nombre a una variable que no vas a usar?

funciones al curry también ayudan en situaciones como esta:

doubleAndSum ys = map (\xs -> sum (map (*2) xs) ys 

doubleAndSum' = map (sum . map (*2)) 

La supresión de estos variables extra que hace que el código sea más fácil de leer y que no hay necesidad para que usted mantenga mentalmente claro qué x es y lo que es YS.

HTH.

+1

esto se llama el estilo de programación * pointfree *, que solo es posible con el código al curry. – comonad

+4

La aplicación parcial de Haskell es mucho más fría que el currying explícito. –

+2

La programación en el estilo sin puntos o tácito no requiere funciones con curry. Se puede hacer usando combinadores, por ejemplo. Dicho estilo es prominente con el lenguaje de programación J. En J, el currying no ocurre por defecto, y la forma tácita nunca depende de ello. – kaleidic

3

Creo que el currying es una forma tradicional de manejar funciones n-arias generales siempre que las únicas que puede definir sean únicas.

Por ejemplo, en el cálculo lambda (del cual se derivan los lenguajes de programación funcional), solo hay abstracciones de una variable (que se traduce en funciones unarias en FPL). En cuanto al cálculo de lambda, creo que es más fácil probar cosas sobre dicho formalismo, ya que no es necesario manejar el caso de las funciones n-arias (ya que puede representar cualquier función n-aria con varias unarias mediante el currying) .

(Otros ya han cubierto algunas de las implicaciones prácticas de esta decisión por lo que voy a parar aquí.)

+1

Citando de mi pregunta inicial: '(fuera de idiomas que solo permiten un argumento por función, donde la necesidad de usar currying es por supuesto bastante evidente.)' :) –

0

En sí mismo es ganarse el azúcar sintáctico. El azúcar sintáctico se trata de lo que quiere hacer fácil. C, por ejemplo, quiere hacer que las instrucciones sean "baratas" en el lenguaje ensamblador, como incrementos, facilidad y, por lo tanto, tienen azúcar sintáctica para el incremento, la notación ++.

t = x + y 
x = x + 1 

se sustituye por t = x + y ++

Los lenguajes funcionales simplemente podrían tener la misma facilidad cosas por el estilo.

f(x,y,z) = abc 
g(r,s)(z) = f(r,s,z). 
h(r)(s)(z) = f(r,s,z) 

pero en su lugar es todo automático. Y eso permite que un g ligado por un particular r0, s0 (es decir, valores específicos) pase como una función variable.

Take para la función de ordenar el ejemplo de Perl que toma ordenar la lista de sub donde sub es una función de dos variables que se evalúa como un valor booleano y lista es una lista arbitraria.

que, naturalmente, que desee utilizar operadores de comparación (< =>) en Perl y tienen sortordinal = sort (< =>) donde las obras sortordinal en las listas. Para hacer esto, ordenarías para que sea una función al curry.
Y de hecho tipo de lista se define precisamente de esta manera en Perl.

En resumen: el curry es azúcar para hacer que las funciones de primera clase sean más naturales.

2

Usando all :: (a -> Bool) -> [a] -> Bool con un predicado curried.

all (`elem` [1,2,3]) [0,3,4,5] 

operadores infijos Haskell pueden ser al curry en cada lado, para que pueda ganarse fácilmente la aguja o el lado del recipiente de la función elem (IS-elemento-de).

+0

Los predicados son una de las cosas más útiles para curry en mi experiencia . –

0

Me gustaría agregar un ejemplo a la respuesta de @Francesco.

enter image description here

2

No podemos componer directamente a las funciones que se lleva a varios parámetros. Dado que la composición de funciones es uno de los conceptos clave en la programación funcional. Al usar la técnica de Currying, podemos componer funciones que toman múltiples parámetros.