Question

J'ai vu des références aux fonctions curry dans plusieurs articles et blogs mais je ne trouve pas de bonne explication (ou du moins une qui ait du sens !)

Était-ce utile?

La solution

Le curry consiste à décomposer une fonction qui prend plusieurs arguments en une série de fonctions qui prennent une partie des arguments.Voici un exemple en JavaScript :

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

add(3, 4); // returns 7

Il s'agit d'une fonction qui prend deux arguments, a et b, et renvoie leur somme.Nous allons maintenant curry cette fonction :

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

Il s'agit d'une fonction qui prend un argument, a, et renvoie une fonction qui prend un autre argument, b, et cette fonction renvoie leur somme.

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 ajoutera 3 à son argument.C’est ce que certains pourraient appeler une fermeture.La troisième instruction utilise l'opération add3 pour ajouter 3 à 4, produisant à nouveau 7.

Autres conseils

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

Alors, comment gérez-vous quelque chose que vous exprimeriez naturellement comme, disons : f(x,y)?Eh bien, vous considérez cela comme équivalent à f(x)(y) -- f(x), appeler 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 AUSSI un argument ;-).

Comme d'habitude, Wikipédia a une belle entrée récapitulative à ce sujet, avec de nombreux indicateurs utiles (y compris probablement ceux concernant vos langues préférées ;-) ainsi qu'un traitement mathématique légèrement plus rigoureux.

Voici un exemple concret :

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

Maintenant, étant sur terre, vous souhaitez uniquement calculer les forces des objets sur cette planète.Dans un langage fonctionnel, vous pourriez transmettre la masse de la terre à la fonction puis l'évaluer partiellement.Ce que vous obtiendriez est une autre fonction qui ne prend que deux arguments et calcule la force gravitationnelle des objets sur Terre.C'est ce qu'on appelle le curry.

Le currying est une transformation qui peut être appliquée aux fonctions pour leur permettre de prendre un argument de moins qu'auparavant.

Par exemple, en F#, vous pouvez définir une fonction ainsi : -

let f x y z = x + y + z

Ici, la fonction f prend les paramètres x, y et z et les additionne ainsi : -

f 1 2 3

Renvoie 6.

A partir de notre définition, nous pouvons donc définir la fonction curry pour f : -

let curry f = fun x -> f x

Où 'fun x -> f x' est une fonction lambda équivalente à x => f(x) en C#.Cette fonction saisit la fonction que vous souhaitez curry et renvoie 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 ainsi : -

let curryf = curry f

Nous pouvons alors faire ce qui suit : -

let f1 = curryf 1

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

f1 2 3

Ce qui renvoie 6.

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

let papply f x = f x

Bien que nous puissions l'étendre à plus d'un paramètre, c'est-à-dire : -

let papply2 f x y = f x y
let papply3 f x y z = f x y z
etc.

Une application partielle prendra la fonction et les paramètres et renverra une fonction qui nécessite un ou plusieurs paramètres en moins, et comme le montrent les deux exemples précédents, elle est implémentée directement dans la définition de fonction F# standard afin que nous puissions obtenir le résultat précédent ainsi : -

let f1 = f 1
f1 2 3

Ce qui renverra un résultat de 6.

En conclusion:-

La différence entre le curry et l'application de fonctions partielles est la suivante : -

Currying 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 paramètres comme une série de fonctions à argument unique..Exemple:-

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

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 n arguments spécifiés.Exemple:-

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

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 exécute le 10 est transmis comme x;

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

ce qui signifie qu'on nous renvoie cette fonction :

function(y) { return 10 + y };

Alors quand tu appelles

 addTen();

tu appelles vraiment :

 function(y) { return 10 + y };

Donc si tu fais ça :

 addTen(4)

c'est la même chose que :

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

Donc notre addTen() ajoute toujours dix à tout ce que nous transmettons.Nous pouvons réaliser 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...

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 deuxième argument et ainsi de suite.Cela permet aux fonctions de plusieurs arguments de voir certains de leurs arguments initiaux partiellement appliqués.

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..."

(Il suffit d'utiliser la concaténation via + pour éviter toute distraction pour les programmeurs non Python.)

Modification à ajouter :

Voir http://docs.python.org/library/functools.html?highlight=partial#functools.partial, qui montre également l'objet partiel vs.distinction de fonction dans la manière dont Python l'implémente.

Si tu comprends partial vous êtes à mi-chemin.L'idée de partial consiste à préappliquer des arguments à une fonction et à restituer 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 tous les arguments qui lui ont été fournis.

À Clojure + est une fonction mais pour que les choses soient très claires :

(defn add [a b] (+ a b))

Vous savez peut-être que le inc la fonction ajoute simplement 1 au nombre transmis.

(inc 7) # => 8

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

(def inc (partial add 1))

Ici, nous renvoyons une autre fonction dont 1 est chargé dans le premier argument de add.Comme add prend deux arguments le nouveau inc la fonction veut seulement le b argument -- 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é-fournies.C'est pourquoi, dans un langage fonctionnel, les fonctions classent souvent les arguments du général au spécifique.Cela facilite la réutilisation de ces fonctions pour construire d’autres fonctions.

Imaginez maintenant si le langage était suffisamment intelligent pour comprendre de manière introspective que add Je voulais deux arguments.Lorsque nous lui avons transmis un argument, plutôt que d'hésiter, que se passe-t-il si la fonction appliquait partiellement l'argument que nous lui avons transmis 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 ainsi que se comportent certaines langues.C'est exceptionnellement utile lorsque l'on souhaite composer des fonctions en transformations plus larges.Cela nous mènerait aux transducteurs.

J'ai trouvé cet article, et l'article auquel il fait référence, utiles 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 paramètres seront transmis, vous n'avez donc pas besoin de fonctions à 2 paramètres, 3 paramètres et 4 paramètres.

Curry peut simplifier votre code.C’est l’une des principales raisons de l’utiliser.Le currying 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é close (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 passés pour compléter l'opération.

L'avantage est que cela peut simplifier le traitement des paramètres 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 gérable.Diviser également le code en morceaux plus petits 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);

C'est très utile pour rendre le code complexe soigné et gérer les méthodes non synchronisées, etc.

Currying consiste à traduire une fonction de callable en f(a, b, c) en callable comme f(a)(b)(c).

Sinon, le curry consiste à décomposer une fonction qui prend plusieurs arguments en une série de fonctions qui prennent une partie des arguments.

Littéralement, le curry est une transformation de fonctions :d'une manière d'appeler à une autre.En JavaScript, nous créons généralement un wrapper pour conserver la fonction d'origine.

Le curry n’appelle pas de fonction.Cela le transforme simplement.

Créons une fonction curry qui effectue le curry pour les fonctions à deux arguments.Autrement dit, curry(f) pour deux arguments f(a, b) le traduit en 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

Comme vous pouvez le voir, l'implémentation est une série de wrappers.

  • Le résultat de curry(func) est un emballage function(a).
  • Quand on l'appelle comme sum(1), l'argument est enregistré dans l'environnement lexical et un nouveau wrapper est renvoyé function(b).
  • Alors sum(1)(2) appelle enfin function(b) fournissant 2, et il transmet l'appel à la somme multi-arguments d'origine.

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

Voici une fonction régulière et non conservée, qui ajoute deux paramètres 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 au curry.Au lieu d'une liste de deux paramètres int, vous appliquez cette fonction à deux listes d'un paramètre int: chacun:

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, c'est que lorsque vous invoquez curriedSum, vous obtenez en fait deux invocations de fonctions traditionnelles consécutives.La première fonction invocation prend un seul paramètre int nommé x et renvoie une valeur de fonction pour la deuxième fonction.Cette deuxième fonction prend le paramètre Inty.

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

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

Appliquer 1 à la première fonction - en d'autres termes, invoquant la première fonction et passant dans 1 - il est entièrement la deuxième fonction:

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

L'application de 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 dont vous ne connaissez qu'un seul des paramètres pour le moment :

Par exemple:

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
}

Ici, puisque vous ne connaissez pas le deuxième paramètre de rappel lors de l'envoi à performAsyncRequest(_:) vous devrez créer un autre lambda/fermeture pour envoyer celui-ci à la fonction.

Comme toutes les autres réponses, le currying permet de créer des fonctions partiellement appliquées.Javascript ne fournit pas de support natif pour le curry automatique.Les exemples fournis ci-dessus peuvent donc ne pas aider au codage pratique.Il existe un excellent exemple dans livescript (qui compile 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 d'arguments, livescript génère une nouvelle fonction curry pour vous (double)

Vous trouverez ici une explication simple de l’implémentation du curry en C#.Dans les commentaires, j'ai essayé de montrer à quel point le curry peut être utile :

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);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top