Was ist Currying?

652

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!).

Ben
quelle
12
[Als Kommentar hinterlassen, da es für Nicht-Mathematiker nutzlos ist.] Gemäß der Definition einer geschlossenen kartesischen Kategorie gibt es eine feste Familie von Zusätzen (natürlich durch A parametrisiert) zwischen X -> X x A und X. -> X ^ A. Die Isomorphismen hom (X x A, Y) <-> hom (X, Y ^ A) sind die curryund uncurryFunktionen von Haskell. Wichtig ist hierbei, dass diese Isomorphismen im Voraus festgelegt und daher in die Sprache "eingebaut" werden.
Alexandre C.
3
Es gibt ein schönes Tutorial hier in Haskell currying learnyouahaskell.com/higher-order-functions#curried-functions kurzen Kommentaren ist , dass add x y = x+y(curried) unterscheidet sich von add (x, y)=x+y(uncurried)
Jaider

Antworten:

870

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.

Kyle Cronin
quelle
235
Wie kann ich dieses Konzept praktisch nutzen?
Erdbeere
43
@Strawberry, sagen Sie zum Beispiel, dass Sie eine Liste von Zahlen in einer haben [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 5und so die Liste zu generieren [5, 10, 15, 20, 25].
Nyson
62
Ich verstehe, was die Kartenfunktion bewirkt, bin mir aber nicht sicher, ob ich den Punkt verstehe, den Sie für mich veranschaulichen möchten. Wollen Sie damit sagen, dass die Kartenfunktion das Konzept des Currying darstellt?
Erdbeere
78
@Strawberry Das erste Argument mapmuss 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 addin 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.
Doval
26
@Strawberry Das Schöne an funktionalen Sprachen wie Standard ML oder Haskell ist, dass Sie Curry "kostenlos" bekommen können. Sie können eine Funktion mit mehreren Argumenten wie in jeder anderen Sprache definieren und erhalten automatisch eine Curry-Version davon, ohne selbst ein paar Lambdas einwerfen zu müssen. So können Sie neue Funktionen erstellen, die ohne großen Aufwand weniger Argumente von einer vorhandenen Funktion übernehmen, und dies macht es einfach, sie an andere Funktionen zu übergeben.
Doval
125

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.

Alex Martelli
quelle
1
Ich nehme einen ähnlichen Kommentar wie oben an - ich habe nicht gesehen, dass funktionale Sprachen Funktionen darauf beschränken, ein einziges Argument zu verwenden. Irre ich mich
Eric M
1
@hoohoo: Funktionale Sprachen beschränken Funktionen im Allgemeinen nicht auf ein einziges Argument. Auf einer niedrigeren, mathematischeren Ebene ist es jedoch viel einfacher, mit Funktionen umzugehen, die nur ein Argument enthalten. (In der Lambda-Rechnung nehmen beispielsweise Funktionen jeweils nur ein Argument auf.)
Sam DeFabbia-Kane
1
OK. Noch eine Frage dann. Ist das Folgende eine wahre Aussage? Der Lambda-Kalkül kann als Modell für die funktionale Programmierung verwendet werden, aber die funktionale Programmierung wird nicht unbedingt als Lambda-Kalkül angewendet.
Eric M
7
Wie Wikipedia-Seiten bemerken, "verschönern" oder "erweitern" die meisten FP-Sprachen den Lambda-Kalkül (z. B. mit einigen Konstanten und Datentypen), anstatt ihn nur "anzuwenden", aber es ist nicht so nah. Übrigens, was erweckt den Eindruck, dass z. B. Haskell die Funktionen NICHT auf ein einziges Argument beschränkt? Es tut es sicher, obwohl das dank Curry irrelevant ist; zB 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.
Alex Martelli
@Alex - Für Haskell & Arg Count habe ich nicht viel Zeit mit Haskell verbracht, und das war alles vor ein paar Wochen. Es war also ein einfacher Fehler.
Eric M
99

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.

Shea Daniels
quelle
2
Aus Neugier bietet die Prototype-Bibliothek für JavaScript eine "Curry" -Funktion, die ziemlich genau das tut, was Sie hier erklärt haben: prototypejs.org/api/function/curry
shuckster
Neuer PrototypeJS-Curry-Funktionslink. prototypejs.org/doc/latest/language/Function/prototype/curry/…
Richard Ayotte
7
Das klingt für mich nach einer teilweisen Anwendung. Nach meinem Verständnis können Sie beim Anwenden von Currying Funktionen mit einem einzigen Argument erstellen und zu komplizierteren Funktionen zusammensetzen. Vermisse ich etwas
Neontapir
9
@neontapir ist korrekt. Was Shea beschrieben hat, ist kein Curry. Es ist Teilanwendung. Wenn eine Funktion mit drei Argumenten verwendet wird und Sie sie als f (1) bezeichnen, erhalten Sie keine Funktion mit zwei Argumenten zurück. Sie erhalten eine Ein-Argument-Funktion zurück, die eine andere Ein-Argument-Funktion zurückgibt. Einer Curry-Funktion kann immer nur ein Argument übergeben werden. Die Curry-Funktion in PrototypeJS ist auch kein Curry. Es ist eine teilweise Anwendung.
MindJuice
nein (zur teilweisen Auswertung) und nein (zum Curry). Dies ist als Teilanwendung bekannt. Currying ist erforderlich, um es zu aktivieren.
Will Ness
47

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
ljs
quelle
Methoden in C # müssten also angewendet werden, bevor sie teilweise angewendet werden könnten?
CDMckay
"Dies ermöglicht es uns, Funktionen mit mehreren Parametern als eine Reihe von Funktionen mit einem Argument darzustellen" - perfekt, das hat alles für mich gut geklärt. Danke
Fuzzy-Analyse
44

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, 10wird 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 + yin 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.

Adzz
quelle
5
Die beste schrittweise Erklärung eines inhärent sequentiellen Prozesses, den ich hier gesehen habe, und vielleicht die beste, erklärendste Antwort des Loses.
4
@jonsilver Ich würde das Gegenteil sagen, keine gute Erklärung. Ich bin damit einverstanden, dass es gut ist, das vorgestellte Beispiel zu erklären, aber die Leute neigen dazu, standardmäßig zu denken: "Ja, vollkommen klar, aber ich hätte das Gleiche auf eine andere Weise tun können. Was nützt Curry?" Mit anderen Worten, ich wünschte, es hätte gerade genug Kontext oder Erklärung, um nicht nur zu beleuchten, wie Currying funktioniert, sondern auch, warum es im Vergleich zu anderen Möglichkeiten, zehn hinzuzufügen, keine nutzlose und triviale Beobachtung ist.
Whitneyland
29

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.

Jon Harrop
quelle
5
"Dadurch können Funktionen mehrerer Argumente einige ihrer ursprünglichen Argumente teilweise anwenden." - Warum ist das vorteilhaft?
Acarlon
5
@acarlon Funktionen werden häufig wiederholt mit einem oder mehreren gleichen Argumenten aufgerufen. Wenn Sie beispielsweise mapeine Funktion füber eine Liste von Listen xssausführen möchten, können Sie dies tun map (map f) xss.
Jon Harrop
1
Danke, das macht Sinn. Ich habe ein bisschen mehr gelesen und es ist in Ordnung gekommen.
Acarlon
4
Ich denke, diese Antwort macht es auf eine schöne, präzise Art und Weise richtig. Das "Currying" ist der Prozess, bei dem die Funktion mehrerer Argumente in eine Reihe von Funktionen umgewandelt wird, die jeweils ein einzelnes Argument annehmen und eine Funktion eines einzelnen Arguments zurückgeben, oder im Fall der endgültigen Funktion das tatsächliche Ergebnis zurückgeben . Dies kann entweder automatisch von der Sprache für Sie erledigt werden, oder Sie können eine curry () - Funktion in anderen Sprachen aufrufen, um die Curry-Version zu generieren. Beachten Sie, dass das Aufrufen einer Curry-Funktion mit einem Parameter kein Curry ist. Das Currying ist schon passiert.
MindJuice
7

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.

Anon
quelle
Ich verstehe das nicht - du machst das: >>> am_quote = curry (display_quote, "Alex Martelli"), aber dann machst du das als nächstes: >>> am_quote ("currying", "Wie immer hat Wikipedia eine schöne Zusammenfassung. .. ") Sie haben also eine Funktion mit zwei Argumenten. Es scheint, dass Curry Ihnen drei verschiedene Funktionen geben sollte, die Sie komponieren würden?
Eric M
Ich verwende partiell, um nur einen Parameter zu curry und eine Funktion mit zwei Argumenten zu erzeugen. Wenn Sie möchten, können Sie am_quote weiter curry, um eine zu erstellen, die nur Alex zu einem bestimmten Thema zitiert. Der mathematische Hintergrund mag sich darauf konzentrieren, Funktionen mit nur einem Parameter zu erhalten - aber ich glaube, dass das Fixieren einer beliebigen Anzahl solcher Parameter häufig (wenn auch vom mathematischen Standpunkt aus ungenau) als Currying bezeichnet wird.
Anon
(Übrigens - das '>>>' ist die Eingabeaufforderung im interaktiven Python-Interpreter, die nicht Teil des Codes ist.)
Anon
OK, danke für die Klarstellung über Argumente. Ich weiß über die Python-Interpreter-Eingabeaufforderung Bescheid, ich habe versucht, die Zeilen zu zitieren, aber es hat nicht funktioniert ;-)
Eric M
Nach Ihrem Kommentar habe ich nach anderen Hinweisen gesucht und gefunden, einschließlich hier auf SO, auf den Unterschied zwischen "Currying" und "Currying". "Teilanwendung" als Reaktion auf viele Fälle der ungenauen Verwendung, mit denen ich vertraut bin. Siehe zum Beispiel: stackoverflow.com/questions/218025/…
Anon
5

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.

  • Das Ergebnis curry(func)ist ein Wrapper function(a).
  • Wenn es wie aufgerufen wird sum(1), wird das Argument in der Lexikalischen Umgebung gespeichert und ein neuer Wrapper zurückgegeben function(b).
  • Ruft dann 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.
MidhunKrishna
quelle
4

Wenn Sie verstehen, sind partialSie auf halbem Weg. Die Idee von partialist, 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 incFunktion 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 addzwei Argumente benötigt incwerden, möchte die neue Funktion nur das bArgument - nicht 2 Argumente wie zuvor, da 1 bereits teilweise angewendet wurde. Somit partialist 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 addzwei 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, incohne 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.

Mario
quelle
3

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.

James Black
quelle
3

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

user3804449
quelle
3

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.

Marcus Thornton
quelle
2

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 curriedSumtatsächlich zwei herkömmliche Funktionsaufrufe hintereinander erhalten. Der erste Funktionsaufruf verwendet einen einzelnen Int-Parameter mit dem Namen xund 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 curriedSumwü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
nazar_art
quelle
2

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.

S2dent
quelle
ist die func callbackRückkehr selbst? Es heißt @ callback(str)so let callback = callback(str), Rückruf ist nur der Rückgabewert vonfunc callback
nikk wong
nein, func callback(_:data:)akzeptiert zwei Parameter, hier gebe ich nur einen, den String, also wartet er auf den nächsten ( NSData), deshalb let callbackwartet jetzt eine andere Funktion darauf, dass Daten übergeben werden
S2dent
2

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)());

Prashant Andani
quelle
1

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);
Vadim S.
quelle
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!

Sabitha Kupusamy
quelle
0

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));
  };
madeinQuant
quelle