Question

Je vois assez souvent sur Internet diverses plaintes selon lesquelles les exemples de personnes qui ont fait du curry ne sont pas du curry, mais ne sont en réalité qu'une application partielle.

Je n'ai pas trouvé d'explication valable sur ce qu'est une application partielle ou sur sa différence avec le curry. Il semble y avoir une confusion générale, des exemples équivalents étant décrits comme currying à certains endroits et application partielle à d’autres.

Quelqu'un pourrait-il me fournir une définition des deux termes et des précisions sur leur différence?

Était-ce utile?

La solution

Currying est en train de convertir une seule fonction d’arguments n en n fonctions avec un seul argument. Étant donné la fonction suivante:

function f(x,y,z) { z(x(y));}

Au curry, devient:

function f(x) { lambda(y) { lambda(z) { z(x(y)); } } }

Pour obtenir la pleine application de f (x, y, z), vous devez procéder comme suit:

f(x)(y)(z);

De nombreux langages fonctionnels vous permettent d'écrire f x y z . Si vous appelez uniquement fxy ou f (x) (y) , vous obtenez une fonction partiellement appliquée: la valeur renvoyée est une fermeture de lambda (z). {z (x (y))}} avec passé les valeurs de x et y à f (x, y) .

Pour appliquer une application partielle, vous pouvez définir les fonctions en tant qu'applications partielles de fonctions généralisées, telles que fold :

.
function fold(combineFunction, accumulator, 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(accumulator,list) {/*...*/}
f(0,list) //returns 10
@g = f(0) //same as sum
g(list)  //returns 10

Autres conseils

Le moyen le plus simple de voir en quoi elles 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 retourne un nombre en sortie, par exemple. Ajouter (7, 5) renvoie 12 . Dans ce cas:

  • L'application partielle de la fonction Ajouter avec une valeur 7 nous donnera une nouvelle fonction en sortie. Cette fonction elle-même prend 1 chiffre en entrée et génère 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
    

    Nous pouvons donc faire ceci:

    f2 = Partial(Add, 7);
    f2(5); // returns 12;
           // f2(7)(5) is just a syntactic shortcut
    
  • Currying , la fonction Ajouter nous donnera une nouvelle fonction en sortie. Cette fonction prend 1 chiffre en entrée et génère pour le moment 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
    

    Nous pouvons donc 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. Le curry prend exactement 1 entrée, alors que l'application partielle en prend 2 (ou plus).

Même s'ils renvoient tous deux une fonction en sortie, les fonctions renvoyées ont des formes totalement différentes, comme indiqué ci-dessus.

Remarque: ceci provient de Bases de F # , un excellent article d'introduction pour les développeurs .NET. dans la programmation fonctionnelle.

  

Currying signifie décomposer une fonction comportant 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 d'origine. Le curry est probablement le plus   sujet difficile pour les développeurs novices en programmation fonctionnelle, en particulier parce   est souvent confondu avec une application partielle. 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   langues impératives. 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 la   même résultat que si vous aviez appelé multiplier par x égal à 2 et y   égal à cet argument. En termes de comportement, il en va de même.   code:

let double2 z = multiply 2 z
     

Souvent, les gens disent à tort que multiplier est un curry pour former un double.   Mais ce n'est qu'un peu vrai. La fonction de multiplication est au curry, mais   cela se produit quand il est défini parce que les fonctions en F # sont au curry par   défaut. Lorsque la double fonction est créée, il est plus précis de   dire que la fonction multiplie est partiellement appliquée.

     

La fonction multiplier est en réalité une série de deux fonctions. La première   fonction prend un argument int et retourne une autre fonction,   liant efficacement x à une valeur spécifique. Cette fonction accepte également   un argument int que vous pouvez considérer comme la valeur à lier à y. Après   appelant cette seconde fonction, x et y sont liés, le résultat est donc   le produit de x et y tel que défini dans le corps de double.

     

Pour créer double, la première fonction de la chaîne de multiplier   fonctions est évalué pour appliquer partiellement multiplier. La résultante   fonction est appelée double. Lorsque 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, " Une application à fonction partielle ne fonctionne pas & ; a donné la meilleure explication. J'ai trouvé. Je ne peux pas dire que la différence pratique m'est particulièrement évidente, mais je ne suis pas un expert en PF ...

Une autre page très utile (dont j’avoue que je n’ai pas encore entièrement lu la lecture) est " Application de currying et partielle avec fermetures de Java ".

Cela ressemble à une paire de termes très confus, remarquez.

J'ai répondu à cette question dans un autre fil de discussion https://stackoverflow.com/a/12846865/1685865 . En bref, l’application de fonctions partielles consiste à corriger certains arguments d’une fonction multivariable donnée pour obtenir une autre fonction comportant moins d’arguments, tandis que Currying consiste à transformer une fonction de N arguments en une fonction unaire qui renvoie une fonction unaire ... Le currying est présenté à la fin de cet article.]

Le curry présente un intérêt principalement théorique: on peut exprimer des calculs en utilisant uniquement des fonctions unaires (c'est-à-dire que toutes les fonctions sont unaires). En pratique et en tant que sous-produit, il s'agit d'une technique qui peut rendre triviales de nombreuses applications fonctionnelles partielles utiles (mais pas toutes), si le langage comporte des fonctions curryings. Encore une fois, ce n'est pas le seul moyen de mettre en œuvre des applications partielles. Vous pouvez donc rencontrer des scénarios dans lesquels une application partielle est effectuée d'une autre manière, mais les gens le prennent pour du Currying.

(Exemple de currying)

En pratique, on n'écrirait pas simplement

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

pour le currying.

Le currying est une fonction de un argument qui prend une fonction f et renvoie une nouvelle fonction h . Notez que h prend un argument de X et renvoie une fonction qui mappe Y en Z :

curry(f) = h 
f: (X x Y) -> Z 
h: X -> (Y -> Z)

L’application partielle est une fonction de deux (ou plus) arguments qui prend une fonction f et d’un ou de plusieurs arguments supplémentaires pour f et renvoie une nouvelle fonction g :

part(f, 2) = g
f: (X x Y) -> Z 
g: Y -> Z

La confusion survient car avec une fonction à deux arguments, l'égalité suivante est vérifiée:

partial(f, a) = curry(f)(a)

Les deux parties produiront la même fonction à un argument.

L’égalité n’est pas vraie pour les fonctions d’architecture supérieure car dans ce cas, currying renverra une fonction à un argument, alors que l’application partielle renverra une fonction à arguments multiples.

La différence réside également dans le comportement, alors que currying transforme de manière récursive (une fois pour chaque argument) la totalité de la fonction d'origine, l'application partielle ne constitue qu'un remplacement en une étape.

Source: Wikipedia au curry .

La différence entre l'application curry et l'application partielle peut être mieux illustrée à l'aide de l'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é inférieure; 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 renvoie immédiatement le résultat lorsqu’il est invoqué , pas une autre fonction dans la chaîne de currying. Donc, si vous voyez quelque chose comme partial (2) (3) , ce n'est pas une application partielle dans la réalité.

Lectures supplémentaires:

Pour moi, les applications partielles doivent créer une nouvelle fonction dans laquelle les arguments utilisés sont complètement intégrés à la fonction résultante.

La plupart des langages fonctionnels implémentent le currying en renvoyant une fermeture: n'évaluez pas sous lambda lorsqu'il est partiellement appliqué. Donc, pour que l’application partielle soit intéressante, nous devons faire la différence entre currying et application partielle et considérer l’application partielle comme currying plus évaluation sous lambda.

Je pourrais très bien me tromper ici, car je n’ai pas de formation solide en mathématiques théoriques ni en programmation fonctionnelle, mais de ma brève incursion en FP, il semble que currying ait tendance à transformer une fonction de N arguments en N fonctions de un argument, alors que l'application partielle [en pratique] fonctionne mieux avec des fonctions variadiques avec un nombre indéterminé d'arguments. Je sais que certains des exemples des réponses précédentes défient cette explication, mais cela m’a le plus aidé à séparer les concepts. Considérez cet exemple (écrit en CoffeeScript pour plus de concision, mes excuses si cela gêne davantage, 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

Ceci est évidemment un exemple artificiel, mais notez que l'application partielle d'une fonction qui accepte un nombre quelconque d'arguments nous permet d'exécuter une fonction mais avec quelques données préliminaires. Currying une fonction est similaire, mais nous permet d’exécuter une fonction de paramètre N par morceaux jusqu’à ce que tous les N paramètres soient pris en compte, mais seulement jusqu’à ce qu’ils soient pris en compte.

Encore une fois, voici ce que je retiens de ce que j'ai lu. Si quelqu'un n'est pas d'accord, j'apprécierais un commentaire expliquant pourquoi plutôt qu'un vote négatif immédiat. De plus, si CoffeeScript est difficile à lire, visitez le site coffeescript.org, cliquez sur "essayer 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 souvent eu cette question lors de mon apprentissage et depuis, je l'ai posée à plusieurs reprises. La façon la plus simple de décrire la différence est que les deux sont identiques :) Laissez-moi vous expliquer ... il existe é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 consiste à ajouter deux nombres. En pseudocode (en réalité JS sans mots-clés), la fonction de base peut être la suivante:

add = (x, y) => x + y

Si je voulais un " addOne " fonction, je pourrais l’appliquer partiellement ou le curry:

addOneC = curry(add, 1)
addOneP = partial(add, 1)

Maintenant, leur utilisation est claire:

addOneC(2) #=> 3
addOneP(2) #=> 3

Alors, quelle est la différence? Eh bien, c’est subtile, mais une application partielle implique de fournir certains arguments et la fonction renvoyée exécutera ensuite la fonction principale lors de la prochaine invocation, alors que currying attendra jusqu’à ce qu’il dispose de 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 prochaine fois que vous appelez la méthode, celle-ci s'exécutera, laissant tous les arguments non définis non définis; utilisez currying lorsque vous souhaitez renvoyer en permanence 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

J'espère que ça aide!

UPDATE: Certaines langues ou implémentations de lib vous permettront de passer une arité (nombre total d’arguments dans l’évaluation finale) à la mise en œuvre partielle de l’application, ce qui pourrait confondre mes deux descriptions dans un désordre confus ... mais à ce stade, deux techniques sont largement interchangeables.

Réponse simple

Curry: vous permet d'appeler une fonction et de la diviser en plusieurs appels, en fournissant un argument par appel.

Partiel: vous permet d'appeler une fonction en la divisant en plusieurs appels et en fournissant plusieurs arguments par appel.

Conseils simples

Les deux vous permettent d'appeler une fonction fournissant moins d'arguments (ou mieux, les fournissant de manière cumulative). En fait, les deux associent (à chaque appel) une valeur spécifique à des arguments spécifiques de la fonction.

La vraie différence peut être constatée 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 sont toujours les mêmes? Il suffit de lier quelques valeurs pour la fonction

processSubject = _.partial(process, my_context, my_success, my_error)

et appelez-le sur sujet1 et foobar avec

processSubject('subject1');
processSubject('foobar');

Confortable, n'est-ce pas? ??

Avec currying , vous devez passer un argument à la fois

.
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');

Clause de non-responsabilité

J'ai sauté toutes les explications académiques / mathématiques. Parce que je ne le sais pas. Peut-être que ça a aidé ??

Il existe d’autres bonnes réponses ici, mais je pense que cet exemple (selon ma compréhension) en Java pourrait être utile à 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 souhaitez copier et coller, les éléments suivants sont plus bruyants mais plus conviviaux, car les types sont plus clairs:

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 cela, j’ai confondu currying et currying. 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.

On ne définit pas très clairement le durcissement (ou plutôt, il existe des définitions "contradictoires" qui reflètent toutes l'esprit de l'idée). Fondamentalement, cela signifie transformer une fonction prenant plusieurs arguments en une fonction prenant un seul argument. Par exemple,

(+) :: Int -> Int -> Int

Maintenant, comment transformez-vous cela en une fonction qui prend un seul argument? Vous trichez, bien sûr!

plus :: (Int, Int) -> Int

Notez que plus prend désormais un seul argument (composé de deux choses). Super!

Quel est le but de cela? 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, tout en obtenant ce que vous attendez. Et, en fait, la tuyauterie nécessaire pour le faire existe déjà, de sorte que vous n’ayez pas à faire des choses comme une correspondance explicite de modèle. Tout ce que vous avez à faire est de:

(uncurry (+)) (1,2)

Alors, quelle est l'application de fonction partielle? C'est une façon différente de transformer une fonction à deux arguments en une fonction à un argument. Cela fonctionne différemment si. Encore une fois, prenons (+) à titre d'exemple. Comment pourrions-nous en faire une fonction prenant un seul Int comme argument? Nous trichons!

((+) 0) :: Int -> Int

C'est la fonction qui ajoute zéro à un Int.

((+) 1) :: Int -> Int

ajoute 1 à tout Int. Etc. Dans chacun de ces cas, (+) est "partiellement appliqué".

Je suppose que la plupart des gens qui posent cette question connaissent déjà les concepts de base et qu’ils n’ont donc pas besoin de parler de cela. C'est le chevauchement qui crée la confusion.

Vous pourrez peut-être utiliser pleinement les concepts, mais vous les comprenez ensemble comme ce flou conceptuel amorphe pseudo-atomique. Ce qui manque, c’est de savoir où se trouve la limite entre eux.

Au lieu de définir chacun d'eux, il est plus facile de mettre en évidence leurs différences et # 8212; la limite.

Le currying est lorsque vous définissez la fonction.

Application partielle est lorsque vous appelez la fonction.

Application parle en mathématique pour appeler une fonction.

L'application

Partielle nécessite d'appeler une fonction curry et d'obtenir une fonction comme type de retour.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top