Frage

Ich habe in mehreren Artikeln und Blogs Verweise auf Curry-Funktionen gesehen, kann aber keine gute Erklärung finden (oder zumindest eine, die Sinn macht!)

War es hilfreich?

Lösung

Beim Currying zerlegt man eine Funktion, die mehrere Argumente übernimmt, in eine Reihe von Funktionen, die einen Teil der Argumente übernehmen.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 nun diese Funktion bearbeiten:

function add (a) {
  return function (b) {
    return a + b;
  }
}

Dies ist eine Funktion, die ein Argument, a, annimmt und eine Funktion zurückgibt, die ein anderes Argument, b, annimmt, 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 3 zu ihrem Argument hinzufügt.Das ist es, was manche Leute als Schließung bezeichnen könnten.Die dritte Anweisung verwendet die Operation add3, um 3 zu 4 zu addieren, was wiederum 7 als Ergebnis ergibt.

Andere Tipps

In einer Algebra von Funktionen ist der Umgang mit Funktionen, die mehrere Argumente (oder ein äquivalentes Argument, das ein N-Tupel ist) annehmen, etwas unelegant – aber wie Moses Schönfinkel (und unabhängig davon Haskell Curry) bewiesen haben, ist dies nicht erforderlich:Alles, was Sie brauchen, sind Funktionen, die ein Argument akzeptieren.

Wie gehen Sie also mit etwas um, das Sie von Natur aus zum Beispiel so ausdrücken würden: f(x,y)?Nun, das halten Sie für gleichbedeutend mit f(x)(y) -- f(x), nennen g, ist eine Funktion, auf die Sie diese Funktion anwenden 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 gewöhnlich, Wikipedia hat dazu einen schönen zusammenfassenden Eintrag mit vielen nützlichen Hinweisen (wahrscheinlich auch zu Ihren Lieblingssprachen;-) sowie einer etwas strengeren mathematischen Behandlung.

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 Hier.Diese Funktion übernimmt die drei notwendigen Parameter als Argumente.

Da Sie nun 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 diese dann teilweise auswerten.Das Ergebnis wäre eine weitere Funktion, die nur zwei Argumente benötigt und die Gravitationskraft von Objekten auf der Erde berechnet.Dies nennt man Curry.

Currying ist eine Transformation, die auf Funktionen angewendet werden kann, um ihnen zu ermöglichen, ein Argument weniger als zuvor anzunehmen.

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 wie folgt:

f 1 2 3

Gibt 6 zurück.

Aus unserer Definition können wir daher die Curry-Funktion für f definieren:-

let curry f = fun x -> f x

Wobei „fun x -> f x“ eine Lambda-Funktion ist, die x => f(x) in C# entspricht.Diese Funktion gibt die Funktion ein, die Sie aufrufen möchten, und gibt eine Funktion zurück, die braucht ein einzelnes Argument und gibt die angegebene Funktion zurück, wobei das erste Argument auf das Eingabeargument gesetzt ist.

Mit unserem vorherigen Beispiel können wir ein Curry von f folgendermaßen erhalten:

let curryf = curry f

Wir können dann Folgendes tun:

let f1 = curryf 1

Das liefert uns eine Funktion f1, die äquivalent zu f1 y z = 1 + y + z ist.Das bedeutet, dass wir Folgendes tun können:-

f1 2 3

Was 6 zurückgibt.

Dieser Prozess wird oft mit der „Teilfunktionsanwendung“ verwechselt, die wie folgt definiert werden kann:

let papply f x = f x

Wir können es jedoch auf mehr als einen Parameter erweitern, d. h.:

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, die einen oder mehrere Parameter weniger erfordert und wie die beiden vorherigen Beispiele zeigen, direkt in der Standard-F#-Funktionsdefinition implementiert ist, sodass wir das vorherige Ergebnis wie folgt erzielen können:

let f1 = f 1
f1 2 3

Das ergibt ein Ergebnis von 6.

Abschließend:-

Der Unterschied zwischen Currying und Teilfunktionsanwendung besteht darin, dass:

Currying übernimmt eine Funktion und stellt eine neue Funktion bereit, die ein einzelnes Argument akzeptiert und die angegebene Funktion zurückgibt, wobei ihr erstes Argument auf dieses Argument gesetzt ist. Dadurch können wir Funktionen mit mehreren Parametern als eine Reihe von Einzelargumentfunktionen 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 übernimmt eine Funktion und ein oder mehrere Argumente und gibt eine Funktion zurück, deren erste 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
  };
};

Erlauben wir uns, es so zu nennen:

let addTen = add(10);

Wenn dies ausgeführt wird 10 wird übergeben als 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 Sie anrufen

 addTen();

Du rufst wirklich an:

 function(y) { return 10 + y };

Wenn Sie also Folgendes tun:

 addTen(4)

es ist dasselbe wie:

function(4) { return 10 + 4} // 14

So unser addTen() fügt immer zehn zu dem hinzu, was wir übergeben.Wir können ähnliche Funktionen auf die gleiche Weise erstellen:

let addTwo = add(2)       // addTwo(); will add two to whatever you pass in
let addSeventy = add(70)  // ... and so on...

Eine Curry-Funktion ist eine Funktion mit mehreren Argumenten, die so umgeschrieben wird, dass sie das erste Argument akzeptiert und eine Funktion zurückgibt, die das zweite Argument akzeptiert, und so weiter.Dadurch können Funktionen mit mehreren Argumenten teilweise auf einige ihrer Anfangsargumente angewendet werden.

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 Ablenkung für Nicht-Python-Programmierer zu vermeiden.)

Bearbeiten zum Hinzufügen:

Sehen http://docs.python.org/library/functools.html?highlight=partial#functools.partial, was auch das Teilobjekt vs.Funktionsunterscheidung in der Art und Weise, wie Python dies implementiert.

Wenn du verstehst partial Du bist auf halbem Weg.Die Idee von partial besteht darin, Argumente vorab auf eine Funktion anzuwenden und eine neue Funktion zurückzugeben, die nur die verbleibenden Argumente benötigt.Wenn diese neue Funktion aufgerufen wird, enthält sie die vorinstallierten Argumente zusammen mit den ihr übergebenen Argumenten.

In Clojure + ist eine Funktion, aber um es ganz deutlich zu machen:

(defn add [a b] (+ a b))

Sie wissen vielleicht, dass die inc Die Funktion addiert einfach 1 zu der übergebenen Zahl.

(inc 7) # => 8

Lassen Sie es uns selbst erstellen partial:

(def inc (partial add 1))

Hier geben wir eine weitere Funktion zurück, die 1 in das erste Argument von geladen hat add.Als add nimmt zwei Argumente an, das neue inc Funktion will nur die b Argument – ​​nicht 2 Argumente wie zuvor, da 1 bereits vorhanden war teilweise angewandt.Daher partial ist ein Tool zum Erstellen neuer Funktionen mit voreingestellten Standardwerten.Aus diesem Grund ordnen Funktionen in einer funktionalen Sprache Argumente häufig vom Allgemeinen zum Spezifischen.Dies macht es einfacher, solche Funktionen wiederzuverwenden, um daraus andere Funktionen zu erstellen.

Stellen Sie sich nun vor, die Sprache wäre intelligent genug, um dies introspektiv zu verstehen add wollte zwei Argumente.Wenn wir ihr ein Argument übergeben haben, anstatt zu zögern, was wäre, wenn die Funktion das Argument, das wir ihr in unserem Namen übergeben haben, teilweise anwendet und dabei weiß, dass wir das andere Argument wahrscheinlich später bereitstellen wollten?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 besonders nützlich, wenn man Funktionen in größeren Transformationen zusammenfassen möchte.Dies würde zu Wandlern führen.

Ich fand diesen Artikel und den Artikel, auf den er verweist, nützlich, um 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, handelt es sich lediglich um 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 2-Parameter-, 3-Parameter- und 4-Parameter-Funktionen benötigen.

Curry kann Ihren Code vereinfachen.Dies ist einer der Hauptgründe, dies zu nutzen.Currying ist ein Prozess, bei dem eine Funktion, die n Argumente akzeptiert, in n Funktionen umgewandelt wird, die nur ein Argument akzeptieren.

Das Prinzip besteht darin, die Argumente der übergebenen Funktion mithilfe der Eigenschaft „Closure“ zu übergeben, 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.Dadurch wird das Programm auch übersichtlicher.Auch die Aufteilung des Codes in kleinere Teile würde ihn wiederverwendbar machen.

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 übersichtlicher zu gestalten und nicht synchronisierte Methoden usw. zu handhaben.

Currying übersetzt eine Funktion von Callable As f(a, b, c) in aufrufbar als f(a)(b)(c).

Ansonsten spricht man vom Currying, wenn man eine Funktion, die mehrere Argumente entgegennimmt, in eine Reihe von Funktionen zerlegt, die einen Teil der Argumente übernehmen.

Im wahrsten Sinne des Wortes ist Currying eine Transformation von Funktionen:von einer Rufart in eine andere.In JavaScript erstellen wir normalerweise einen Wrapper, um die ursprüngliche Funktion beizubehalten.

Currying ruft keine Funktion auf.Es verwandelt es einfach.

Lassen Sie uns eine Curry-Funktion erstellen, die das Currying für Funktionen mit zwei Argumenten durchführt.Mit anderen Worten, curry(f) für zwei Argumente f(a, b) übersetzt es in f(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, besteht die Implementierung aus einer Reihe von Wrappern.

  • Das Ergebnis von curry(func) ist ein Wrapper function(a).
  • Wenn es so heißt sum(1), wird das Argument in der lexikalischen Umgebung gespeichert und ein neuer Wrapper zurückgegeben function(b).
  • Dann sum(1)(2) ruft endlich an function(b) stellt 2 bereit und leitet den Aufruf an die ursprüngliche Summe mit mehreren Argumenten weiter.

Eine Curry -Funktion wird auf mehrere Argument -Listen anstelle von nur einer angewendet.

Hier ist eine reguläre, nicht abgeschnittene Funktion, die zwei Int-Parameter hinzufügt, x und y:

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 von 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 das, wenn Sie anrufen curriedSum, erhalten Sie tatsächlich zwei traditionelle Funktionsaufrufe hintereinander.Der erste Funktionsinformation nimmt einen einzelnen Int -Parameter mit dem Namen x und gibt einen Funktionswert für die zweite Funktion zurück.Diese zweite Funktion verwendet den Int-Parametery.

Hier ist eine Funktion mit dem Namen first das tut im Geiste, was die erste traditionelle Funktionserholung von curriedSum würdest du:

scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int

Anwendung von 1 auf die erste Funktion - mit anderen Worten, die erste Funktion aufrufen und in 1 übergeben -, sorgt die zweite Funktion:

scala> val second = first(1)
second: (Int) => Int = <function1>

Die Anwendung von 2 auf die zweite Funktion liefert das Ergebnis:

scala> second(2)
res6: Int = 3

Ein Beispiel für Currying wäre, wenn Sie Funktionen haben, von denen Sie im Moment 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
}

Hier, da Sie den zweiten Parameter für den Rückruf beim Senden an nicht kennen performAsyncRequest(_:) Sie müssten einen weiteren Lambda-/Verschluss erstellen, um diesen an die Funktion zu senden.

Wie bei allen anderen Antworten hilft Currying dabei, teilweise angewendete Funktionen zu erstellen.Javascript bietet keine native Unterstützung für automatisches Currying.Daher helfen die oben aufgeführten 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).

Hier finden Sie eine einfache Erklärung der Currying-Implementierung in C#.In den Kommentaren habe ich versucht zu zeigen, wie Curry nützlich 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);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top