Ich habe jeden Tag Artikel über funktionale Programmierung gelesen und versucht, einige Praktiken so weit wie möglich anzuwenden. Aber ich verstehe nicht, was beim Curry oder bei der teilweisen Anwendung einzigartig ist.
Nehmen Sie diesen Groovy-Code als Beispiel:
def mul = { a, b -> a * b }
def tripler1 = mul.curry(3)
def tripler2 = { mul(3, it) }
Ich verstehe nicht, was der Unterschied zwischen tripler1
und ist tripler2
. Sind sie nicht beide gleich? Das 'Currying' wird in reinen oder teilweise funktionalen Sprachen wie Groovy, Scala, Haskell usw. unterstützt. Aber ich kann das Gleiche tun (Links-Curry, Rechts-Curry, N-Curry oder Teilanwendung), indem ich einfach eine andere benannte oder anonyme Sprache erstelle Funktion oder Schließung, die die Parameter in den tripler2
meisten Sprachen (sogar C) an die ursprüngliche Funktion (wie ) weiterleitet .)
Vermisse ich hier etwas? Es gibt Stellen, an denen ich Curry und Teilanwendung in meiner Grails-Anwendung verwenden kann, aber ich zögere, dies zu tun, weil ich mich frage: "Wie ist das anders?"
Bitte erleuchte mich.
EDIT: Wollen Sie damit sagen, dass eine teilweise Anwendung / Currying einfach effizienter ist als das Erstellen / Aufrufen einer anderen Funktion, die Standardparameter an die ursprüngliche Funktion weiterleitet?
quelle
f x y = x + y
bedeutet dies beispielsweise, dassf
eine Funktion einen int-Parameter akzeptiert. Das Ergebnis vonf x
(f
angewendet aufx
) ist eine Funktion, die einen int-Parameter akzeptiert. Das Ergebnisf x y
(oder(f x) y
, dhf x
angewendet aufy
) ist ein Ausdruck, der keine Eingabeparameter akzeptiert und durch Reduzieren ausgewertet wirdx + y
.Antworten:
Beim Currying geht es darum, eine Funktion zu drehen / darzustellen, die n Eingaben in n Funktionen umwandelt, die jeweils 1 Eingabe annehmen. Bei einer Teilanwendung geht es darum, einige der Eingaben für eine Funktion zu korrigieren.
Die Motivation für eine teilweise Anwendung besteht hauptsächlich darin, dass es einfacher ist, Funktionsbibliotheken höherer Ordnung zu schreiben. Zum Beispiel nehmen die Algorithmen in C ++ STL alle weitgehend Prädikate oder unäre Funktionen an. Mit bind1st kann der Bibliotheksbenutzer nicht unäre Funktionen mit einem gebundenen Wert einbinden . Der Bibliotheksschreiber muss daher nicht überladene Funktionen für alle Algorithmen bereitstellen, die unäre Funktionen zur Bereitstellung von Binärversionen verwenden
Currying selbst ist nützlich, da es Ihnen eine teilweise Anwendung bietet, wo immer Sie es kostenlos möchten, dh Sie benötigen keine Funktion mehr
bind1st
, die teilweise angewendet werden soll.quelle
currying
etwas spezifisch für groovig oder sprachübergreifend anwendbar?Und der Optimierer wird sich das ansehen und umgehend zu etwas übergehen, das er verstehen kann. Currying ist ein netter kleiner Trick für den Endbenutzer, hat aber vom Standpunkt des Sprachdesigns aus viel bessere Vorteile. Es ist wirklich schön, alle Methoden als unär zu behandeln,
A -> B
woB
es eine andere Methode geben kann.Es vereinfacht, welche Methoden Sie schreiben müssen, um Funktionen höherer Ordnung zu verarbeiten. Ihre statische Analyse und Optimierung in der Sprache hat nur einen Pfad, mit dem Sie arbeiten können, der sich auf bekannte Weise verhält. Die Parameterbindung fällt nur aus dem Entwurf heraus, anstatt dass Reifen dieses allgemeine Verhalten ausführen müssen.
quelle
Als @jk. Curry kann dazu beitragen, den Code allgemeiner zu gestalten.
Angenommen, Sie hatten diese drei Funktionen (in Haskell):
Die Funktion
f
nimmt hier zwei Funktionen als Argumente an, übergibt sie1
an die erste Funktion und übergibt das Ergebnis des ersten Aufrufs an die zweite Funktion.Wenn wir
f
usingq
undr
als Argumente aufrufen würden, wäre dies effektiv:wo
q
würde auf eine1
andere Funktion angewendet werden und diese zurückgeben (wieq
es Curry ist); Diese zurückgegebene Funktion würde dannr
als Argument übergeben, um ein Argument von zu erhalten3
. Das Ergebnis wäre ein Wert von9
.Nehmen wir an, wir hatten zwei weitere Funktionen:
wir könnten diese
f
auch weitergeben und einen Wert von7
oder erhalten15
, abhängig davon, ob unsere Argumentes t
oder warent s
. Da diese Funktionen beide eher einen Wert als eine Funktion zurückgeben, würde inf s t
oder keine Teilanwendung stattfindenf t s
.Wenn wir
f
mitq
undr
im Hinterkopf geschrieben hätten, hätten wir möglicherweise ein Lambda (anonyme Funktion) anstelle einer Teilanwendung verwendet, z.aber dies hätte die Allgemeinheit von eingeschränkt
f'
.f
kann mit Argumentenq
undr
oders
und aufgerufen werdent
,f'
kann aber nur mitq
undr
- aufgerufen werdenf' s t
undf' t s
beide führen zu einem Fehler.MEHR
Wenn
f'
mit einemq'
/r'
pair aufgerufen würde, bei demq'
mehr als zwei Argumente verwendet wurden,q'
würde das immer noch teilweise angewendetf'
.Alternativ könnten Sie
q
außenf
statt innen einwickeln , aber das würde Sie mit einem bösen verschachtelten Lambda zurücklassen:Das ist im Wesentlichen das, was der Curry überhaupt
q
war!quelle
def f = { a, b -> b a.curry(1) }
, umf q, r
arbeiten zu können unddef f = { a, b -> b a(1) }
oderdef f = { a, b -> b a.curry(1)() }
umf s, t
zu arbeiten. Sie müssen alle Parameter übergeben oder explizit sagen, dass Sie Curry spielen. :(f x y
dh was viele Sprachen schreiben würdenf(x)(y)
, nichtf(x, y)
. Vielleicht würde Ihr Code in Groovy funktionieren, wenn Sieq
so schreiben , dass er voraussichtlich so aufgerufen wirdq(1)(2)
?(partial f a b ...)
- da ich an Haskell gewöhnt bin, vermisse ich das richtige Curry sehr, wenn ich in anderen Sprachen programmiere (obwohl ich kürzlich in F # gearbeitet habe, was es zum Glück unterstützt).Es gibt zwei wichtige Punkte bei der Teilanwendung. Die erste ist syntaktisch / bequem - einige Definitionen werden einfacher und kürzer zu lesen und zu schreiben, wie @jk erwähnt. (Schauen Sie sich die Pointfree-Programmierung an, um mehr darüber zu erfahren, wie großartig das ist!)
Die zweite, wie @telastyn erwähnt, handelt von einem Funktionsmodell und ist nicht nur praktisch. In der Haskell-Version, aus der ich meine Beispiele erhalte, weil ich mit anderen Sprachen mit teilweiser Anwendung nicht vertraut bin, verwenden alle Funktionen ein einziges Argument. Ja, funktioniert sogar wie:
nimm ein einziges Argument; Aufgrund der Assoziativität des Funktionstypkonstruktors
->
entspricht das Obige:Dies ist eine Funktion, die eine Funktion übernimmt
a
und zurückgibt[a] -> [a]
.Dies ermöglicht es uns, Funktionen zu schreiben wie:
Dies kann jede Funktion auf ein Argument des entsprechenden Typs anwenden . Sogar verrückte wie:
Okay, das war ein erfundenes Beispiel. Eine nützlichere ist jedoch die Typklasse Applicative , die diese Methode enthält:
Wie Sie sehen können, ist der Typ identisch, ähnlich wie
$
wenn Sie dasApplicative f
Bit entfernen , und tatsächlich beschreibt diese Klasse die Funktionsanwendung in einem Kontext. Also anstelle der normalen Funktionsanwendung:Wir können Funktionen in einem anwendbaren Kontext anwenden. Zum Beispiel im Kontext Vielleicht, in dem etwas vorhanden sein oder fehlen kann:
Der wirklich coole Teil ist nun, dass die Applicative-Typklasse nichts über Funktionen von mehr als einem Argument erwähnt - dennoch kann sie damit umgehen, sogar Funktionen von 6 Argumenten wie
f
:Soweit ich weiß, wäre die anwendbare Typklasse in ihrer allgemeinen Form ohne eine Vorstellung von einer teilweisen Anwendung nicht möglich. (Um jegliche Programmier Experten da draußen - bitte korrigieren Sie mich , wenn ich falsch!) Natürlich, wenn Sie die Sprache teilweise Anwendung fehlt, man könnte es in in irgendeiner Form bauen, aber ... es ist einfach nicht das gleiche, es ist ? :) :)
quelle
Applicative
ohne Curry oder teilweise Anwendung würde verwendenfzip :: (f a, f b) -> f (a, b)
. In einer Sprache mit Funktionen höherer Ordnung können Sie Currying und teilweise Anwendung in den Kontext des Funktors heben und sind gleichbedeutend mit(<*>)
. Ohne Funktionen höherer Ordnung haben Sie keine,fmap
so dass das Ganze nutzlos wäre.f <$> x <*> y
idiomatische Stil leicht funktioniert, da Currying und Teilanwendung leicht funktionieren. Mit anderen Worten, was angenehm ist, ist wichtiger als das, was hier möglich ist.