J'ai vu des références aux fonctions curry dans plusieurs articles et blogs, mais je n'arrive pas à trouver une bonne explication (ou du moins une explication qui ait du sens !)
Dans un sens pratique, comment puis-je utiliser ce concept ?
J'ai vu des références aux fonctions curry dans plusieurs articles et blogs, mais je n'arrive pas à trouver une bonne explication (ou du moins une explication qui ait du sens !)
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 étriller 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)
déclaration. 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 la fonction add3
pour ajouter 3 à 4, produisant à nouveau 7 comme résultat.
@Strawberry, disons par exemple que vous avez une liste de chiffres dans un fichier [1, 2, 3, 4, 5]
que vous souhaitez multiplier par un nombre arbitraire. En Haskell, je peux écrire map (* 5) [1, 2, 3, 4, 5]
pour multiplier toute la liste par 5
et génère ainsi la liste [5, 10, 15, 20, 25]
.
Je comprends ce que fait la fonction map, mais je ne suis pas sûr de comprendre le point que vous essayez d'illustrer pour moi. Vous voulez dire que la fonction map représente le concept de currying ?
Il peut s'agir d'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 comme ça :
let addTen = add(10);
Lorsque cela fonctionne, le 10
est transmis en tant que 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 };
Donc quand vous appelez
addTen();
vous appelez 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 qu'on passe. 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...
La question qui suit est évidemment : pourquoi diable voudriez-vous faire ça ? Cela transforme ce qui était une opération enthousiaste x + y
en une qui peut être parcourue paresseusement, ce qui signifie que nous pouvons faire au moins deux choses 1. mettre en cache les opérations coûteuses 2. réaliser des abstractions dans le paradigme fonctionnel.
Imaginez que notre fonction curry ressemble à ça :
let doTheHardStuff = function(x) {
let z = doSomethingComputationallyExpensive(x)
return function (y){
z + y
}
}
Nous pourrions appeler cette fonction une fois, puis transmettre le résultat pour qu'il soit utilisé à de nombreux endroits, ce qui signifie que nous n'effectuons qu'une seule fois les opérations coûteuses en termes de calcul :
let finishTheJob = doTheHardStuff(10)
finishTheJob(20)
finishTheJob(30)
Nous pouvons obtenir des abstractions d'une manière similaire.
Le currying 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, 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 de la manière suivante:-
f 1 2 3
Retours 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 entre la fonction que vous souhaitez curer et retourne une fonction qui prend un seul argument et retourne la fonction spécifiée avec le premier argument défini comme l'argument d'entrée.
En utilisant notre exemple précédent, nous pouvons obtenir une courbe de f ainsi:-
let curryf = curry f
Nous pouvons alors faire ce qui suit:-
let f1 = curryf 1
Ce qui nous donne une fonction f1 qui est équivalente à f1 y z = 1 + y + z. Cela signifie que nous pouvons faire ce qui suit:-
f1 2 3
Ce qui donne 6.
Ce processus est souvent confondu avec "l'application partielle de la fonction" qui peut être définie comme suit:-.
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 prend la fonction et le(s) paramètre(s) et renvoie une fonction qui nécessite un ou plusieurs paramètres en moins, et comme le montrent les deux exemples précédents, elle est mise en œuvre directement dans la définition de la fonction F# standard, de sorte que nous pourrions 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 currying et l'application d'une fonction partielle est la suivante:-
Currying prend une fonction et fournit une nouvelle fonction acceptant un seul argument, et retournant la fonction spécifiée avec son premier argument défini à cet argument. Cela nous permet de représenter les fonctions à paramètres multiples 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 d'une fonction partielle est plus directe - elle prend une fonction et un ou plusieurs arguments et renvoie une fonction dont les n premiers arguments correspondent aux 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
Une fonction curry est une fonction de plusieurs arguments réécrite 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.
"Cela permet aux fonctions de plusieurs arguments d'avoir certains de leurs arguments initiaux partiellement appliqués." - pourquoi est-ce bénéfique ?
@acarlon Les fonctions sont souvent appelées à plusieurs reprises avec un ou plusieurs arguments identiques. Par exemple, si vous voulez map
une fonction f
sur une liste de listes xss
vous pouvez faire map (map f) xss
.
Le curage consiste à convertir une fonction d'arité N en N fonctions d'arité 1. Le site arity
de la fonction est le nombre d'arguments qu'elle requiert.
Voici la définition officielle :
curry(f) :: (a,b,c) -> f(a) -> f(b)-> f(c)
Voici un exemple concret qui a du sens :
Vous allez au guichet automatique pour obtenir de l'argent. Vous passez votre carte, entrez votre numéro de code et faites votre choix, puis vous appuyez sur la touche Entrée pour soumettre le "montant" correspondant à la demande.
voici la fonction normale pour retirer de l'argent.
const withdraw=(cardInfo,pinNumber,request){
// process it
return request.amount
}
Dans cette implémentation, la fonction s'attend à ce que nous entrions tous les arguments en même temps. Nous devions faire glisser la carte, entrer le code pin et faire la demande, puis la fonction s'exécutait. Si l'une de ces étapes posait problème, on le découvrait après avoir entré tous les arguments. Avec la fonction curry, nous pouvons créer des fonctions d'arité supérieure, pures et simples. Les fonctions pures nous aideront à déboguer facilement notre code.
il s'agit d'un Atm avec fonction curry :
const withdraw=(cardInfo)=>(pinNumber)=>(request)=>request.amount
ATM, prend la carte en entrée et renvoie une fonction qui attend le pinNumber et cette fonction renvoie une fonction qui accepte l'objet de demande et après le processus réussi, vous obtenez le montant que vous avez demandé. À chaque étape, si vous avez eu une erreur, vous pourrez facilement prévoir ce qui n'a pas fonctionné. Imaginons que vous saisissiez la carte et que vous obteniez une erreur, vous savez qu'elle est liée soit à la carte, soit à la machine, mais pas au numéro de code. Ou si vous avez saisi le code pin et qu'il n'est pas accepté, vous savez que vous avez saisi un mauvais code pin. Vous pourrez facilement corriger l'erreur.
En outre, chaque fonction est réutilisable, de sorte que vous pouvez utiliser les mêmes fonctions dans différentes parties de votre projet.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.