J'ai vu des références à des fonctions au curry dans plusieurs articles et blogs mais je ne trouve pas de bonne explication (ou au moins une explication!)
add x y = x+y
(curry) est différent de add (x, y)=x+y
(uncurried)
J'ai vu des références à des fonctions au curry dans plusieurs articles et blogs mais je ne trouve pas de bonne explication (ou au moins une explication!)
add x y = x+y
(curry) est différent de add (x, y)=x+y
(uncurried)
Réponses:
Le currying consiste à décomposer une fonction qui prend plusieurs arguments en une série de fonctions qui ne prennent chacune qu'un seul argument. 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 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 certains appellent une fermeture. La troisième instruction utilise l'opération add3 pour ajouter 3 à 4, produisant à nouveau 7 en conséquence.
[1, 2, 3, 4, 5]
que vous souhaitez multiplier par un nombre arbitraire. Dans Haskell, je peux écrire map (* 5) [1, 2, 3, 4, 5]
pour multiplier la liste entière par 5
, et ainsi générer la liste [5, 10, 15, 20, 25]
.
map
doit être une fonction qui ne prend qu'un seul argument - un élément de la liste. La multiplication - en tant que concept mathématique - est une opération binaire; il faut 2 arguments. Cependant, dans Haskell *
est une fonction curry, similaire à la deuxième version de add
dans cette réponse. Le résultat de (* 5)
est une fonction qui prend un seul argument et le multiplie par 5, et qui nous permet de l'utiliser avec map.
Dans une algèbre de fonctions, traiter des fonctions qui prennent plusieurs arguments (ou un argument équivalent qui est un tuple N) est quelque peu inélégant - mais, comme l'a démontré Moses Schönfinkel (et, indépendamment, Haskell Curry), 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, disons 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 une belle entrée récapitulative à ce sujet, avec de nombreux conseils utiles (y compris probablement ceux concernant vos langues préférées ;-) ainsi qu'un traitement mathématique légèrement plus rigoureux.
div :: Integral a => a -> a -> a
- notez ces multiples flèches? "Mapper une fonction mappant une à une" est une lecture ;-). Vous pouvez utiliser un argument (unique) de tuple pour div
& c, mais ce serait vraiment anti-idiomatique dans Haskell.
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 la terre, vous voulez seulement calculer les forces des objets sur cette planète. Dans un langage fonctionnel, vous pourriez passer la masse de la terre à la fonction et l'évaluer ensuite partiellement. Ce que vous obtiendrez en retour 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 curry 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.
D'après notre définition, nous pouvons donc définir la fonction de curry pour f: -
let curry f = fun x -> f x
Où 'fun x -> fx' est une fonction lambda équivalente à x => f (x) en C #. Cette fonction entre 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
On peut 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 renvoie 6.
Ce processus est souvent confondu avec «application de fonction partielle» qui peut être définie ainsi: -
let papply f x = f x
Bien que nous puissions l'étendre à plusieurs paramètres, à 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 retournera une fonction qui nécessite un ou plusieurs paramètres en moins, et comme les deux exemples précédents le montrent est implémenté 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
Qui retournera un résultat de 6.
En conclusion:-
La différence entre le curry et l'application d'une 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 à 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
};
};
Nous permettrait de l'appeler ainsi:
let addTen = add(10);
Lorsque cela s'exécute, le 10
est transmis en tant que x
;
let add = function(10){
return function(y){
return 10 + y
};
};
ce qui signifie que nous retournons cette fonction:
function(y) { return 10 + y };
Alors quand tu appelles
addTen();
vous appelez vraiment:
function(y) { return 10 + y };
Donc, si vous faites cela:
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 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...
Maintenant, la question de suivi évidente est pourquoi diable voudriez-vous jamais faire cela? Cela transforme ce qui était une opération enthousiaste x + y
en une opération qui peut être exécutée paresseusement, ce qui signifie que nous pouvons faire au moins deux choses: 1. mettre en cache des opérations coûteuses 2. réaliser des abstractions dans le paradigme fonctionnel.
Imaginez que notre fonction au curry ressemble à ceci:
let doTheHardStuff = function(x) {
let z = doSomethingComputationallyExpensive(x)
return function (y){
z + y
}
}
Nous pourrions appeler cette fonction une fois, puis faire circuler le résultat à utiliser dans de nombreux endroits, ce qui signifie que nous ne faisons qu'une fois les choses coûteuses en calcul:
let finishTheJob = doTheHardStuff(10)
finishTheJob(20)
finishTheJob(30)
Nous pouvons obtenir des abstractions de la même manière.
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 d'avoir certains de leurs arguments initiaux partiellement appliqués.
map
une fonction f
sur une liste de listes, xss
vous pouvez le faire map (map f) xss
.
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 la distinction entre l'objet partiel et la fonction dans la façon dont Python l'implémente.
Currying traduit une fonction de callable as f(a, b, c)
en callable as f(a)(b)(c)
.
Sinon, le curry est lorsque vous décomposez une fonction qui prend plusieurs arguments en une série de fonctions qui font partie des arguments.
Littéralement, le curry est une transformation des 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.
Faisons la fonction curry qui effectue le curry pour les fonctions à deux arguments. En d'autres termes, curry(f)
pour deux arguments, f(a, b)
cela se traduit parf(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.
curry(func)
est un wrapper function(a)
.sum(1)
, l'argument est enregistré dans l'environnement lexical et un nouveau wrapper est renvoyé function(b)
.sum(1)(2)
finalement, il function(b)
fournit 2 et il passe l'appel à la somme multi-arguments d'origine.Si vous comprenez que partial
vous êtes à mi-chemin. L'idée de partial
est de pré-appliquer des arguments à une fonction et de redonner 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.
Dans Clojure +
est une fonction mais pour que les choses soient clairement claires:
(defn add [a b] (+ a b))
Vous savez peut-être que la inc
fonction ajoute simplement 1 au nombre passé.
(inc 7) # => 8
Construisons-le nous-mêmes en utilisant partial
:
(def inc (partial add 1))
Ici, nous renvoyons une autre fonction qui a 1 chargé dans le premier argument de add
. Comme add
prend deux arguments, la nouvelle inc
fonction ne veut que l' b
argument - pas 2 arguments comme avant car 1 a déjà été partiellement appliqué. C'est donc partial
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 les arguments du général au spécifique. Cela facilite la réutilisation de telles fonctions à partir desquelles on peut construire d'autres fonctions.
Imaginez maintenant si le langage était suffisamment intelligent pour comprendre de manière introspective qui add
voulait deux arguments. Lorsque nous lui avons transmis un argument, plutôt que de rechigner, que se passerait-il si la fonction appliquait partiellement l'argument que nous lui avons transmis en notre nom, 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 certaines langues se comportent. Il est exceptionnellement utile lorsque l'on souhaite composer des fonctions en transformations plus importantes. Cela conduirait à des 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 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 2 paramètres, 3 paramètres et 4 fonctions de paramètres.
Comme toutes les autres réponses, le curry aide à créer des fonctions partiellement appliquées. Javascript ne fournit pas de support natif pour le curry automatique. Par conséquent, les exemples fournis ci-dessus peuvent ne pas aider au codage pratique. Il y a 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, 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 de l'utiliser. 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é de fermeture (fermeture), de les stocker dans une autre fonction et de la traiter comme une valeur de retour, et ces fonctions forment une chaîne, et les arguments finaux sont passés pour terminer l'opération.
L'avantage de ceci est qu'il 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. La division du code en morceaux plus petits le rendrait également réutilisable.
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 bien pour rendre le code complexe soigné et gérer les méthodes non synchronisées, etc.
Une fonction curry est appliquée à plusieurs listes d'arguments, au lieu d'une seule.
Voici une fonction régulière, non curry, 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 qui est curry. Au lieu d'une liste de deux paramètres 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, c'est que lorsque vous appelez curriedSum
, vous obtenez en fait deux invocations de fonctions traditionnelles consécutives. 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 deuxième fonction prend le paramètre Int
y
.
Voici une fonction nommée first
qui fait dans l'esprit ce que curriedSum
ferait la première invocation de fonction traditionnelle :
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 passer à 1 - donne 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 actuellement qu'un seul paramètre:
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 son envoi, performAsyncRequest(_:)
vous devrez créer un autre lambda / fermeture pour envoyer celui-ci à la fonction.
func callback
retourne? Il est appelé @ callback(str)
donc let callback = callback(str)
, le rappel n'est que la valeur de retour defunc callback
func callback(_:data:)
accepte deux paramètres, ici je ne lui en donne qu'un, le String
, donc il attend le suivant ( NSData
), c'est pourquoi maintenant let callback
une autre fonction attend que les données soient transmises
Voici l'exemple de générique et la version la plus courte pour le curry de fonction avec n non. de params.
const add = a => b => b ? add(a + b) : a;
const add = a => b => b ? add(a + b) : a;
console.log(add(1)(2)(3)(4)());
Vous trouverez ici une explication simple de l'implémentation du curry en C #. Dans les commentaires, j'ai essayé de montrer comment 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);
Le curry est l'une des fonctions d'ordre supérieur de Java Script.
Le currying est une fonction de nombreux arguments qui est réécrite de telle sorte qu'il prend le premier argument et renvoie une fonction qui à son tour utilise les arguments restants et renvoie la valeur.
Confus?
Voyons un exemple,
function add(a,b)
{
return a+b;
}
add(5,6);
Ceci est similaire à la fonction de curry suivante,
function add(a)
{
return function(b){
return a+b;
}
}
var curryAdd = add(5);
curryAdd(6);
Alors, que signifie ce code?
Maintenant, relisez la définition,
Le currying est une fonction de nombreux arguments qui est réécrite de manière à prendre le premier argument et à renvoyer une fonction qui à son tour utilise les arguments restants et renvoie la valeur.
Encore confus? Laissez-moi vous expliquer en profondeur!
Lorsque vous appelez cette fonction,
var curryAdd = add(5);
Il vous renverra une fonction comme celle-ci,
curryAdd=function(y){return 5+y;}
Donc, cela s'appelle des fonctions d'ordre supérieur. Cela signifie que l'invocation d'une fonction à son tour renvoie une autre fonction est une définition exacte pour une fonction d'ordre supérieur. C'est le plus grand avantage de la légende, Java Script. Revenez donc au curry,
Cette ligne passera le deuxième argument à la fonction curryAdd.
curryAdd(6);
qui à son tour résulte,
curryAdd=function(6){return 5+6;}
// Which results in 11
J'espère que vous comprenez l'utilisation du curry ici. Donc, en ce qui concerne les avantages,
Pourquoi Currying?
Il utilise la réutilisabilité du code. Moins de code, moins d'erreur. Vous pouvez demander en quoi c'est moins de code?
Je peux le prouver avec le script ECMA 6 nouvelles fonctions de flèche de fonctionnalité.
Oui! ECMA 6, fournissez-nous la merveilleuse fonctionnalité appelée fonctions de flèche,
function add(a)
{
return function(b){
return a+b;
}
}
Avec l'aide de la fonction flèche, nous pouvons écrire la fonction ci-dessus comme suit,
x=>y=>x+y
Cool non?
Donc, moins de code et moins de bugs !!
Avec l'aide de ces fonctions d'ordre supérieur, on peut facilement développer un code sans bogue.
Je te met au défi!
J'espère que vous avez compris ce qui est curry. N'hésitez pas à commenter ici si vous avez besoin de clarifications.
Merci bonne journée!
Il existe un exemple de "Currying in ReasonML".
let run = () => {
Js.log("Curryed function: ");
let sum = (x, y) => x + y;
Printf.printf("sum(2, 3) : %d\n", sum(2, 3));
let per2 = sum(2);
Printf.printf("per2(3) : %d\n", per2(3));
};
curry
etuncurry
de Haskell. Ce qui est important ici, c'est que ces isomorphismes sont fixés au préalable, et donc "intégrés" dans le langage.