Ich habe in mehreren Artikeln und Blogs Hinweise auf Curried-Funktionen gesehen, aber ich kann keine gute Erklärung finden (oder zumindest eine, die Sinn macht!)
Wie kann ich dieses Konzept in der Praxis nutzen?
Ich habe in mehreren Artikeln und Blogs Hinweise auf Curried-Funktionen gesehen, aber ich kann keine gute Erklärung finden (oder zumindest eine, die Sinn macht!)
Currying bedeutet, dass man eine Funktion, die mehrere Argumente benötigt, in eine Reihe von Funktionen aufteilt, die jeweils nur ein Argument benötigen. Hier ist ein Beispiel in JavaScript:
function add (a, b) {
return a + b;
}
add(3, 4); // returns 7
Dies ist eine Funktion, die zwei Argumente, a und b, annimmt und deren Summe zurückgibt. Wir werden diese Funktion nun kurieren:
function add (a) {
return function (b) {
return a + b;
}
}
Dies ist eine Funktion, die ein Argument benötigt, a
und gibt eine Funktion zurück, die ein weiteres Argument benötigt, b
und diese Funktion gibt deren Summe zurück.
add(3)(4);
var add3 = add(3);
add3(4);
Die erste Anweisung gibt 7 zurück, wie die Anweisung add(3, 4)
Erklärung. Die zweite Anweisung definiert eine neue Funktion namens add3
die zu ihrem Argument 3 hinzufügt. (Das ist es, was manche eine Schließung nennen.) Die dritte Anweisung verwendet die add3
Operation, um 3 zu 4 zu addieren, was wiederum 7 als Ergebnis ergibt.
@Strawberry, nehmen wir an, Sie haben eine Liste von Zahlen in einer [1, 2, 3, 4, 5]
die Sie mit einer beliebigen Zahl multiplizieren möchten. In Haskell kann ich schreiben map (* 5) [1, 2, 3, 4, 5]
die gesamte Liste zu multiplizieren mit 5
und erzeugt so die Liste [5, 10, 15, 20, 25]
.
Ich verstehe, was die Kartenfunktion tut, aber ich bin mir nicht sicher, ob ich den Punkt verstehe, den Sie mir zu verdeutlichen versuchen. Wollen Sie damit sagen, dass die map-Funktion das Konzept des Currys darstellt?
In einer Algebra der Funktionen ist der Umgang mit Funktionen, die mehrere Argumente annehmen (oder ein äquivalentes Argument, das ein N-Tupel ist), etwas unelegant -- aber, wie Moses Schönfinkel (und unabhängig davon Haskell Curry) bewiesen hat, ist das nicht nötig: alles was man braucht, sind Funktionen, die ein Argument annehmen.
Wie gehen Sie also mit etwas um, das Sie ganz natürlich ausdrücken würden, wie zum Beispiel, f(x,y)
? Nun, Sie nehmen das als gleichwertig mit f(x)(y)
-- f(x)
nennen Sie es g
ist eine Funktion, und Sie wenden diese Funktion auf y
. Mit anderen Worten, Sie haben nur Funktionen, die ein Argument benötigen - aber einige dieser Funktionen geben andere Funktionen zurück (die ebenfalls ein Argument benötigen;-).
Wie immer, wikipedia hat einen schönen zusammenfassenden Eintrag dazu, mit vielen nützlichen Hinweisen (wahrscheinlich auch zu Ihren Lieblingssprachen;-) sowie einer etwas strengeren mathematischen Behandlung.
Ich vermute, dass ich ähnliche Bemerkungen wie oben gemacht habe - ich habe nicht gesehen, dass funktionale Sprachen Funktionen auf ein einziges Argument beschränken. Irre ich mich?
@hoohoo: Funktionale Sprachen beschränken Funktionen nicht generell auf ein einziges Argument. Auf einer niedrigeren, mathematischeren Ebene ist es jedoch viel einfacher, mit Funktionen umzugehen, die nur ein Argument annehmen. (Im Lambda-Kalkül zum Beispiel nehmen Funktionen jeweils nur ein Argument an.)
GUT. Dann eine andere Frage. Ist die folgende Aussage wahr? Lambda-Kalkül kann als Modell für funktionale Programmierung verwendet werden, aber funktionale Programmierung ist nicht unbedingt angewandtes Lambda-Kalkül.
Hier ein konkretes Beispiel:
Angenommen, Sie haben eine Funktion, die die auf ein Objekt wirkende Gravitationskraft berechnet. Wenn Sie die Formel nicht kennen, können Sie sie finden aquí . Diese Funktion nimmt die drei erforderlichen Parameter als Argumente auf.
Da Sie sich auf der Erde befinden, wollen Sie nur Kräfte für Objekte auf diesem Planeten berechnen. In einer funktionalen Sprache könnten Sie die Masse der Erde an die Funktion übergeben und sie dann teilweise auswerten. Was Sie zurückbekommen würden, ist eine andere Funktion, die nur zwei Argumente benötigt und die Gravitationskraft von Objekten auf der Erde berechnet. Dies wird als Currying bezeichnet.
Als Kuriosität bietet die Prototype-Bibliothek für JavaScript eine "curry"-Funktion, die ziemlich genau das tut, was Sie hier erklärt haben: prototypejs.org/api/funktion/curry
Neue PrototypeJS-Curry-Funktion Link. prototypejs.org/doc/latest/language/Function/prototype/curry/
Das klingt für mich nach einer Teilanwendung. Meinem Verständnis nach kann man, wenn man Currying anwendet, Funktionen mit einem einzigen Argument erstellen und sie zu komplizierteren Funktionen zusammensetzen. Übersehe ich etwas?
Es kann eine Möglichkeit sein, Funktionen zu verwenden, um andere Funktionen zu erstellen.
In Javascript:
let add = function(x){
return function(y){
return x + y
};
};
würde uns erlauben, es so zu nennen:
let addTen = add(10);
Wenn dies geschieht, wird die 10
wird übergeben als x
;
let add = function(10){
return function(y){
return 10 + y
};
};
was bedeutet, dass wir diese Funktion zurückbekommen:
function(y) { return 10 + y };
Wenn Sie also anrufen
addTen();
Sie rufen wirklich an:
function(y) { return 10 + y };
Wenn Sie dies also tun:
addTen(4)
ist es dasselbe wie:
function(4) { return 10 + 4} // 14
Also unser addTen()
addiert immer zehn zu dem, was wir eingeben. Wir können ähnliche Funktionen auf dieselbe Weise erstellen:
let addTwo = add(2) // addTwo(); will add two to whatever you pass in
let addSeventy = add(70) // ... and so on...
Die offensichtliche Folgefrage ist nun, warum in aller Welt Sie das überhaupt tun wollen? Es verwandelt das, was eine eifrige Operation war x + y
in ein System verwandeln, durch das man einfach hindurchgehen kann, was bedeutet, dass wir mindestens zwei Dinge tun können 1. teure Operationen zwischenspeichern 2. Abstraktionen im funktionalen Paradigma erreichen.
Stellen Sie sich vor, unsere Curry-Funktion sähe so aus:
let doTheHardStuff = function(x) {
let z = doSomethingComputationallyExpensive(x)
return function (y){
z + y
}
}
Wir könnten diese Funktion einmal aufrufen und dann das Ergebnis weitergeben, damit es an vielen Stellen verwendet werden kann, was bedeutet, dass wir die rechenintensiven Aufgaben nur einmal erledigen:
let finishTheJob = doTheHardStuff(10)
finishTheJob(20)
finishTheJob(30)
Wir können Abstraktionen auf ähnliche Weise erhalten.
Die beste Schritt-für-Schritt-Erklärung eines inhärent sequentiellen Prozesses, die ich hier gesehen habe, und vielleicht die beste, erklärendste Antwort von allen.
@jonsilver Ich würde das Gegenteil sagen, keine gute Erklärung. Ich stimme zu, dass es gut ist, das Beispiel zu erklären, aber die Leute neigen dazu, zu denken: "Ja, das ist klar, aber ich hätte das Gleiche auch anders machen können, also wozu soll das Currying gut sein?" Mit anderen Worten, ich wünschte, es gäbe gerade genug Kontext oder Erklärung, um nicht nur zu erklären, wie Currying funktioniert, sondern auch, warum es im Vergleich zu anderen Möglichkeiten, zehn zu addieren, keine nutzlose und triviale Beobachtung ist.
Currying ist eine Transformation, die auf Funktionen angewandt werden kann, damit sie ein Argument weniger als bisher annehmen können.
In F# können Sie zum Beispiel eine Funktion folgendermaßen definieren:-
let f x y z = x + y + z
Hier nimmt die Funktion f die Parameter x, y und z und summiert sie so auf:-
f 1 2 3
Rückgabe 6.
Aus unserer Definition können wir also die Curry-Funktion für f definieren:-
let curry f = fun x -> f x
Dabei ist 'fun x -> f x' eine Lambda-Funktion, die in C# gleichbedeutend mit x => f(x) ist. Diese Funktion gibt die gewünschte Funktion ein und gibt eine Funktion zurück, die nimmt ein einziges Argument und gibt die angegebene Funktion zurück, wobei das erste Argument auf das Eingabeargument gesetzt ist.
Anhand unseres vorherigen Beispiels können wir einen Curry von f wie folgt erhalten:-
let curryf = curry f
Wir können dann Folgendes tun:-
let f1 = curryf 1
Damit erhalten wir eine Funktion f1, die gleich ist mit f1 y z = 1 + y + z. Das bedeutet, dass wir Folgendes tun können:-
f1 2 3
Das ergibt 6.
Dieser Prozess wird oft mit der "Anwendung von Teilfunktionen" verwechselt, die folgendermaßen definiert werden kann:-
let papply f x = f x
Wir können es jedoch auf mehr als einen Parameter ausdehnen, d.h.:-
let papply2 f x y = f x y
let papply3 f x y z = f x y z
etc.
Eine partielle Anwendung nimmt die Funktion und den/die Parameter und gibt eine Funktion zurück, die einen oder mehrere Parameter weniger benötigt. Wie die beiden vorangegangenen Beispiele zeigen, ist diese Funktion direkt in der Standard-F#-Funktionsdefinition implementiert, so dass wir das vorherige Ergebnis folgendermaßen erreichen können:-
let f1 = f 1
f1 2 3
Das Ergebnis ist 6.
Zusammenfassend:-
Der Unterschied zwischen Currying und partieller Funktionsanwendung besteht darin, dass:-
Currying nimmt eine Funktion und stellt eine neue Funktion bereit, die ein einziges Argument akzeptiert und die angegebene Funktion zurückgibt, wobei ihr erstes Argument auf dieses Argument gesetzt wird. So können wir Funktionen mit mehreren Parametern als eine Reihe von Funktionen mit einem Argument darstellen . Beispiel:-
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
Die Anwendung einer Teilfunktion ist direkter - sie nimmt eine Funktion und ein oder mehrere Argumente und gibt eine Funktion zurück, bei der die ersten n Argumente auf die angegebenen n Argumente gesetzt sind. Beispiel:-
let f x y z = x + y + z
let f1 = f 1
let f2 = f 2
f1 2 3
6
f2 1 3
6
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
13 Stimmen
[Gemäß der Definition einer kartesischen geschlossenen Kategorie gibt es eine fest Familie von Adjunktionen (natürlich parametrisiert durch A) zwischen X -> X x A und X -> X ^ A. Die Isomorphismen hom(X x A, Y) <-> hom(X, Y^A) sind die
curry
yuncurry
Funktionen von Haskell. Wichtig ist hier, dass diese Isomorphismen von vornherein festgelegt und somit in die Sprache "eingebaut" sind.3 Stimmen
Es gibt ein schönes Tutorial hier für currying in haskell learnyouahaskell.com/higher-order-functions#curried-functions Kurzkommentare sind, dass
add x y = x+y
(curried) ist anders alsadd (x, y)=x+y
(ohne Eile)