Question 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 une bonne explication (ou du moins une qui a du sens!)


487
2017-08-30 20:12


origine


Réponses:


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

C'est une fonction qui prend deux arguments, a et b, et retourne leur somme. 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 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 certaines personnes appellent une fermeture. La troisième instruction utilise l'opération add3 pour ajouter 3 à 4, produisant à nouveau 7 en conséquence.


618
2017-08-30 20:19



Dans une algèbre de fonctions, traiter des fonctions qui prennent des arguments multiples (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: besoin sont des fonctions qui prennent un argument.

Alors, comment gérez-vous quelque chose que vous exprimez naturellement comme, disons, f(x,y)? Eh bien, vous prenez 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 également un argument ;-).

Comme d'habitude, Wikipédia a un bon résumé à ce sujet, avec de nombreux points utiles (y compris ceux concernant vos langues préférées ;-) ainsi qu'un traitement mathématique un peu plus rigoureux.


107
2017-08-30 02:08



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 le trouver ici. Cette fonction prend en paramètre les trois paramètres nécessaires.

Maintenant, étant sur la terre, vous voulez seulement calculer les forces pour les objets 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 objets sur terre. C'est ce qu'on appelle la curry.


80
2017-08-30 02:22



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 ainsi:

let f x y z = x + y + z

Ici, la fonction f prend les paramètres 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 -> f x

Où 'fun x -> f x' est une fonction lambda équivalente à x => f (x) en C #. Cette fonction entre la fonction que vous souhaitez activer et retourne une fonction prend un seul argumentet 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 équivalente à f1 y z = 1 + y + z. Cela signifie que nous pouvons faire ce qui suit: -

f1 2 3

Qui retourne 6.

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

let papply f x = f x

Bien que nous pouvons l'étendre à plus d'un paramètre, à savoir:

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 le (s) paramètre (s) et renverra une fonction nécessitant un ou plusieurs paramètres inférieurs, et comme le montrent les deux exemples précédents, elle est directement implémentée dans la définition de fonction F # standard.

let f1 = f 1
f1 2 3

Ce qui retournera un résultat de 6.

En conclusion:-

La différence entre l'application de currying et de fonction partielle est que: -

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 paramètres comme une série de fonctions à un seul argument. 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 pour 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

43
2017-08-30 21:06



Une fonction curry est une fonction de plusieurs arguments réécrits de telle sorte qu'elle accepte le premier argument et retourne 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.


28
2017-10-19 05:35



Cela peut être un moyen d'utiliser des fonctions pour faire d'autres fonctions.

En javascript:

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

Cela nous permettrait de l'appeler comme ça:

let addTen = add(10);

Quand cela exécute le 10 est passé en tant que x;

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

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

function(y) { 10 + y };

Alors quand vous appelez

 addTen();

vous appelez vraiment:

 function(y) { 10 + y };

Donc, si vous faites ceci:

 addTen(4)

c'est pareil que:

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

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

27
2018-01-14 17:05



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 pour ajouter:

Voir http://docs.python.org/library/functools.html?highlight=partial#functools.partial, ce qui montre aussi la distinction entre l'objet partiel et la fonction dans la façon dont Python l'implémente.


5
2017-08-30 17:47



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 paramètre.

Ceci est utile car vous n'avez pas besoin de supposer combien de paramètres seront transmis, vous n'avez donc pas besoin de 2 paramètres, 3 paramètres et 4 paramètres.


3
2017-08-30 02:19



Si tu comprends partial vous êtes à mi-chemin L'idée de partial est de pré-présenter des arguments à une fonction et de rendre 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.

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

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

Vous savez peut-être que le inc La fonction ajoute simplement 1 à n'importe quel 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 le nouveau inc la fonction ne veut que 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ésupposées. C'est pourquoi dans un langage fonctionnel, les fonctions ordonnent souvent des arguments allant du général au spécifique. Cela facilite la réutilisation de telles fonctions à partir desquelles construire d'autres fonctions.

Maintenant, imaginez si le langage était assez intelligent pour comprendre introspectivement que add voulait deux arguments. Lorsque nous lui avons passé un argument, plutôt que de rechigner, que se passe-t-il si la fonction appliquait partiellement l'argument que nous lui avons transmis en comprenant que nous voulions probablement 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. Il est exceptionnellement utile quand on veut composer des fonctions dans des transformations plus grandes. Cela conduirait à des transducteurs.


3
2018-06-26 20:26



Une fonction curry est appliquée à plusieurs listes d'arguments, au lieu de simplement un.

Voici une fonction régulière, non-curry, qui ajoute deux Int paramètres, 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 la fonction similaire qui est curry. Au lieu d'une liste de deux paramètres Int, vous appliquez cette fonction à deux listes de 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

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

Voici une fonction nommée first qui fait en esprit ce que le premier traditionnel invocation de fonction 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, invoquer la première fonction et en passant à 1 - la deuxième fonction:

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

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

scala> second(2)
res6: Int = 3

2
2018-05-04 09:03



Un exemple de curry serait lorsque vous avez des fonctions que vous connaissez seulement l'un 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 pour le rappel lors de l'envoi à performAsyncRequest(_:) vous devez créer un autre lambda / closing pour l'envoyer à la fonction.


2
2018-04-11 07:03