Quelle est la différence entre le currying et l'application partielle?
Je vois assez souvent sur Internet diverses plaintes selon lesquelles d'autres exemples de currying ne sont pas currying, mais ne sont en fait qu'une application partielle.
Je n'ai pas trouvé une explication décente de ce qu'est l'application partielle, ou comment elle diffère de currying. Il semble y avoir une confusion générale, avec des exemples équivalents décrits comme currying dans certains endroits, et une application partielle dans d'autres.
Quelqu'un pourrait-il me fournir une définition des deux termes et des détails de la façon dont ils diffèrent-ils?
13 réponses
Currying convertit une seule fonction de N arguments en N fonctions avec un seul argument chacune. Étant donné la fonction suivante:
function f(x,y,z) { z(x(y));}
Quand curry, devient:
function f(x) { lambda(y) { lambda(z) { z(x(y)); } } }
Afin d'obtenir l'application intégrale de f(x,y,z), vous devez faire ceci:
f(x)(y)(z);
De Nombreux langages fonctionnels vous permettent d'écrire f x y z
. Si vous appelez seulement f x y
ou f (x) (y) alors vous obtenez une fonction partiellement appliquée-la valeur de retour est une fermeture de lambda(z){z(x(y))}
avec passé-dans les valeurs de x et y à f(x,y)
.
Un moyen d'utiliser l'application partielle est de définir des fonctions que les applications partielles de des fonctions généralisées, comme pli:
function fold(combineFunction, accumalator, list) {/* ... */}
function sum = curry(fold)(lambda(accum,e){e+accum}))(0);
function length = curry(fold)(lambda(accum,_){1+accum})(empty-list);
function reverse = curry(fold)(lambda(accum,e){concat(e,accum)})(empty-list);
/* ... */
@list = [1, 2, 3, 4]
sum(list) //returns 10
@f = fold(lambda(accum,e){e+accum}) //f = lambda(accumaltor,list) {/*...*/}
f(0,list) //returns 10
@g = f(0) //same as sum
g(list) //returns 10
La façon la plus simple de voir comment ils diffèrent est de considérer un exemple réel. Supposons que nous ayons une fonction Add
qui prend 2 nombres en entrée et renvoie un nombre en sortie, par exemple Add(7, 5)
renvoie 12
. Dans ce cas:
-
L'application partielle de la fonction
Add
avec une valeur7
nous donnera une nouvelle fonction en sortie. Cette fonction prend 1 nombre en entrée et retourne un nombre. En tant que tel:Partial(Add, 7); // returns a function f2 as output // f2 takes 1 number as input and returns a number as output
Donc nous pouvons faire ce:
f2 = Partial(Add, 7); f2(5); // returns 12; // f2(7)(5) is just a syntactic shortcut
-
Nourrissage la fonction
Add
va nous donner une nouvelle fonction de sortie. Cette fonction elle-même prend 1 Nombre comme entrée et sorties encore une autre nouvelle fonction. Cette troisième fonction prend alors 1 nombre en entrée et renvoie un nombre en sortie. En tant que tel:Curry(Add); // returns a function f2 as output // f2 takes 1 number as input and returns a function f3 as output // i.e. f2(number) = f3 // f3 takes 1 number as input and returns a number as output // i.e. f3(number) = number
Donc nous pouvons faire ceci:
f2 = Curry(Add); f3 = f2(7); f3(5); // returns 12
En d'autres termes, "currying" et "application partielle" sont deux fonctions totalement différentes. Currying prend exactement 1 entrée, alors que l'application partielle prend 2 (ou plus) entrées.
Même si elles renvoient toutes deux une fonction en sortie, les fonctions renvoyées sont de formes totalement différentes comme démontré ci-dessus.
Note: Ceci a été tiré de F # Basics un excellent article d'introduction pour les développeurs. net entrant dans la programmation fonctionnelle.
Currying signifie briser une fonction avec de nombreux arguments en une série de fonctions qui prennent chacune un argument et produisent finalement le même résultat que la fonction originale. Nourrissage est probablement le plus sujet difficile pour les développeurs nouveaux à la programmation fonctionnelle, en particulier parce qu'il est souvent confondu avec partiel application. Vous pouvez voir les deux au travail dans cet exemple:
let multiply x y = x * y let double = multiply 2 let ten = double 5
Tout de suite, vous devriez voir un comportement différent de la plupart des langages impératifs. La deuxième instruction crée une nouvelle fonction appelé double en passant un argument à une fonction qui en prend deux. Le résultat est une fonction qui accepte un argument int et donne le même sortie que si vous aviez appelé multiplier avec x égal à 2 et y égale à celle de l'argument. En termes de comportement, c'est la même chose que ce code:
let double2 z = multiply 2 z
Souvent, les gens disent à tort que multiplier est curry pour former double. Mais ce n'est que quelque peu vrai. La fonction de multiplication est curry, mais cela se produit quand il est défini parce que les fonctions dans F# sont curry par défaut. Lorsque la double fonction est créée, il est plus précis de dites que la fonction de multiplication est partiellement appliquée.
La fonction de multiplication est vraiment une série de deux fonctions. Première la fonction prend un argument int et renvoie une autre fonction, lier efficacement x à une valeur spécifique. Cette fonction accepte également un argument int que vous pouvez considérer comme la valeur à lier à Y. After en appelant cette deuxième fonction, x et y sont tous deux liés, donc le résultat est le produit de x et y tel que défini dans le corps de double.
Pour créer double, la première fonction dans la chaîne de multiplier les fonctions sont évaluées pour appliquer partiellement multiplier. Le résultant la fonction reçoit le nom double. Lors d'un double est évalué, il utilise son argument avec la valeur partiellement appliquée pour créer le résultat.
Question intéressante. Après un peu de recherche, "L'Application de fonction partielle n'est pas en cours" a donné la meilleure explication que j'ai trouvée. Je ne peux pas dire que la différence pratique est particulièrement évidente pour moi, mais alors je ne suis pas un expert en FP...
Une autre page utile (que j'avoue ne pas avoir encore complètement lue) est "Currying et application partielle avec des fermetures Java" .
Il semble que ce soit une paire de termes largement confus, rappelez-vous.
J'ai répondu à cela dans un autre fil https://stackoverflow.com/a/12846865/1685865 . En bref, l'application de fonction partielle consiste à fixer certains arguments d'une fonction multivariable donnée pour produire une autre fonction avec moins d'arguments, tandis que Currying consiste à transformer une fonction de n arguments en une fonction unaire qui renvoie une fonction unaire...[Un exemple de Currying est montré à la fin de ce post.]
Le Currying est surtout d'intérêt théorique: on peut exprimer les calculs en utilisant uniquement des fonctions unaires (c'est-à-dire chaque fonction est unaire). En pratique et en tant que sous-produit, c'est une technique qui peut rendre triviales de nombreuses applications fonctionnelles partielles utiles (mais pas toutes), si le langage a des fonctions au curry. Encore une fois, ce n'est pas le seul moyen de mettre en œuvre des applications partielles. Ainsi, vous pourriez rencontrer des scénarios où l'application partielle est faite d'une autre manière, mais les gens le confondent avec Currying.
(Exemple de Nourrissage)
En pratique, on ne se contenterait pas d'écrire
lambda x: lambda y: lambda z: x + y + z
Ou le javascript équivalent
function (x) { return function (y){ return function (z){ return x + y + z }}}
Au Lieu de
lambda x, y, z: x + y + z
Par souci de Currying.
La différence entre curry et application partielle peut être mieux illustrée par cet exemple JavaScript suivant:
function f(x, y, z) {
return x + y + z;
}
var partial = f.bind(null, 1);
6 === partial(2, 3);
L'application partielle entraîne une fonction d'arité plus petite; dans l'exemple ci-dessus, f
a une arité de 3 alors que partial
n'a qu'une arité de 2. Plus important encore, une fonction partiellement appliquée retournerait le résultat immédiatement après avoir été invoke , Pas une autre fonction dans la chaîne de currying. Donc, si vous voyez quelque chose comme partial(2)(3)
, ce n'est pas le cas application partielle en réalité.
Autres lectures:
Pour moi, l'application partielle doit créer une nouvelle fonction, où les arguments utilisés sont complètement intégrés dans la fonction obtenue.
La plupart des langages fonctionnels implémentent le currying en retournant une fermeture: ne pas évaluer sous lambda lorsqu'il est partiellement appliqué. Donc, pour que l'application partielle soit intéressante, nous devons faire une différence entre currying et application partielle et considérer l'application partielle comme currying plus evaluation sous lambda.
Je pourrais me tromper ici, car je n'ai pas une solide expérience en mathématiques théoriques ou en programmation fonctionnelle, mais de ma brève incursion dans FP, il semble que currying tend à transformer une fonction de n arguments en n Fonctions d'un argument, alors que l'application partielle [en pratique] fonctionne mieux avec des fonctions variadiques avec un nombre indéterminé Je sais que certains des exemples dans les réponses précédentes défient cette explication, mais cela m'a le plus aidé à séparer le concept. Considérez cet exemple (écrit en CoffeeScript pour la concision, mes excuses si cela confond plus loin, mais veuillez demander des éclaircissements, si nécessaire):
# partial application
partial_apply = (func) ->
args = [].slice.call arguments, 1
-> func.apply null, args.concat [].slice.call arguments
sum_variadic = -> [].reduce.call arguments, (acc, num) -> acc + num
add_to_7_and_5 = partial_apply sum_variadic, 7, 5
add_to_7_and_5 10 # returns 22
add_to_7_and_5 10, 11, 12 # returns 45
# currying
curry = (func) ->
num_args = func.length
helper = (prev) ->
->
args = prev.concat [].slice.call arguments
return if args.length < num_args then helper args else func.apply null, args
helper []
sum_of_three = (x, y, z) -> x + y + z
curried_sum_of_three = curry sum_of_three
curried_sum_of_three 4 # returns a function expecting more arguments
curried_sum_of_three(4)(5) # still returns a function expecting more arguments
curried_sum_of_three(4)(5)(6) # returns 15
curried_sum_of_three 4, 5, 6 # returns 15
C'est évidemment un exemple artificiel, mais remarquez que partiellement l'application d'une fonction qui accepte un nombre quelconque d'arguments nous permet d'exécuter une fonction, mais avec certaines données préliminaires. Currying une fonction est similaire mais nous permet d'exécuter une fonction n-paramètre en morceaux jusqu'à ce que, mais seulement jusqu'à ce que, tous les N paramètres sont pris en compte.
Encore une fois, c'est ma prise de choses que j'ai lues. Si quelqu'un n'est pas d'accord, j'apprécierais un commentaire sur pourquoi plutôt qu'un downvote immédiat. En outre, si le CoffeeScript est difficile à lire, veuillez visiter coffeescript.org, cliquez sur "try coffeescript" et collez dans mon code pour voir la version compilée, ce qui peut (espérons-Le) avoir plus de sens. Merci!
J'ai beaucoup eu cette question en apprenant et je l'ai depuis posée plusieurs fois. La façon la plus simple de décrire la différence est que les deux sont les mêmes :) Laissez-moi vous expliquer...il y a évidemment des différences.
L'application partielle et le currying impliquent de fournir des arguments à une fonction, peut-être pas tous en même temps. Un exemple assez canonique est l'ajout de deux nombres. En pseudocode (en fait JS sans mots-clés), la fonction de base peut être la suivante:
add = (x, y) => x + y
Si je voulais une fonction "addOne", je pourrais partiellement l'appliquer ou la curry:
addOneC = curry(add, 1)
addOneP = partial(add, 1)
Maintenant, les utiliser est clair:
addOneC(2) #=> 3
addOneP(2) #=> 3
Alors, quelle est la différence? Eh bien, c'est subtil, mais l'application partielle implique de fournir des arguments et la fonction retournée exécutera alors la fonction principale lors de l'invocation suivante alors que currying continuera à attendre jusqu'à ce qu'il ait tous les arguments nécessaires:
curriedAdd = curry(add) # notice, no args are provided
addOne = curriedAdd(1) # returns a function that can be used to provide the last argument
addOne(2) #=> returns 3, as we want
partialAdd = partial(add) # no args provided, but this still returns a function
addOne = partialAdd(1) # oops! can only use a partially applied function once, so now we're trying to add one to an undefined value (no second argument), and we get an error
En bref, utilisez une application partielle pour préremplir certaines valeurs, sachant que la suivante lorsque vous appelez la méthode, elle s'exécutera, laissant indéfini tous les arguments non fournis; utilisez currying lorsque vous voulez retourner continuellement une fonction partiellement appliquée autant de fois que nécessaire pour remplir la signature de la fonction. Un dernier exemple artificiel:
curriedAdd = curry(add)
curriedAdd()()()()()(1)(2) # ugly and dumb, but it works
partialAdd = partial(add)
partialAdd()()()()()(1)(2) # second invocation of those 7 calls fires it off with undefined parameters
Espérons que cela aide!
UPDATE: certains langages ou implémentations lib vous permettront de passer une arity (nombre total d'arguments en évaluation finale) à l'implémentation partielle de l'application qui peut confondre mes deux descriptions dans un désordre confus...mais à ce stade, les deux techniques sont en grande partie interchangeables.
Nourrissage est une fonction de un argument qui prend une fonction f
et retourne une nouvelle fonction h
:
curry(f) = h
Application Partielle est une fonction de deux(ou plus) les arguments qui prend une fonction f
et un ou plusieurs des arguments supplémentaires à f
et retourne une nouvelle fonction g
:
part(f, 2) = g
La confusion se produit parce qu'avec une fonction à deux arguments, l'égalité suivante tient:
partial(f, a) = curry(f)(a)
Les deux parties donneront le même argument fonction.
L'égalité n'est pas vraie pour les fonctions d'arité supérieure car dans ce cas, currying renverra une fonction à un argument, alors que l'application partielle renverra une fonction à plusieurs arguments.
La différence est également dans le comportement, alors que currying transforme toute la fonction d'origine récursivement (une fois pour chaque argument), l'application partielle n'est qu'un remplacement en une étape.
Source: Wikipedia Nourrissage.
Il y a d'autres bonnes réponses ici mais je crois que cet exemple (selon ma compréhension) en Java pourrait être bénéfique pour certaines personnes:
public static <A,B,X> Function< B, X > partiallyApply( BiFunction< A, B, X > aBiFunction, A aValue ){
return b -> aBiFunction.apply( aValue, b );
}
public static <A,X> Supplier< X > partiallyApply( Function< A, X > aFunction, A aValue ){
return () -> aFunction.apply( aValue );
}
public static <A,B,X> Function< A, Function< B, X > > curry( BiFunction< A, B, X > bif ){
return a -> partiallyApply( bif, a );
}
Donc, currying vous donne une fonction à un argument pour créer des fonctions, où partial-application crée une fonction wrapper qui code en dur un ou plusieurs arguments.
Si vous voulez copier et coller, ce qui suit est plus bruyant mais plus convivial car les types sont plus indulgents:
public static <A,B,X> Function< ? super B, ? extends X > partiallyApply( final BiFunction< ? super A, ? super B, X > aBiFunction, final A aValue ){
return b -> aBiFunction.apply( aValue, b );
}
public static <A,X> Supplier< ? extends X > partiallyApply( final Function< ? super A, X > aFunction, final A aValue ){
return () -> aFunction.apply( aValue );
}
public static <A,B,X> Function< ? super A, Function< ? super B, ? extends X > > curry( final BiFunction< ? super A, ? super B, ? extends X > bif ){
return a -> partiallyApply( bif, a );
}
En écrivant ceci, j'ai confondu currying et uncurrying. Ce sont des transformations inverses sur les fonctions. Peu importe ce que vous appelez, tant que vous obtenez ce que la transformation et son inverse représentent.
Uncurrying n'est pas défini très clairement (ou plutôt, il y a des définitions "contradictoires" qui capturent toutes l'esprit de l'idée). Fondamentalement, cela signifie transformer une fonction qui prend plusieurs arguments en une fonction qui prend un seul argument. Pour exemple,
(+) :: Int -> Int -> Int
Maintenant, comment transformez-vous cela en une fonction qui prend un seul argument? Vous tentez de tricher, bien sûr!
plus :: (Int, Int) -> Int
Notez que plus prend maintenant un seul argument (qui est composé de deux choses). Super!
À quoi ça sert? Eh bien, si vous avez une fonction qui prend deux arguments, et vous avez une paire d'arguments, il est bon de savoir que vous pouvez appliquer la fonction aux arguments, et toujours obtenir ce que vous attendez. Et, en fait, la plomberie pour le faire déjà existe, de sorte que vous n'avez pas à faire des choses comme la correspondance de modèle explicite. Tout ce que vous avez à faire est:
(uncurry (+)) (1,2)
Alors, quelle est l'application de fonction partielle? C'est une manière différente de transformer une fonction dans deux arguments en une fonction avec un argument. Cela fonctionne différemment cependant. Encore une fois, prenons ( + ) comme exemple. Comment pourrions-nous le transformer en une fonction qui prend un seul Int comme argument? On triche!
((+) 0) :: Int -> Int
C'est la fonction qui ajoute zéro à N'importe quel Int.
((+) 1) :: Int -> Int
Ajoute 1 à tout Int. Etc. Dans chacun de ces cas, (+) est "partiellement".
Réponse Simple
Curry: vous permet d'appeler une fonction, en la divisant en plusieurs appels, en fournissant un argument par appel.
Partial: vous permet d'appeler une fonction, de la diviser en plusieurs appels, en fournissant plusieurs arguments par appel.
Conseils simples
Les deux vous permettent d'appeler une fonction fournissant moins d'arguments (ou, mieux, de les fournir cumulativement). En fait, les deux lient (à chaque appel) une valeur spécifique à des arguments spécifiques du fonction.
La différence réelle peut être vu lorsque la fonction a plus de 2 arguments.
Simple e(c)(échantillon)
(en Javascript)
function process(context, success_callback, error_callback, subject) {...}
Pourquoi toujours passer les arguments, comme le contexte et les rappels, s'ils seront toujours les mêmes? Il suffit de lier quelques valeurs pour la fonction
processSubject = _.partial(process, my_context, my_success, my_error)
Et de l'appeler sur sujet1 et foobar avec
processSubject('subject1');
processSubject('foobar');
Confortable, n'est-ce pas?
Avec nourrissage vous auriez besoin de passer un argument par temps
curriedProcess = _.curry(process);
processWithBoundedContext = curriedProcess(my_context);
processWithCallbacks = processWithBoundedContext(my_success)(my_error); // note: these are two sequential calls
result1 = processWithCallbacks('subject1');
// same as: process(my_context, my_success, my_error, 'subject1');
result2 = processWithCallbacks('foobar');
// same as: process(my_context, my_success, my_error, 'foobar');
Avertissement
J'ai sauté toute l'explication académique / mathématique. Car je ne le sais pas. Peut-être que cela a aidé