497 Stimmen

Was ist der Unterschied zwischen Currying und partieller Anwendung?

Ich erlebe im Internet häufig, dass sich andere darüber beschweren, dass ihre Beispiele für das Striegeln gar kein Striegeln sind, sondern nur eine teilweise Anwendung.

Ich habe keine vernünftige Erklärung dafür gefunden, was partielle Anwendung ist oder wie sie sich vom Curry unterscheidet. Es scheint eine allgemeine Verwirrung zu herrschen, wobei gleichwertige Beispiele an einigen Stellen als Currying und an anderen als Partial Application bezeichnet werden.

Könnte mir jemand eine Definition der beiden Begriffe geben und erläutern, wie sie sich unterscheiden?

287voto

Mark Cidade Punkte 95914

Currying ist die Umwandlung einer einzelnen Funktion von n Argumente in n Funktionen mit jeweils einem einzigen Argument. Gegeben sei die folgende Funktion:

function f(x,y,z) { z(x(y));}

Wenn es als Curry zubereitet wird:

function f(x) { lambda(y) { lambda(z) { z(x(y)); } } }

Um die vollständige Anwendung von f(x,y,z) zu erhalten, müssen Sie dies tun:

f(x)(y)(z);

In vielen funktionalen Sprachen können Sie f x y z . Wenn Sie nur anrufen f x y o f(x)(y) dann erhalten Sie eine teilweise angewandte Funktion - der Rückgabewert ist eine Schließung von lambda(z){z(x(y))} mit Übergabe der Werte von x und y an f(x,y) .

Eine Möglichkeit, partielle Anwendungen zu verwenden, besteht darin, Funktionen als partielle Anwendungen von verallgemeinerten Funktionen zu definieren, z. B. falten :

function fold(combineFunction, accumulator, list) {/* ... */}
function sum     = curry(fold)(lambda(accum,e){e+accum}))(0);
function length  = curry(fold)(lambda(accum,_){1+accum})(empty-list);
function reverse = curry(fold)(lambda(accum,e){concat(e,accum)})(empty-list);

/* ... */
@list = [1, 2, 3, 4]
sum(list) //returns 10
@f = fold(lambda(accum,e){e+accum}) //f = lambda(accumulator,list) {/*...*/}
f(0,list) //returns 10
@g = f(0) //same as sum
g(list)  //returns 10

183voto

Pacerier Punkte 80774

Der einfachste Weg, um zu sehen, wie sie sich unterscheiden, ist die Betrachtung einer echtes Beispiel . Nehmen wir an, wir haben eine Funktion Add die 2 Zahlen als Eingabe annimmt und eine Zahl als Ausgabe zurückgibt, z. B. Add(7, 5) gibt zurück. 12 . In diesem Fall:

  • Teilweise Anwendung die Funktion Add mit einem Wert 7 gibt uns eine neue Funktion als Ausgabe. Diese Funktion selbst nimmt 1 Zahl als Eingabe und gibt eine Zahl aus. Als solche:

    Partial(Add, 7); // returns a function f2 as output
    
                     // f2 takes 1 number as input and returns a number as output

    Wir können also Folgendes tun:

    f2 = Partial(Add, 7);
    f2(5); // returns 12;
           // f2(7)(5) is just a syntactic shortcut
  • Currying die Funktion Add gibt uns eine neue Funktion als Ausgabe. Diese Funktion selbst nimmt 1 Zahl als Eingabe und gibt aus dennoch eine weitere neue Funktion. Diese dritte Funktion nimmt dann 1 Zahl als Eingabe und gibt eine Zahl als Ausgabe zurück. Als solche:

    Curry(Add); // returns a function f2 as output
    
                // f2 takes 1 number as input and returns a function f3 as output
                // i.e. f2(number) = f3
    
                // f3 takes 1 number as input and returns a number as output
                // i.e. f3(number) = number

    Wir können also Folgendes tun:

    f2 = Curry(Add);
    f3 = f2(7);
    f3(5); // returns 12

Mit anderen Worten: "Currying" und "partielle Anwendung" sind zwei völlig unterschiedliche Funktionen. Currying benötigt genau 1 Eingabe, während eine Teilanwendung 2 (oder mehr) Eingaben erfordert.

Obwohl beide eine Funktion als Ausgabe zurückgeben, sind die zurückgegebenen Funktionen von völlig unterschiedlicher Form, wie oben gezeigt.

57voto

dodgy_coder Punkte 12043

Anmerkung: Dies wurde entnommen aus F#-Grundlagen ein ausgezeichneter Einführungsartikel für .NET-Entwickler, die in die funktionale Programmierung einsteigen.

Currying bedeutet, eine Funktion mit vielen Argumenten in eine Reihe zu zerlegen in eine Reihe von Funktionen, die jeweils ein Argument aufnehmen und letztlich das das gleiche Ergebnis wie die ursprüngliche Funktion. Currying ist wahrscheinlich die größte schwierigste Thema für Entwickler, die neu in der funktionalen Programmierung sind, vor allem weil es oft mit partieller Anwendung verwechselt wird. Sie können beides bei der Arbeit sehen in diesem Beispiel:

let multiply x y = x * y    
let double = multiply 2
let ten = double 5

Sie sollten sofort ein Verhalten feststellen, das sich von den meisten imperativen Sprachen unterscheidet. Die zweite Anweisung erzeugt eine neue Funktion namens double, indem ein Argument an eine Funktion übergeben wird, die zwei Argumente entgegennimmt. Das Ergebnis ist eine Funktion, die ein int-Argument annimmt und die gleiche Ausgabe liefert die gleiche Ausgabe, als hätten Sie multiply mit x gleich 2 und y gleich diesem Argument. In Bezug auf das Verhalten ist es dasselbe wie das folgende Code:

let double2 z = multiply 2 z

Oft wird fälschlicherweise behauptet, dass das Multiplizieren mit Curry das Doppelte ergibt. Das ist aber nur zum Teil richtig. Die Multiplikationsfunktion wird gekrümmt, aber aber das passiert, wenn sie definiert wird, weil Funktionen in F# standardmäßig kuriert Standard sind. Wenn die Funktion double erstellt wird, ist es genauer zu sagen zu sagen, dass die Multiplikationsfunktion teilweise angewendet wird.

Die Multiplikationsfunktion ist eigentlich eine Folge von zwei Funktionen. Die erste Funktion nimmt ein int-Argument und gibt eine andere Funktion zurück, die x effektiv an einen bestimmten Wert bindet. Diese Funktion akzeptiert auch ein ein int-Argument, das Sie sich als den Wert vorstellen können, der an y gebunden werden soll. Aufruf dieser zweiten Funktion sind x und y beide gebunden, so dass das Ergebnis das Produkt aus x und y, wie es im Körper von double definiert ist.

Um double zu erzeugen, muss die erste Funktion in der Kette von multiply Funktionen wird ausgewertet, um multiply teilweise anzuwenden. Die resultierende Funktion wird der Name double gegeben. Wenn double ausgewertet wird, verwendet sie sein Argument zusammen mit dem teilweise angewandten Wert, um das Ergebnis.

38voto

Jon Skeet Punkte 1325502

Eine interessante Frage. Nach ein wenig Suche, "Partielle Funktionsanwendung ist kein Currying" gab die beste Erklärung, die ich gefunden habe. Ich kann nicht sagen, dass die praktisch Unterschied ist für mich besonders offensichtlich, aber ich bin ja auch kein FP-Experte...

Eine weitere nützlich aussehende Seite (die ich zugegebenermaßen noch nicht vollständig gelesen habe) ist "Currying und partielle Anwendung mit Java Closures" .

Es sieht so aus, als ob es sich hier um ein weithin verwechseltes Begriffspaar handelt, wohlgemerkt.

18voto

Ji Han Punkte 611

Ich habe dies in einem anderen Thema beantwortet https://stackoverflow.com/a/12846865/1685865 . Kurz gesagt, geht es bei der Anwendung einer partiellen Funktion darum, einige Argumente einer gegebenen multivariablen Funktion zu fixieren, um eine andere Funktion mit weniger Argumenten zu erhalten, während es beim Currying darum geht, eine Funktion mit N Argumenten in eine unäre Funktion zu verwandeln, die eine unäre Funktion zurückgibt... [Ein Beispiel für Currying wird am Ende dieses Beitrags gezeigt.]

Currying ist hauptsächlich von theoretischem Interesse: Man kann Berechnungen nur mit unären Funktionen ausdrücken (d. h. jede Funktion ist unär). In der Praxis und als Nebenprodukt ist dies eine Technik, die viele nützliche (aber nicht alle) partielle funktionale Anwendungen trivial machen kann, wenn die Sprache curried functions hat. Auch hier ist dies nicht die einzige Möglichkeit, partielle Anwendungen zu implementieren. Man kann also auf Szenarien stoßen, in denen partielle Anwendungen auf andere Weise implementiert werden, aber die Leute verwechseln sie mit Currying.

(Beispiel für Currying)

In der Praxis würde man nicht einfach schreiben

lambda x: lambda y: lambda z: x + y + z

oder das entsprechende Javascript

function (x) { return function (y){ return function (z){ return x + y + z }}}

anstelle von

lambda x, y, z: x + y + z

um des Currys willen.

CodeJaeger.com

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.

Powered by:

X