J'ai lu des articles sur la programmation fonctionnelle tous les jours et j'ai essayé d'appliquer autant que possible certaines pratiques. Mais je ne comprends pas ce qui est unique au curry ou en application partielle.
Prenez ce code Groovy comme exemple:
def mul = { a, b -> a * b }
def tripler1 = mul.curry(3)
def tripler2 = { mul(3, it) }
Je ne comprends pas quelle est la différence entre tripler1
et tripler2
. Ne sont-ils pas tous les deux les mêmes? Le 'currying' est supporté dans des langages fonctionnels purs ou partiels comme Groovy, Scala, Haskell etc. Mais je peux faire la même chose (curry gauche, curry droit, curry n ou application partielle) en créant simplement un autre nommé ou anonyme fonction ou fermeture qui transmettra les paramètres à la fonction d'origine (comme tripler2
) dans la plupart des langues (même C.)
Est-ce que j'ai râté quelque chose? Il y a des endroits où je peux utiliser le curry et l'application partielle dans mon application Grails mais j'hésite à le faire parce que je me demande "En quoi est-ce différent?"
Veuillez m'éclairer.
EDIT: Êtes-vous en train de dire que l'application / le curry partiel est tout simplement plus efficace que de créer / appeler une autre fonction qui transmet les paramètres par défaut à la fonction d'origine?
la source
f x y = x + y
signifie quef
c'est une fonction qui prend un paramètre int. Le résultat def x
(f
appliqué àx
) est une fonction qui prend un paramètre int. Le résultatf x y
(ou(f x) y
, c'est-f x
à- dire appliqué ày
) est une expression qui ne prend aucun paramètre d'entrée et est évaluée en réduisantx + y
.Réponses:
Le curry consiste à transformer / représenter une fonction qui prend n entrées en n fonctions qui prennent chacune 1 entrée. Une application partielle consiste à corriger certaines des entrées d'une fonction.
La motivation pour une application partielle est principalement qu'elle facilite l'écriture de bibliothèques de fonctions d'ordre supérieur. Par exemple, les algorithmes en C ++ STL prennent tous en grande partie des prédicats ou des fonctions unaires, bind1st permet à l'utilisateur de la bibliothèque de connecter des fonctions non unaires avec une valeur liée. L'écrivain de bibliothèque n'a donc pas besoin de fournir des fonctions surchargées pour tous les algorithmes qui prennent des fonctions unaires pour fournir des versions binaires
Le curry lui-même est utile car il vous donne une application partielle partout où vous le voulez gratuitement, c'est-à-dire que vous n'avez plus besoin d'une fonction comme
bind1st
appliquer partiellement.la source
currying
quelque chose de spécifique au groovy ou applicable à toutes les langues?Et l'optimiseur examinera cela et passera rapidement à quelque chose qu'il pourra comprendre. Le curry est une petite astuce intéressante pour l'utilisateur final, mais présente de bien meilleurs avantages du point de vue de la conception du langage. C'est vraiment bien de gérer toutes les méthodes comme unaires,
A -> B
où ilB
peut y avoir une autre méthode.Il simplifie les méthodes à écrire pour gérer les fonctions d'ordre supérieur. Votre analyse statique et votre optimisation dans le langage n'ont qu'un seul chemin avec lequel se comporter de manière connue. La liaison de paramètres tombe simplement hors de la conception plutôt que d'exiger des cercles pour effectuer ce comportement commun.
la source
Comme @jk. mentionné, le curry peut aider à rendre le code plus général.
Par exemple, supposons que vous disposiez de ces trois fonctions (dans Haskell):
La fonction
f
prend ici deux fonctions comme arguments, passe1
à la première fonction et transmet le résultat du premier appel à la deuxième fonction.Si nous devions appeler
f
usingq
etr
comme arguments, ce serait effectivement:où
q
serait appliqué à1
et retourner une autre fonction (commeq
c'est curry); cette fonction retournée serait alors passée àr
son argument pour recevoir un argument de3
. Le résultat serait une valeur de9
.Maintenant, disons que nous avions deux autres fonctions:
nous pourrions également les passer à
f
et obtenir une valeur de7
ou15
, selon que nos arguments étaients t
out s
. Étant donné que ces fonctions renvoient toutes les deux une valeur plutôt qu'une fonction, aucune application partielle n'aurait lieu dansf s t
ouf t s
.Si nous avions écrit
f
avecq
etr
en tête, nous aurions pu utiliser une lambda (fonction anonyme) au lieu d'une application partielle, par exemple:mais cela aurait restreint la généralité de
f'
.f
peut être appelé avec des argumentsq
etr
ous
ett
, maisf'
ne peut être appelé qu'avecq
etr
-f' s t
et lesf' t s
deux entraînent une erreur.PLUS
Si
f'
étaient appelés avec une paireq'
/r'
où le aq'
pris plus de deux arguments, leq'
serait finalement appliqué partiellementf'
.Alternativement, vous pouvez envelopper l'
q
extérieurf
plutôt que l'intérieur, mais cela vous laisserait avec un lambda imbriqué méchant:qui est essentiellement ce que le curry
q
était en premier lieu!la source
def f = { a, b -> b a.curry(1) }
pour fairef q, r
travaillerdef f = { a, b -> b a(1) }
oudef f = { a, b -> b a.curry(1)() }
pourf s, t
travailler. Vous devez passer tous les paramètres ou dire explicitement que vous êtes au curry. :(f x y
signifie ce que de nombreuses langues écriraientf(x)(y)
, nonf(x, y)
. Peut-être que votre code fonctionnerait dans Groovy si vous écrivezq
pour qu'il s'attende à être appelé commeq(1)(2)
?(partial f a b ...)
- étant habitué à Haskell, je manque beaucoup de curry approprié lors de la programmation dans d'autres langages (bien que je travaille récemment en F # qui, heureusement, le supporte).Il y a deux points clés concernant une application partielle. Le premier est syntaxique / pratique - certaines définitions deviennent plus faciles et plus courtes à lire et à écrire, comme l'a mentionné @jk. (Consultez la programmation Pointfree pour en savoir plus sur la façon dont c'est génial!)
Le deuxième, comme l'a mentionné @telastyn, concerne un modèle de fonctions et n'est pas simplement pratique. Dans la version Haskell, dont je vais récupérer mes exemples car je ne connais pas les autres langages à application partielle, toutes les fonctions prennent un seul argument. Oui, même des fonctions comme:
prendre un seul argument; en raison de l'associativité du constructeur de type de fonction
->
, ce qui précède équivaut à:qui est une fonction qui prend un
a
et retourne une fonction[a] -> [a]
.Cela nous permet d'écrire des fonctions comme:
qui peut appliquer n'importe quelle fonction à un argument du type approprié. Même les plus fous comme:
D'accord, c'était donc un exemple artificiel. Mais une plus utile implique la classe de type Applicative , qui inclut cette méthode:
Comme vous pouvez le voir, le type est identique, comme
$
si vous enlevez leApplicative f
bit, et en fait, cette classe décrit l'application de fonction dans un contexte. Donc, au lieu d'une application de fonction normale:Nous pouvons appliquer des fonctions dans un contexte applicatif; par exemple, dans le contexte Peut-être dans lequel quelque chose peut être présent ou manquant:
Maintenant, la partie vraiment cool est que la classe de type Applicative ne mentionne rien sur les fonctions de plus d'un argument - néanmoins, elle peut les traiter, même les fonctions de 6 arguments comme
f
:Pour autant que je sache, la classe de type Applicative dans sa forme générale ne serait pas possible sans une conception de l'application partielle. (À tous les experts en programmation - corrigez-moi si je me trompe!) Bien sûr, si votre langue manque d'application partielle, vous pouvez la créer sous une forme ou une autre, mais ... ce n'est tout simplement pas la même chose, n'est-ce pas? ? :)
la source
Applicative
sans curry ou application partielle utiliseraitfzip :: (f a, f b) -> f (a, b)
. Dans un langage avec des fonctions d'ordre supérieur, cela vous permet de déplacer le curry et l'application partielle dans le contexte du foncteur et est équivalent à(<*>)
. Sans fonctions d'ordre supérieur, vous n'auriez pas,fmap
donc le tout serait inutile.f <$> x <*> y
style idiomatique fonctionne facilement parce que le curry et l'application partielle fonctionnent facilement. En d'autres termes, ce qui est agréable est plus important que ce qui est possible ici.