2008-09-22 37 views
157

No creo que aún haya asimilado el curry. Entiendo lo que hace y cómo hacerlo. No puedo pensar en una situación en la que lo usaría.JavaScript curry: ¿cuáles son las aplicaciones prácticas?

¿Dónde está usando currying en JavaScript (o donde están las principales bibliotecas que lo usan)? Ejemplos de manipulación de DOM o desarrollo de aplicaciones generales son bienvenidos.

One of the answers menciones de animación. Las funciones como slideUp, fadeIn toman un elemento como argumentos y normalmente son una función curried que devuelve la función de orden superior con la función de animación predeterminada incorporada. ¿Por qué es mejor que simplemente aplicar la función ascendente con algunos valores predeterminados?

¿Hay algún inconveniente al usarlo?

a lo solicitado Estos son algunos buenos recursos en JavaScript currificación:

voy a añadir más a medida que surgen en los comentarios.


Por lo tanto, de acuerdo con las respuestas, el currying y la aplicación parcial en general son técnicas de conveniencia.

Si con frecuencia "refina" una función de alto nivel llamándola con la misma configuración, puede curry (o usar la función parcial de Resig) la función de nivel superior para crear métodos de ayuda simples y concisos.

+0

se puede añadir un enlace a un recurso que describe lo que JS currificación es? un tutorial o una publicación de blog sería genial. –

+2

svendtofte.com es extenso, pero si omite toda la sección de "Un curso acelerado en ML" y comienza de nuevo en "Cómo escribir JavaScript curry", se convierte en una excelente introducción al currículum en js. – danio

+1

Este es un buen punto de partida para entender qué es el curry y la aplicación parcial realmente: http://slid.es/gsklee/functional-programming-in-5-minutes – Kay

Respuesta

28

@Hank Gay

En respuesta al comentario de EmbiggensTheMind:

No puedo pensar en un caso en que currying -por sí misma, es útil en JavaScript; es una técnica para convertir llamadas a funciones con múltiples argumentos en cadenas de llamadas a función con un único argumento para cada llamada, pero JavaScript admite múltiples argumentos en una sola llamada a función.

En JavaScript, y supongo que en la mayoría de los otros lenguajes reales (no en cálculo lambda), se asocia comúnmente con la aplicación parcial. John Resig explains it better, pero la esencia es que tienen alguna lógica que se aplicará a dos o más argumentos, y usted solo conoce los valores para algunos de esos argumentos.

Puede utilizar la aplicación/currying parcial para corregir esos valores conocidos y devolver una función que solo acepta las incógnitas, que se invocará más adelante cuando realmente tenga los valores que desea aprobar. Esto proporciona una manera ingeniosa de evitar repetirse cuando habría estado llamando a los mismos complementos de JavaScript una y otra vez con todos los mismos valores, excepto uno. Para robar el ejemplo de Juan:

String.prototype.csv = String.prototype.split.partial(/,\s*/); 
var results = "John, Resig, Boston".csv(); 
alert((results[1] == "Resig") + " The text values were split properly"); 
+1

He intentado explicarlo con [algunos ejemplos y demos en mi artículo escrito]. (Http://conceptf1.blogspot.com/2014/03/currying-in-javascript.html) –

+4

Esta es realmente una mala respuesta. Currying no tiene nada que ver con la aplicación parcial. Currying permite la composición de la función. La composición de la función permite la reutilización de la función. La reutilización de funciones aumenta la capacidad de mantenimiento del código. ¡Es fácil! – ftor

1

En cuanto a las bibliotecas que lo utilizan, siempre hay Functional.

¿Cuándo es útil en JS? Probablemente las mismas veces sea útil en otros idiomas modernos, pero el único momento en que puedo ver que lo uso es junto con la aplicación parcial.

+0

Gracias Hank - por favor, ¿puede ampliar cuando sea útil en general? –

1

Diría que, probablemente, toda la biblioteca de animación en JS está usando currying. En lugar de tener que pasar para cada llamada, un conjunto de elementos impactados y una función que describe cómo debe comportarse el elemento, a una función de orden superior que garantizará todo el tiempo, generalmente es más fácil para el cliente, como API pública. funcionan como "slideUp", "fadeIn" que toma solo elementos como argumentos, y que son solo algunas funciones al curry que devuelven la función de orden alto con la "función de animación" predeterminada incorporada.

+0

¿Por qué es mejor curry la función superior en vez de simplemente llamarla con algunos valores predeterminados? –

+1

Porque es mucho más modular poder curry una "doMathOperation" con una adición/multiplicación/cuadrado/módulo/otro-calucation a voluntad que imaginar todo el "predeterminado" que la función más alta podría soportar. – gizmo

2

No es magia ni nada ... solo una taquigrafía agradable para funciones anónimas.

parcial (alerta, "¡FOO!") Es equivalente a la función() {alert (" FOO ");!}

parcial (Math.max, 0) corresponde a la función (x) {return Math.max (0, x);}

las llamadas a parcial (MochiKit terminología. creo que algunas otras bibliotecas dan funciones de un método .curry el que hace lo mismo) tener un aspecto ligeramente más agradable y menos ruidoso que las funciones anónimas.

5

encontré funciones que se asemejan a pitón de functools.partial más útil en JavaScript:

function partial(fn) { 
    return partialWithScope.apply(this, 
    Array.prototype.concat.apply([fn, this], 
     Array.prototype.slice.call(arguments, 1))); 
} 

function partialWithScope(fn, scope) { 
    var args = Array.prototype.slice.call(arguments, 2); 
    return function() { 
    return fn.apply(scope, Array.prototype.concat.apply(args, arguments)); 
    }; 
} 

¿Por qué querrías usarlo? Una situación común en la que desea utilizar esto es cuando se desea enlazar this en función de un valor:

var callback = partialWithScope(Object.function, obj); 

Ahora, cuando se llama devolución de llamada, this puntos a obj. Esto es útil en situaciones de eventos o para ahorrar espacio porque generalmente hace que el código sea más corto.

El currying es similar a parcial, con la diferencia de que la función que devuelve el currying solo acepta un argumento (por lo que yo entiendo).

2

Aquí hay un ejemplo.

Estoy instrumentando muchos campos con JQuery para poder ver lo que los usuarios están haciendo. El código es el siguiente:

$('#foo').focus(trackActivity); 
$('#foo').blur(trackActivity); 
$('#bar').focus(trackActivity); 
$('#bar').blur(trackActivity); 

(para usuarios no JQuery, estoy diciendo que siempre que un par de campos de obtener o perder el foco, quiero que el trackActivity() función que se llama también pude. usar una función anónima, pero tendría que duplicarla 4 veces, así que la saqué y la llamé.)

Ahora resulta que uno de esos campos debe manejarse de manera diferente. Me gustaría poder pasar un parámetro en una de esas llamadas para pasar a nuestra infraestructura de seguimiento. Con currying, puedo.

0

Estoy de acuerdo en que a veces le gustaría poner en marcha la bola creando una pseudo-función que siempre tendrá el valor del primer argumento relleno. Afortunadamente, me encontré con una nueva biblioteca de JavaScript llamada jPaq (h ttp://jpaq.org/) que proporciona esta funcionalidad. Lo mejor de la biblioteca es el hecho de que puedes descargar tu propia compilación que contiene solo el código que necesitarás.

1

funciones de JavaScript se llama lamda en otro lenguaje funcional. Se puede usar para componer una nueva API (función más completa o complementaria) en función de la entrada simple de otro desarrollador. Curry es solo una de las técnicas. Puede usarlo para crear una API simplificada para llamar a una API compleja. Si usted es el desarrollador que usa la API simplificada (por ejemplo, usa jQuery para hacer una manipulación simple), no necesita usar curry. Pero si quieres crear la API simplificada, Curry es tu amigo. Tienes que escribir un marco de JavaScript (como jQuery, mootools) o una biblioteca, entonces puedes apreciar su poder. Escribí una función mejorada de curry, al http://blog.semanticsworks.com/2011/03/enhanced-curry-method.html. No necesita el método de curry para currar, solo ayuda a currar, pero siempre puede hacerlo manualmente escribiendo una función A() {} para devolver otra función B() {}. Para hacerlo más interesante, use la función B() para devolver otra función C().

0

Acabo de escribir un ejemplo de jPaq que muestra algunas aplicaciones geniales de la función de curry. Échale un vistazo aquí: Currying Up String Functions

105

He aquí un interesting AND practical use of currying in JavaScript that uses closures:

function converter(toUnit, factor, offset, input) { 
    offset = offset || 0; 
    return [((offset + input) * factor).toFixed(2), toUnit].join(" "); 
} 

var milesToKm = converter.curry('km', 1.60936, undefined); 
var poundsToKg = converter.curry('kg', 0.45460, undefined); 
var farenheitToCelsius = converter.curry('degrees C', 0.5556, -32); 

milesToKm(10);   // returns "16.09 km" 
poundsToKg(2.5);   // returns "1.14 kg" 
farenheitToCelsius(98); // returns "36.67 degrees C" 

Esto se basa en una extensión curry de Function, aunque como se puede ver que sólo utiliza apply (pero nada de lujos):

Function.prototype.curry = function() { 
    if (arguments.length < 1) { 
     return this; //nothing to curry with - return function 
    } 
    var __method = this; 
    var args = toArray(arguments); 
    return function() { 
     return __method.apply(this, args.concat([].slice.apply(null, arguments))); 
    } 
} 
+5

¡Esto es genial! Lo veo similar a la cita de ceceo que dice "Lisp es un lenguaje de programación programable" – santiagobasulto

+0

Bien ... tal vez soy tonto ... pero no tengo idea de lo que quieres decir? –

+2

Interesante, pero este ejemplo no parece funcionar. 'offset + input' será' undefined + 1.60936' en su ejemplo 'milesToKm'; que resulta en 'NaN'. –

0

sólo quería añadir algunos recursos para Functional.js:

Conferencia/conferencia explicando algunas aplicaciones http://www.youtube.com/watch?v=HAcN3JyQoyY

Actualizado biblioteca Functional.js: https://github.com/loop-recur/FunctionalJS Algunos ayudantes agradable (lo siento nuevo aquí, ninguna reputación: p): /loop-recur/PreludeJS

He estado usando esta biblioteca mucho recientemente para reducir la repetición en una biblioteca auxiliar js IRC clientes. Es genial, realmente ayuda a limpiar y simplificar el código.

Además, si el rendimiento se convierte en un problema (pero esta lib es bastante ligera), es fácil simplemente reescribir utilizando una función nativa.

1

que conocer su hilo de edad pero tendrá que demostrar cómo se está utilizando en las bibliotecas de javascript:

voy a utilizar lodash.js biblioteca para describir estos conceptos concretamente.

Ejemplo:

var fn = function(a,b,c){ 
return a+b+c+(this.greet || ‘'); 
} 

aplicación parcial:

var partialFnA = _.partial(fn, 1,3); 

Currying:

var curriedFn = _.curry(fn); 

Encuadernación:

var boundFn = _.bind(fn,object,1,3);//object= {greet: ’!'} 

uso:

curriedFn(1)(3)(5); // gives 9 
or 
curriedFn(1,3)(5); // gives 9 
or 
curriedFn(1)(_,3)(2); //gives 9 


partialFnA(5); //gives 9 

boundFn(5); //gives 9! 

diferencia:

después de ganarse obtenemos una nueva función sin parámetros pre enlazado.

Después de la aplicación parcial, obtenemos una función que está limitada por algunos parámetros prebound.

en el enlace podemos enlazar un contexto que se utilizará para reemplazar 'esto', si no está predeterminado por defecto de cualquier función será el alcance de la ventana.

Asesoramiento: no es necesario reinventar la rueda. La aplicación/encuadernación/currículo parciales están muy relacionadas. Puedes ver la diferencia anterior. Use este significado en cualquier lugar y la gente reconocerá lo que está haciendo sin problemas de comprensión, además tendrá que usar menos código.

0

Puede utilizar unen nativa para una rápida, una solución de línea

function clampAngle(min, max, angle) { 
 
    var result, delta; 
 
    delta = max - min; 
 
    result = (angle - min) % delta; 
 
    if (result < 0) { 
 
     result += delta; 
 
    } 
 
    return min + result; 
 
}; 
 

 
var clamp0To360 = clampAngle.bind(null, 0, 360); 
 

 
console.log(clamp0To360(405)) // 45

2

Coincidiendo con Hank Gay - Es muy útil en ciertas verdaderos lenguajes de programación funcionales - porque es una parte necesaria. Por ejemplo, en Haskell simplemente no puede tomar múltiples parámetros para una función: no puede hacer eso en la programación funcional pura. Toma un parámetro a la vez y desarrolla su función. En JavaScript, simplemente no es necesario, a pesar de ejemplos artificiales como "convertidor". Aquí está el mismo código de convertidor, sin la necesidad de ganarse:

var converter = function(ratio, symbol, input) { 
    return (input*ratio).toFixed(2) + " " + symbol; 
} 

var kilosToPoundsRatio = 2.2; 
var litersToUKPintsRatio = 1.75; 
var litersToUSPintsRatio = 1.98; 
var milesToKilometersRatio = 1.62; 

converter(kilosToPoundsRatio, "lbs", 4); //8.80 lbs 
converter(litersToUKPintsRatio, "imperial pints", 2.4); //4.20 imperial pints 
converter(litersToUSPintsRatio, "US pints", 2.4); //4.75 US pints 
converter(milesToKilometersRatio, "km", 34); //55.08 km 

me mal deseo Douglas Crockford, en "JavaScript: las partes buenas", había dado alguna mención de la historia y el uso real de ganarse el lugar de su offhanded observaciones Durante mucho tiempo después de leer eso, me quedé boquiabierto, hasta que estuve estudiando la programación funcional y me di cuenta de que venía de allí.

Después de pensar un poco más, postulo que hay un caso de uso válido para currying en JavaScript: si está tratando de escribir utilizando técnicas de programación puramente funcionales usando JavaScript. Parece un caso de uso raro sin embargo.

+1

Su código es mucho más fácil de entender que Prisoner Zero y resuelve el mismo problema sin currying ni nada complejo. Tienes 2 pulgares arriba y él tiene casi 100. Ve a la figura. – DR01D

+1

Bueno .... https://renomad.com/external_author_docs/parable_two_programmers.txt –

0

Otra puñalada, de trabajar con promesas.

(.. Negación: Noob JS, que viene del mundo de Python Incluso allí, currying no se utiliza todo lo que mucho, pero puede ser útil en ocasiones Así que cribbed la función currificación - ver enlaces)

Primero, estoy empezando con una llamada ajax.Tengo un procesamiento específico que hacer en cuanto al éxito, pero en caso de error, solo quiero darle al usuario la opinión de que al llamar al algo resultó en algún error. En mi código actual, muestro los comentarios de error en un panel de arranque, pero estoy usando el registro aquí.

He modificado mi url en vivo para que esto falle.

function ajax_batch(e){ 
    var url = $(e.target).data("url"); 

    //induce error 
    url = "x" + url; 

    var promise_details = $.ajax(
     url, 
     { 
      headers: { Accept : "application/json" }, 
      // accepts : "application/json", 
      beforeSend: function (request) { 
       if (!this.crossDomain) { 
        request.setRequestHeader("X-CSRFToken", csrf_token); 
       } 
     }, 
     dataType : "json", 
     type : "POST"} 
    ); 
    promise_details.then(notify_batch_success, fail_status_specific_to_batch); 
} 

Ahora, aquí con el fin de avisar al usuario de que dio positivo en un proceso por lotes, lo que necesito para escribir esa información en el controlador de errores, porque todo lo que está consiguiendo es una respuesta del servidor.

Todavía tengo la información disponible en el momento de la codificación, en mi caso tengo varios lotes posibles, pero no sé cuál ha fallado. analizar la respuesta del servidor sobre la URL fallida.

function fail_status_specific_to_batch(d){ 
    console.log("bad batch run, dude"); 
    console.log("response.status:" + d.status); 
} 

Hagámoslo. salida de la consola es:

consola:

bad batch run, dude utility.js (line 109) response.status:404

Ahora, vamos a cambiar un poco las cosas y usar un controlador de fallo genérico reutilizable, pero también uno que es al curry en tiempo de ejecución tanto con el conocido en contexto de llamadas en tiempo de código y la información de tiempo de ejecución disponible desde el evento.

... rest is as before... 
    var target = $(e.target).text(); 
    var context = {"user_msg": "bad batch run, dude. you were calling :" + target}; 
    var contexted_fail_notification = curry(generic_fail, context); 

    promise_details.then(notify_batch_success, contexted_fail_notification); 
} 

function generic_fail(context, d){ 
    console.log(context); 
    console.log("response.status:" + d.status); 
} 

function curry(fn) { 
    var slice = Array.prototype.slice, 
     stored_args = slice.call(arguments, 1); 
    return function() { 
     var new_args = slice.call(arguments), 
       args = stored_args.concat(new_args); 
     return fn.apply(null, args); 
    }; 
} 

consola:

Object { user_msg="bad batch run, dude. you were calling :Run ACL now"} utility.js (line 117) response.status:404 utility.js (line 118)

De manera más general, teniendo en cuenta cómo el uso generalizado de devolución de llamada está en JS, currificación parece una herramienta muy útil a tener.

https://javascriptweblog.wordpress.com/2010/04/05/curry-cooking-up-tastier-functions/ http://www.drdobbs.com/open-source/currying-and-partial-functions-in-javasc/231001821?pgno=2

Cuestiones relacionadas