Qu’est-ce que le curry?

J’ai vu des références à des fonctions curry dans plusieurs articles et blogs mais je ne trouve pas de bonne explication (ou du moins une explication logique!)

Le curry est lorsque vous décomposez une fonction qui prend plusieurs arguments dans une série de fonctions faisant partie des arguments. Voici un exemple en JavaScript:

 function add (a, b) { return a + b; } add(3, 4); // returns 7 

Ceci est une fonction qui prend deux arguments, a et b, et retourne leur sum. Nous allons maintenant curry cette fonction:

 function add (a) { return function (b) { return a + b; } } 

Ceci est une fonction qui prend un argument, a, et retourne une fonction qui prend un autre argument, b, et cette fonction retourne leur sum.

 add(3)(4); var add3 = add(3); add3(4); 

La première instruction renvoie 7, comme l’instruction add (3, 4). La deuxième instruction définit une nouvelle fonction appelée add3 qui appenda 3 à son argument. C’est ce que certaines personnes peuvent appeler une fermeture. La troisième instruction utilise l’opération add3 pour append 3 à 4, produisant à nouveau 7 en conséquence.

Dans une algèbre de fonctions, traiter des fonctions qui prennent plusieurs arguments (ou un argument équivalent à un N-tuple) est quelque peu inélégant – mais, comme Moses Schönfinkel (et, indépendamment, Haskell Curry) l’a prouvé, ce n’est pas nécessaire: tout ce que vous besoin sont des fonctions qui prennent un argument.

Alors, comment gérez-vous quelque chose que vous exprimeriez naturellement, par exemple, f(x,y) ? Eh bien, vous prenez cela comme équivalent à f(x)(y)f(x) , appelez-le g , est une fonction, et vous appliquez cette fonction à y . En d’autres termes, vous n’avez que des fonctions qui prennent un argument – mais certaines de ces fonctions renvoient d’autres fonctions (qui prennent également un argument ;-).

Comme d’habitude, wikipedia a un bon résumé à ce sujet, avec de nombreuses indications utiles (probablement celles concernant vos langues préférées 😉 ainsi qu’un traitement mathématique un peu plus rigoureux.

Voici un exemple concret:

Supposons que vous ayez une fonction qui calcule la force gravitationnelle agissant sur un object. Si vous ne connaissez pas la formule, vous pouvez la trouver ici . Cette fonction prend en paramètre les trois parameters nécessaires.

Maintenant, étant sur la terre, vous voulez seulement calculer des forces pour des objects sur cette planète. Dans un langage fonctionnel, vous pourriez passer la masse de la Terre à la fonction et l’évaluer partiellement. Ce que vous récupérez est une autre fonction qui ne prend que deux arguments et calcule la force gravitationnelle des objects sur terre. Cela s’appelle le curry.

Le curry est une transformation qui peut être appliquée aux fonctions pour leur permettre de prendre un argument de moins que précédemment.

Par exemple, dans F #, vous pouvez définir une fonction de la manière suivante: –

 let fxyz = x + y + z 

Ici, la fonction f prend les parameters x, y et z et les additionne pour: –

 f 1 2 3 

Retours 6.

De notre définition on peut donc définir la fonction curry pour f: –

 let curry f = fun x -> fx 

Où ‘fun x -> fx’ est une fonction lambda équivalente à x => f (x) en C #. Cette fonction introduit la fonction que vous souhaitez curry et retourne une fonction qui prend un seul argument et renvoie la fonction spécifiée avec le premier argument défini sur l’argument d’entrée.

En utilisant notre exemple précédent, nous pouvons obtenir un curry de f donc: –

 let curryf = curry f 

Nous pouvons alors faire ce qui suit: –

 let f1 = curryf 1 

Ce qui nous donne une fonction f1 qui équivaut à f1 yz = 1 + y + z. Cela signifie que nous pouvons faire ce qui suit: –

 f1 2 3 

Qui retourne 6.

Ce processus est souvent confondu avec «application de fonction partielle» qui peut être définie ainsi: –

 let papply fx = fx 

Bien que nous puissions l’étendre à plusieurs parameters, c’est-à-dire:

 let papply2 fxy = fxy let papply3 fxyz = fxyz etc. 

Une application partielle prendra la fonction et le (s) paramètre (s) et renverra une fonction nécessitant un ou plusieurs parameters inférieurs, et comme le montrent les deux exemples précédents, elle est directement implémentée dans la définition de la fonction F #

 let f1 = f 1 f1 2 3 

Qui retournera un résultat de 6.

En conclusion:-

La différence entre le currying et l’application partielle est la suivante: –

Le curry prend une fonction et fournit une nouvelle fonction acceptant un seul argument et renvoyant la fonction spécifiée avec son premier argument défini sur cet argument. Cela nous permet de représenter des fonctions avec plusieurs parameters comme une série de fonctions à un seul argument . Exemple:-

 let fxyz = x + y + z let curryf = curry f let f1 = curryf 1 let f2 = curryf 2 f1 2 3 6 f2 1 3 6 

L’application de fonction partielle est plus directe – elle prend une fonction et un ou plusieurs arguments et renvoie une fonction avec les n premiers arguments définis sur les arguments spécifiés. Exemple:-

 let fxyz = x + y + z let f1 = f 1 let f2 = f 2 f1 2 3 6 f2 1 3 6 

Une fonction curry est une fonction de plusieurs arguments réécrits de telle sorte qu’elle accepte le premier argument et renvoie une fonction qui accepte le second argument et ainsi de suite. Cela permet aux fonctions de plusieurs arguments d’appliquer partiellement certains de leurs arguments initiaux.

Cela peut être un moyen d’utiliser des fonctions pour créer d’autres fonctions.

En javascript:

 let add = function(x){ return function(y){ return x + y }; }; 

Cela nous permettrait de l’appeler ainsi:

 let addTen = add(10); 

Lorsque cela fonctionne, le 10 est passé en tant que x ;

 let add = function(10){ return function(y){ return 10 + y }; }; 

ce qui signifie que nous sums retournés cette fonction:

 function(y) { 10 + y }; 

Donc, quand vous appelez

  addTen(); 

vous appelez vraiment:

  function(y) { 10 + y }; 

Donc, si vous faites ceci:

  addTen(4) 

c’est la même chose que:

 function(4) { 10 + 4} // 14 

Donc, notre addTen() ajoute toujours dix à tout ce que nous passons. Nous pouvons créer des fonctions similaires de la même manière:

 let addTwo = add(2) // addTwo(); will add two to whatever you pass in let addSeventy = add(70) // ... and so on... 

Voici un exemple de jouet 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..." 

(Juste en utilisant la concaténation via + pour éviter toute distraction pour les programmeurs non-Python.)

Modification à append:

Voir http://docs.python.org/library/functools.html?highlight=partial#functools.partial , qui montre également la distinction entre object partiel et fonction dans la manière dont Python implémente cela.

J’ai trouvé cet article, et l’article auquel il fait référence, utile pour mieux comprendre le curry: http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx

Comme les autres l’ont mentionné, c’est juste un moyen d’avoir une fonction à un seul paramètre.

Ceci est utile dans la mesure où vous n’avez pas à supposer combien de parameters seront transmis, vous n’avez donc pas besoin de fonctions à 2 parameters, 3 parameters et 4 parameters.

Si vous comprenez partial vous êtes à mi-chemin. L’idée de partialité est de pré-appliquer des arguments à une fonction et de renvoyer une nouvelle fonction qui ne veut que les arguments restants. Lorsque cette nouvelle fonction est appelée, elle inclut les arguments préchargés ainsi que les arguments qui lui ont été fournis.

En Clojure + est une fonction mais pour rendre les choses clairement claires:

 (defn add [ab] (+ ab)) 

Vous savez peut-être que la fonction inc ajoute simplement 1 à tout nombre passé.

 (inc 7) # => 8 

Construisons-le nous-mêmes en utilisant partial :

 (def inc (partial add 1)) 

Ici, nous retournons une autre fonction qui a 1 chargé dans le premier argument de add . Comme add prend deux arguments, la nouvelle fonction inc ne veut que l’argument b – pas 2 arguments comme avant puisque 1 a déjà été partiellement appliqué. Ainsi, partial est un outil à partir duquel créer de nouvelles fonctions avec des valeurs par défaut pré-définies. C’est pourquoi, dans un langage fonctionnel, les fonctions ordonnent souvent des arguments de général à spécifique. Cela facilite la réutilisation de ces fonctions pour construire d’autres fonctions.

Maintenant, imaginez si le langage était suffisamment intelligent pour comprendre introspectivement que l’ add deux arguments était nécessaire. Lorsque nous lui avons passé un argument plutôt que de le laisser en suspens, que se passe-t-il si la fonction appliquait partiellement l’argument que nous avons passé en notre nom, sachant que nous avions probablement l’intention de fournir l’autre argument plus tard? On pourrait alors définir inc sans utiliser explicitement partial .

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

C’est la façon dont certaines langues se comportent. Il est exceptionnellement utile quand on veut composer des fonctions dans des transformations plus grandes. Cela conduirait à des transducteurs.

Une fonction curry est appliquée à plusieurs listes d’arguments, au lieu d’une seule.

Voici une fonction régulière, sans curry, qui ajoute deux parameters Int, x et y:

 scala> def plainOldSum(x: Int, y: Int) = x + y plainOldSum: (x: Int,y: Int)Int scala> plainOldSum(1, 2) res4: Int = 3 

Voici une fonction similaire qui est curry. Au lieu d’une liste de deux parameters Int, vous appliquez cette fonction à deux listes d’un paramètre Int chacune:

 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 

Ce qui se passe ici est que lorsque vous invoquez curriedSum , vous obtenez en fait deux invocations de fonctions traditionnelles. La première invocation de fonction prend un seul paramètre Int nommé x et renvoie une valeur de fonction pour la deuxième fonction. Cette seconde fonction prend le paramètre Int y .

Voici une fonction nommée en first qui fait dans l’esprit ce que ferait la première invocation de fonction traditionnelle de curriedSum :

 scala> def first(x: Int) = (y: Int) => x + y first: (x: Int)(Int) => Int 

Appliquer 1 à la première fonction – en d’autres termes, appeler la première fonction et passer 1 – donne la deuxième fonction:

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

Appliquer 2 à la deuxième fonction donne le résultat:

 scala> second(2) res6: Int = 3 

Un exemple de curry serait lorsque vous avez des fonctions que vous connaissez seulement l’un des parameters pour le moment:

Par exemple:

 func aFunction(str: Ssortingng) { let callback = callback(str) // signature now is `NSData -> ()` performAsyncRequest(callback) } func callback(str: Ssortingng, data: NSData) { // Callback code } func performAsyncRequest(callback: NSData -> ()) { // Async code that will call callback with NSData as parameter } 

Ici, puisque vous ne connaissez pas le second paramètre pour le rappel lors de l’envoi à performAsyncRequest(_:) vous devez créer un autre lambda / closure pour l’envoyer à la fonction.

Pour donner un exemple concret (et potentiellement utile) de currying, découvrez comment créer des appels serveur en javascript avec la bibliothèque fetch

  Get(url) { let fullUrl = toFullUrl(url); let promise = getPromiseForFetchWithToken((token) => { let headers = Object.assign( getDefaultHeaders(token), jsonHeaders); let config = { method: "GET", headers: headers }; return fetch(fullUrl, config); }); return promise; } 

getPromiseForFetchWithToken est une fonction curry qui renvoie une Promise avec le résultat du fetch, illustré ci-dessous:

 function getPromiseForFetchWithToken(tokenConsumingFetch) { function resolver(resolve, reject) { let token = localStorage.getItem("token"); tokenConsumingFetch(token) .then(checkForError) .then((response) => { if (response) resolve(response); }) .catch(reject); } var promise = new Promise(resolver); return promise; } 

Cela vous permet d’attendre l’appel de la fonction Get et de gérer correctement la valeur de retour, quelle que soit sa getPromiseForFetchWithToken . Vous pouvez réutiliser la fonction getPromiseForFetchWithToken partout où vous devez créer un appel serveur qui doit inclure un jeton de support. (Mettre, supprimer, poster, etc.)

Comme toutes les autres réponses, le curry permet de créer des fonctions partiellement appliquées. Javascript ne fournit pas de support natif pour le curry automatique. Ainsi, les exemples fournis ci-dessus peuvent ne pas aider à un codage pratique. Il y a un excellent exemple de script en live (qui comstack essentiellement 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 

Dans l’exemple ci-dessus, lorsque vous avez donné moins de arguments, le script de vie génère une nouvelle fonction curry pour vous (double)

Curry peut simplifier votre code. C’est l’une des principales raisons d’utiliser ceci. Le curry est un processus de conversion d’une fonction qui accepte n arguments en n fonctions qui n’acceptent qu’un seul argument.

Le principe est de passer les arguments de la fonction passée, en utilisant la propriété closure (closure), pour les stocker dans une autre fonction et la traiter comme une valeur de retour, et ces fonctions forment une chaîne et les arguments finaux sont transmis pour compléter l’opération.

L’avantage de ceci est qu’il peut simplifier le traitement des parameters en traitant un paramètre à la fois, ce qui peut également améliorer la flexibilité et la lisibilité du programme. Cela rend également le programme plus facile à gérer. En outre, diviser le code en petits morceaux le rendrait plus facile à réutiliser.

Par exemple:

 function curryMinus(x) { return function(y) { return x - y; } } var minus5 = curryMinus(1); minus5(3); minus5(5); 

Je peux aussi faire …

 var minus7 = curryMinus(7); minus7(3); minus7(5); 

Ceci est très utile pour créer des codes complexes et gérer des méthodes non synchronisées, etc.