Ich habe in mehreren Artikeln und Blogs Verweise auf Curry-Funktionen gesehen, aber ich kann keine gute Erklärung finden (oder zumindest eine, die Sinn macht!).
add x y = x+y
(curried) unterscheidet sich von add (x, y)=x+y
(uncurried)
Ich habe in mehreren Artikeln und Blogs Verweise auf Curry-Funktionen gesehen, aber ich kann keine gute Erklärung finden (oder zumindest eine, die Sinn macht!).
add x y = x+y
(curried) unterscheidet sich von add (x, y)=x+y
(uncurried)
Antworten:
Currying ist, wenn Sie eine Funktion, die mehrere Argumente akzeptiert, in eine Reihe von Funktionen aufteilen, die jeweils nur ein Argument enthalten. 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, akzeptiert und deren Summe zurückgibt. Wir werden jetzt diese Funktion curry:
function add (a) {
return function (b) {
return a + b;
}
}
Dies ist eine Funktion, die ein Argument a verwendet und eine Funktion zurückgibt, die ein anderes Argument b verwendet, und diese Funktion gibt ihre Summe zurück.
add(3)(4);
var add3 = add(3);
add3(4);
Die erste Anweisung gibt 7 zurück, wie die add (3, 4) -Anweisung. Die zweite Anweisung definiert eine neue Funktion namens add3, die ihrem Argument 3 hinzufügt. Dies ist, was manche Leute eine Schließung nennen können. Die dritte Anweisung verwendet die Operation add3, um 3 zu 4 zu addieren, wodurch wiederum 7 erzeugt wird.
[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]
, um die gesamte Liste mit zu multiplizieren 5
und so die Liste zu generieren [5, 10, 15, 20, 25]
.
map
muss eine Funktion sein, die nur 1 Argument akzeptiert - ein Element aus der Liste. Multiplikation - als mathematisches Konzept - ist eine binäre Operation; Es dauert 2 Argumente. In Haskell *
gibt es jedoch eine Curry-Funktion, ähnlich der zweiten Version add
in dieser Antwort. Das Ergebnis von (* 5)
ist eine Funktion, die ein einzelnes Argument nimmt und mit 5 multipliziert und die es uns ermöglicht, es mit map zu verwenden.
In einer Algebra von Funktionen ist der Umgang mit Funktionen, die mehrere Argumente annehmen (oder einem Argument, das ein N-Tupel entspricht), etwas unelegant - aber wie Moses Schönfinkel (und unabhängig davon Haskell Curry) bewiesen hat, ist es nicht erforderlich: Sie alle Need sind Funktionen, die ein Argument annehmen.
Wie gehen Sie mit etwas um, als das Sie sich natürlich ausdrücken würden f(x,y)
? Nun, Sie nehmen das als Äquivalent zu f(x)(y)
- f(x)
, nennen es g
, ist eine Funktion, und Sie wenden diese Funktion auf an y
. Mit anderen Worten, Sie haben nur Funktionen, die ein Argument annehmen - aber einige dieser Funktionen geben andere Funktionen zurück (die AUCH ein Argument annehmen ;-).
Wie üblich hat Wikipedia einen schönen zusammenfassenden Eintrag dazu, mit vielen nützlichen Hinweisen (wahrscheinlich auch in Bezug auf Ihre Lieblingssprachen ;-) sowie einer etwas strengeren mathematischen Behandlung.
div :: Integral a => a -> a -> a
- diese mehreren Pfeile beachten? "Map a to function Mapping a to a" ist eine Lesung ;-). Sie könnten ein (einzelnes) Tupelargument für div
& c verwenden, aber das wäre in Haskell wirklich anti-idiomatisch.
Hier ist ein konkretes Beispiel:
Angenommen, Sie haben eine Funktion, die die auf ein Objekt wirkende Gravitationskraft berechnet. Wenn Sie die Formel nicht kennen, finden Sie sie hier . Diese Funktion verwendet die drei erforderlichen Parameter als Argumente.
Wenn Sie jetzt auf der Erde sind, möchten 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 bewerten. Was Sie zurückbekommen würden, ist eine weitere Funktion, die nur zwei Argumente verwendet und die Gravitationskraft von Objekten auf der Erde berechnet. Dies nennt man Currying.
Currying ist eine Transformation, die auf Funktionen angewendet werden kann, damit diese ein Argument weniger als zuvor verwenden können.
In F # können Sie beispielsweise 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: -
f 1 2 3
Rückgabe 6.
Aus unserer Definition können wir daher die Curry-Funktion für f definieren: -
let curry f = fun x -> f x
Wobei 'fun x -> fx' eine Lambda-Funktion ist, die x => f (x) in C # entspricht. Diese Funktion gibt die Funktion ein, die Sie curry möchten, und gibt eine Funktion zurück, die ein einzelnes Argument verwendet und die angegebene Funktion zurückgibt, wobei das erste Argument auf das Eingabeargument gesetzt ist.
Mit unserem vorherigen Beispiel können wir ein Curry von f erhalten, indem wir:
let curryf = curry f
Wir können dann Folgendes tun: -
let f1 = curryf 1
Was uns eine Funktion f1 liefert, die f1 yz = 1 + y + z entspricht. Dies bedeutet, dass wir Folgendes tun können:
f1 2 3
Welches gibt 6 zurück.
Dieser Prozess wird oft mit "Teilfunktionsanwendung" verwechselt, die folgendermaßen definiert werden kann:
let papply f x = f x
Obwohl wir es auf mehr als einen Parameter erweitern können, dh:
let papply2 f x y = f x y
let papply3 f x y z = f x y z
etc.
Eine Teilanwendung übernimmt die Funktion und die Parameter und gibt eine Funktion zurück, für die ein oder mehrere Parameter erforderlich sind. Wie die beiden vorherigen Beispiele zeigen, wird sie direkt in die Standarddefinition der F # -Funktion implementiert, sodass wir das vorherige Ergebnis folgendermaßen erzielen können:
let f1 = f 1
f1 2 3
Welches ergibt ein Ergebnis von 6.
Abschließend:-
Der Unterschied zwischen Currying und Teilfunktionsanwendung besteht darin, dass:
Currying nimmt eine Funktion und stellt eine neue Funktion bereit, die ein einzelnes Argument akzeptiert und die angegebene Funktion zurückgibt, wobei das erste Argument auf dieses Argument gesetzt ist. Dies ermöglicht es uns, Funktionen mit mehreren Parametern als eine Reihe von Funktionen mit einem Argument darzustellen . 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 von Teilfunktionen ist direkter - sie verwendet 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
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 es uns erlauben, es so zu nennen:
let addTen = add(10);
Wenn dies ausgeführt wird, 10
wird das als übergeben x
;
let add = function(10){
return function(y){
return 10 + y
};
};
was bedeutet, dass uns diese Funktion zurückgegeben wird:
function(y) { return 10 + y };
Also wenn du anrufst
addTen();
Sie rufen wirklich an:
function(y) { return 10 + y };
Wenn Sie dies tun:
addTen(4)
es ist das gleiche wie:
function(4) { return 10 + 4} // 14
addTen()
Wir addieren also immer zehn zu allem, was wir übergeben. Wir können ähnliche Funktionen auf die gleiche Weise ausführen:
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 um alles in der Welt würden Sie das jemals tun wollen? Es verwandelt eine eifrige Operation x + y
in eine Operation , die träge durchlaufen werden kann, was bedeutet, dass wir mindestens zwei Dinge tun können: 1. teure Operationen zwischenspeichern 2. Abstraktionen im funktionalen Paradigma erzielen.
Stellen Sie sich vor, unsere Curry-Funktion sieht folgendermaßen 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, um es an vielen Stellen zu verwenden, was bedeutet, dass wir die rechenintensiven Dinge nur einmal ausführen:
let finishTheJob = doTheHardStuff(10)
finishTheJob(20)
finishTheJob(30)
Auf ähnliche Weise können wir Abstraktionen erhalten.
Eine Curry-Funktion ist eine Funktion mehrerer Argumente, die so umgeschrieben wurden, dass sie das erste Argument akzeptiert und eine Funktion zurückgibt, die das zweite Argument akzeptiert, und so weiter. Dadurch können Funktionen mehrerer Argumente einige ihrer ursprünglichen Argumente teilweise anwenden.
map
eine Funktion f
über eine Liste von Listen xss
ausführen möchten, können Sie dies tun map (map f) xss
.
Hier ist ein Spielzeugbeispiel in Python:
>>> from functools import partial as curry
>>> # Original function taking three parameters:
>>> def display_quote(who, subject, quote):
print who, 'said regarding', subject + ':'
print '"' + quote + '"'
>>> display_quote("hoohoo", "functional languages",
"I like Erlang, not sure yet about Haskell.")
hoohoo said regarding functional languages:
"I like Erlang, not sure yet about Haskell."
>>> # Let's curry the function to get another that always quotes Alex...
>>> am_quote = curry(display_quote, "Alex Martelli")
>>> am_quote("currying", "As usual, wikipedia has a nice summary...")
Alex Martelli said regarding currying:
"As usual, wikipedia has a nice summary..."
(Verwenden Sie einfach die Verkettung über +, um Ablenkungen für Nicht-Python-Programmierer zu vermeiden.)
Bearbeitung zum Hinzufügen:
Siehe http://docs.python.org/library/functools.html?highlight=partial#functools.partial , in dem auch die Unterscheidung zwischen Teilobjekt und Funktion in der Art und Weise angezeigt wird, wie Python dies implementiert.
Beim Currying wird eine Funktion von aufrufbar f(a, b, c)
in aufrufbar wie übersetzt f(a)(b)(c)
.
Andernfalls wird Currying ausgeführt, wenn Sie eine Funktion, die mehrere Argumente enthält, in eine Reihe von Funktionen aufteilen, die Teil der Argumente sind.
Currying ist buchstäblich eine Transformation von Funktionen: von einer Art des Aufrufs in eine andere. In JavaScript erstellen wir normalerweise einen Wrapper, um die ursprüngliche Funktion beizubehalten.
Curry ruft keine Funktion auf. Es verwandelt es einfach.
Lassen Sie uns eine Curry-Funktion erstellen, die das Currying für Funktionen mit zwei Argumenten ausführt. Mit anderen Worten, curry(f)
für zwei Argumente f(a, b)
übersetzt es inf(a)(b)
function curry(f) { // curry(f) does the currying transform
return function(a) {
return function(b) {
return f(a, b);
};
};
}
// usage
function sum(a, b) {
return a + b;
}
let carriedSum = curry(sum);
alert( carriedSum(1)(2) ); // 3
Wie Sie sehen können, besteht die Implementierung aus einer Reihe von Wrappern.
curry(func)
ist ein Wrapper function(a)
.sum(1)
, wird das Argument in der Lexikalischen Umgebung gespeichert und ein neuer Wrapper zurückgegeben function(b)
.sum(1)(2)
schließlich die function(b)
Bereitstellung von 2 auf und leitet den Aufruf an die ursprüngliche Summe mit mehreren Argumenten weiter.Wenn Sie verstehen, sind partial
Sie auf halbem Weg. Die Idee von partial
ist, Argumente auf eine Funktion vorab anzuwenden und eine neue Funktion zurückzugeben, die nur die verbleibenden Argumente will. Wenn diese neue Funktion aufgerufen wird, enthält sie die vorinstallierten Argumente sowie alle Argumente, die ihr zur Verfügung gestellt wurden.
In Clojure +
ist eine Funktion, aber die Dinge klar zu machen:
(defn add [a b] (+ a b))
Möglicherweise wissen Sie, dass die inc
Funktion einfach 1 zu der übergebenen Zahl addiert.
(inc 7) # => 8
Lassen Sie es uns selbst bauen mit partial
:
(def inc (partial add 1))
Hier geben wir eine weitere Funktion zurück, die 1 in das erste Argument von geladen hat add
. Da add
zwei Argumente benötigt inc
werden, möchte die neue Funktion nur das b
Argument - nicht 2 Argumente wie zuvor, da 1 bereits teilweise angewendet wurde. Somit partial
ist ein Werkzeug zum Erstellen neuer Funktionen mit vorgegebenen Standardwerten. Deshalb ordnen Funktionen in einer funktionalen Sprache häufig Argumente von allgemein nach spezifisch. Dies erleichtert die Wiederverwendung solcher Funktionen, aus denen andere Funktionen erstellt werden können.
Stellen Sie sich nun vor, die Sprache wäre klug genug, um introspektiv zu verstehen, was add
zwei Argumente wollte. Wenn wir ein Argument übergeben haben, anstatt es zu verhindern, was ist, wenn die Funktion das Argument teilweise angewendet hat, haben wir es in unserem Namen übergeben und verstanden, dass wir wahrscheinlich beabsichtigten, das andere Argument später bereitzustellen? Wir könnten dann definieren, inc
ohne explizit zu verwenden partial
.
(def inc (add 1)) #partial is implied
So verhalten sich einige Sprachen. Es ist außerordentlich nützlich, wenn man Funktionen zu größeren Transformationen zusammensetzen möchte. Dies würde zu Wandlern führen.
Ich fand diesen Artikel und den Artikel, auf den er verweist, nützlich, um das Curry besser zu verstehen: http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx
Wie die anderen bereits erwähnt haben, ist dies nur eine Möglichkeit, eine Ein-Parameter-Funktion zu haben.
Dies ist insofern nützlich, als Sie nicht davon ausgehen müssen, wie viele Parameter übergeben werden, sodass Sie keine Funktionen mit 2 Parametern, 3 Parametern und 4 Parametern benötigen.
Wie alle anderen Antworten hilft das Curry, teilweise angewendete Funktionen zu erstellen. Javascript bietet keine native Unterstützung für das automatische Currying. Daher helfen die oben angegebenen Beispiele möglicherweise nicht bei der praktischen Codierung. Es gibt einige hervorragende Beispiele in Livescript (das im Wesentlichen zu js kompiliert wird) http://livescript.net/
times = (x, y) --> x * y
times 2, 3 #=> 6 (normal use works as expected)
double = times 2
double 5 #=> 10
Wenn Sie im obigen Beispiel weniger Argumente angegeben haben, generiert Livescript eine neue Curry-Funktion für Sie (doppelt).
Curry kann Ihren Code vereinfachen. Dies ist einer der Hauptgründe dafür. Beim Currying wird eine Funktion, die n Argumente akzeptiert, in n Funktionen konvertiert, die nur ein Argument akzeptieren.
Das Prinzip besteht darin, die Argumente der übergebenen Funktion unter Verwendung der Closure (Closure) -Eigenschaft zu übergeben, um sie in einer anderen Funktion zu speichern und als Rückgabewert zu behandeln. Diese Funktionen bilden eine Kette, und die endgültigen Argumente werden zur Vervollständigung übergeben die Operation.
Dies hat den Vorteil, dass die Verarbeitung von Parametern vereinfacht werden kann, indem jeweils nur ein Parameter behandelt wird, was auch die Flexibilität und Lesbarkeit des Programms verbessern kann. Dies macht das Programm auch übersichtlicher. Wenn Sie den Code auch in kleinere Teile aufteilen, wird er wiederverwendungsfreundlich.
Zum Beispiel:
function curryMinus(x)
{
return function(y)
{
return x - y;
}
}
var minus5 = curryMinus(1);
minus5(3);
minus5(5);
Ich kann auch ...
var minus7 = curryMinus(7);
minus7(3);
minus7(5);
Dies ist sehr gut geeignet, um komplexen Code sauber zu machen und nicht synchronisierte Methoden usw. zu handhaben.
Eine Curry-Funktion wird auf mehrere Argumentlisten anstatt nur auf eine angewendet.
Hier ist eine reguläre Funktion ohne Curry, die zwei Int-Parameter x und y hinzufügt:
scala> def plainOldSum(x: Int, y: Int) = x + y
plainOldSum: (x: Int,y: Int)Int
scala> plainOldSum(1, 2)
res4: Int = 3
Hier ist eine ähnliche Funktion, die Curry ist. Anstelle einer Liste mit zwei Int-Parametern wenden Sie diese Funktion auf zwei Listen mit jeweils einem Int-Parameter an:
scala> def curriedSum(x: Int)(y: Int) = x + y
curriedSum: (x: Int)(y: Int)Intscala> second(2)
res6: Int = 3
scala> curriedSum(1)(2)
res5: Int = 3
Was hier passiert, ist, dass Sie beim Aufrufen curriedSum
tatsächlich zwei herkömmliche Funktionsaufrufe hintereinander erhalten. Der erste Funktionsaufruf verwendet einen einzelnen Int-Parameter mit dem Namen x
und gibt einen Funktionswert für die zweite Funktion zurück. Diese zweite Funktion übernimmt den Int-Parameter
y
.
Hier ist eine Funktion mit dem Namen first
, die im Geiste das tut, was der erste Aufruf einer traditionellen Funktion bewirken curriedSum
würde:
scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int
Wenn Sie 1 auf die erste Funktion anwenden, dh die erste Funktion aufrufen und 1 übergeben, erhalten Sie die zweite Funktion:
scala> val second = first(1)
second: (Int) => Int = <function1>
Das Anwenden von 2 auf die zweite Funktion ergibt das Ergebnis:
scala> second(2)
res6: Int = 3
Ein Beispiel für Currying wäre, wenn Sie Funktionen haben, von denen Sie momentan nur einen der Parameter kennen:
Zum Beispiel:
func aFunction(str: String) {
let callback = callback(str) // signature now is `NSData -> ()`
performAsyncRequest(callback)
}
func callback(str: String, data: NSData) {
// Callback code
}
func performAsyncRequest(callback: NSData -> ()) {
// Async code that will call callback with NSData as parameter
}
Da Sie den zweiten Parameter für den Rückruf beim Senden nicht kennen, performAsyncRequest(_:)
müssten Sie hier ein weiteres Lambda / Closure erstellen, um dieses an die Funktion zu senden.
func callback
Rückkehr selbst? Es heißt @ callback(str)
so let callback = callback(str)
, Rückruf ist nur der Rückgabewert vonfunc callback
func callback(_:data:)
akzeptiert zwei Parameter, hier gebe ich nur einen, den String
, also wartet er auf den nächsten ( NSData
), deshalb let callback
wartet jetzt eine andere Funktion darauf, dass Daten übergeben werden
Hier ist das Beispiel einer generischen und der kürzesten Version für das Funktionscurrying mit n-Nr. von params.
const add = a => b => b ? add(a + b) : a;
const add = a => b => b ? add(a + b) : a;
console.log(add(1)(2)(3)(4)());
Hier finden Sie eine einfache Erklärung der Curry-Implementierung in C #. In den Kommentaren habe ich versucht zu zeigen, wie nützlich Curry sein kann:
public static class FuncExtensions {
public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
{
return x1 => x2 => func(x1, x2);
}
}
//Usage
var add = new Func<int, int, int>((x, y) => x + y).Curry();
var func = add(1);
//Obtaining the next parameter here, calling later the func with next parameter.
//Or you can prepare some base calculations at the previous step and then
//use the result of those calculations when calling the func multiple times
//with different input parameters.
int result = func(1);
Currying ist eine der Funktionen höherer Ordnung von Java Script.
Currying ist eine Funktion vieler Argumente, die so umgeschrieben wird, dass sie das erste Argument verwendet und eine Funktion zurückgibt, die wiederum die verbleibenden Argumente verwendet und den Wert zurückgibt.
Verwirrt?
Sehen wir uns ein Beispiel an:
function add(a,b)
{
return a+b;
}
add(5,6);
Dies ähnelt der folgenden Curry-Funktion:
function add(a)
{
return function(b){
return a+b;
}
}
var curryAdd = add(5);
curryAdd(6);
Was bedeutet dieser Code?
Lesen Sie nun die Definition noch einmal,
Currying ist eine Funktion vieler Argumente, die so umgeschrieben wird, dass sie das erste Argument verwendet und eine Funktion zurückgibt, die wiederum die verbleibenden Argumente verwendet und den Wert zurückgibt.
Immer noch verwirrt? Lassen Sie mich tief erklären!
Wenn Sie diese Funktion aufrufen,
var curryAdd = add(5);
Es wird Ihnen eine Funktion wie diese zurückgeben,
curryAdd=function(y){return 5+y;}
Dies nennt man also Funktionen höherer Ordnung. Das heißt, wenn Sie eine Funktion nacheinander aufrufen, wird eine andere Funktion zurückgegeben. Dies ist eine genaue Definition für eine Funktion höherer Ordnung. Dies ist der größte Vorteil für die Legende Java Script. Also komm zurück zum Currying,
Diese Zeile übergibt das zweite Argument an die Funktion curryAdd.
curryAdd(6);
was wiederum ergibt,
curryAdd=function(6){return 5+6;}
// Which results in 11
Ich hoffe, Sie verstehen die Verwendung von Curry hier. Also, zu den Vorteilen kommen,
Warum Curry?
Es nutzt die Wiederverwendbarkeit von Code. Weniger Code, weniger Fehler. Sie fragen sich vielleicht, wie es weniger Code ist?
Ich kann es mit ECMA Script 6 neuen Funktionspfeilfunktionen beweisen.
Ja! ECMA 6 bietet uns die wunderbare Funktion Pfeilfunktionen.
function add(a)
{
return function(b){
return a+b;
}
}
Mit Hilfe der Pfeilfunktion können wir die obige Funktion wie folgt schreiben:
x=>y=>x+y
Cool, oder?
Also, weniger Code und weniger Fehler !!
Mit Hilfe dieser Funktion höherer Ordnung kann man leicht einen fehlerfreien Code entwickeln.
Ich fordere dich heraus!
Hoffe, du hast verstanden, was Curry ist. Bitte zögern Sie nicht, hier zu kommentieren, wenn Sie weitere Erläuterungen benötigen.
Danke. Schönen Tag noch!
Es gibt ein Beispiel für "Currying in ReasonML".
let run = () => {
Js.log("Curryed function: ");
let sum = (x, y) => x + y;
Printf.printf("sum(2, 3) : %d\n", sum(2, 3));
let per2 = sum(2);
Printf.printf("per2(3) : %d\n", per2(3));
};
curry
unduncurry
Funktionen von Haskell. Wichtig ist hierbei, dass diese Isomorphismen im Voraus festgelegt und daher in die Sprache "eingebaut" werden.