Was ist der Unterschied zwischen Curry und Teilapplikation?

438

Ich sehe ziemlich oft im Internet verschiedene Beschwerden, dass Beispiele für Currying anderer Leute kein Currying sind, sondern eigentlich nur eine teilweise Anwendung.

Ich habe keine anständige Erklärung dafür gefunden, was Teilanwendung ist oder wie sie sich vom Curry unterscheidet. Es scheint eine allgemeine Verwirrung zu geben, wobei äquivalente Beispiele an einigen Stellen als Curry und an anderen als teilweise Anwendung beschrieben werden.

Könnte mir jemand eine Definition beider Begriffe und Einzelheiten zu deren Unterschieden geben?

SpoonMeiser
quelle

Antworten:

256

Beim Currying wird eine einzelne Funktion von n Argumenten in n Funktionen mit jeweils einem Argument umgewandelt. Gegeben die folgende Funktion:

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

Wenn Curry, 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 Folgendes tun:

f(x)(y)(z);

In vielen funktionalen Sprachen können Sie schreiben f x y z. Wenn Sie nur f x yoder f (x) (y) aufrufen, erhalten Sie eine teilweise angewendete Funktion - der Rückgabewert ist ein Abschluss von lambda(z){z(x(y))}mit übergebenen Werten von x und y an f(x,y).

Eine Möglichkeit , Teil Anwendung zu verwenden ist Funktionen als Teil-Anwendungen allgemeiner Funktionen zu definieren, wie Falte :

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
Mark Cidade
quelle
40
Sie sagen, dass eine teilweise Anwendung erfolgt, wenn Sie eine Funktion curryen und einige, aber nicht alle resultierenden Funktionen verwenden?
SpoonMeiser
9
mehr oder weniger ja. Wenn Sie nur eine Teilmenge der Argumente
Mark Cidade
1
Würde das Ändern einer Funktion f (a, b, c, d) in g (a, b) als Teilanwendung gelten? Oder nur bei Curry-Funktionen? Es tut mir leid, ein Schmerz zu sein, aber ich bin hier auf der Suche nach einer expliziten Antwort.
SpoonMeiser
2
@Mark: Ich denke, dies ist nur eines dieser Konzepte, die den Pedanten in mir zum Vorschein bringen - aber ein Appell an maßgebliche Quellen befriedigt wenig, da sie alle aufeinander zu verweisen scheinen. Wikipedia ist kaum eine maßgebliche Quelle, aber ich verstehe, dass es schwierig ist, viel anderes zu finden. Es genügt zu sagen, dass wir beide das wissen, wovon wir sprechen, und die Macht davon, unabhängig davon, ob wir uns auf die Einzelheiten der Umgangssprache einigen können (oder nicht)! :) Danke Mark!
Jason Bunting
5
@ JasonBunting, In Bezug auf Ihren ersten Kommentar ist das, worüber Sie gesprochen haben, das Entschlüsseln . Beim Currying wird eine Multi-Arg-Funktion als Eingabe verwendet und eine Kette von 1-Arg-Funktionen als Ausgabe zurückgegeben. Beim Entcurrying wird eine Kette von 1-Arg-Funktionen als Eingabe verwendet und eine Multi-Arg-Funktion als Ausgabe zurückgegeben. Wie auf stackoverflow.com/a/23438430/632951
Pacerier
165

Der einfachste Weg, um zu sehen, wie sie sich unterscheiden, besteht darin, ein reales Beispiel zu betrachten . Nehmen wir an, wir haben eine Funktion, Adddie 2 Zahlen als Eingabe verwendet und eine Zahl als Ausgabe Add(7, 5)zurückgibt , z 12. B. return . In diesem Fall:

  • Teilweise Anwendung der FunktionAdd mit einem Wert erhalten 7wir eine neue Funktion als Ausgabe. Diese Funktion selbst nimmt 1 Nummer als Eingabe und gibt eine Nummer aus. So wie:

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

    Also können wir das tun:

    f2 = Partial(Add, 7);
    f2(5); // returns 12;
           // f2(7)(5) is just a syntactic shortcut
    
  • Durch Currying der Funktion erhalten Addwir eine neue Funktion als Ausgabe. Diese Funktion selbst nimmt 1 Nummer als Eingabe und gibt eine weitere neue Funktion aus. Diese dritte Funktion nimmt dann 1 Zahl als Eingabe und gibt eine Zahl als Ausgabe zurück. So wie:

    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
    

    Also können wir das tun:

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

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

Obwohl beide eine Funktion als Ausgabe zurückgeben, haben die zurückgegebenen Funktionen, wie oben gezeigt, völlig unterschiedliche Formen.

Pacerier
quelle
24
Teilanwendung transformiert eine Funktion von n-arybis (x - n)-ary, Curry von n-arybis n * 1-ary. Eine teilweise angewendete Funktion hat einen reduzierten Anwendungsbereich, das heißt, sie Add7ist weniger aussagekräftig als Add. Eine Curry-Funktion ist dagegen genauso ausdrucksstark wie die ursprüngliche Funktion.
Bob
4
Ich glaube, das charakteristischere Merkmal ist, wenn wir f (x, y, z) => R curry, erhalten wir f (x), das g (y) => h (z) => R zurückgibt, wobei jedes ein einzelnes Argument verbraucht; aber wenn wir f (x, y, z) teilweise als f (x) anwenden, erhalten wir g (y, z) => R, dh mit zwei Argumenten. Ohne dieses Merkmal könnten wir sagen, dass Currying wie eine teilweise Anwendung auf 0 Argumente ist, wodurch alle Argumente ungebunden bleiben. In der Realität ist f (), das teilweise auf 0 Argumente angewendet wird, eine Funktion, die im Gegensatz zu curied f () 3 Argumente gleichzeitig verbraucht.
Maksim Gumerov
2
Wiederum ist die richtige Antwort nicht die erste oder die am meisten gewählte: Die einfache Erklärung der Signatur von Curry vs. partiell am Ende dieser Antwort ist wirklich der einfachste Weg, die Frage zu lösen.
Fnl
2
Was bedeutet der Kommentar f2(7)(5) is just a syntactic shortcut? (Ich weiß sehr wenig.) Enthält f2/ "weiß nicht über" 7 noch nicht?
Zach Mierzejewski
@ Pacerier, gibt es curryirgendwo eine Implementierung (glaube nicht, dass es in ist functools)
alancalvitti
51

Hinweis: Dies wurde aus F # Basics übernommen einem hervorragenden Einführungsartikel für .NET-Entwickler, die sich mit funktionaler Programmierung befassen.

Currying bedeutet, eine Funktion mit vielen Argumenten in eine Reihe von Funktionen zu unterteilen, die jeweils ein Argument annehmen und letztendlich das gleiche Ergebnis wie die ursprüngliche Funktion liefern. Currying ist wahrscheinlich das schwierigste Thema für Entwickler, die noch keine Erfahrung mit funktionaler Programmierung haben, insbesondere weil es häufig mit Teilanwendungen verwechselt wird. In diesem Beispiel können Sie beide bei der Arbeit sehen:

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 zwingenden Sprachen unterscheidet. Die zweite Anweisung erstellt eine neue Funktion namens double, indem ein Argument an eine Funktion übergeben wird, die zwei benötigt. Das Ergebnis ist eine Funktion, die ein int-Argument akzeptiert und dieselbe Ausgabe liefert, als hätten Sie multiplizieren mit x gleich 2 und y gleich diesem Argument aufgerufen. In Bezug auf das Verhalten ist es dasselbe wie dieser Code:

let double2 z = multiply 2 z

Oft sagen die Leute fälschlicherweise, dass Multiplikation zu Double verdreht wird. Das ist aber nur etwas wahr. Die Multiplikationsfunktion ist Curry, aber das passiert, wenn sie definiert ist, weil Funktionen in F # standardmäßig Curry sind. Wenn die Doppelfunktion erstellt wird, ist es genauer zu sagen, dass die Multiplikationsfunktion teilweise angewendet wird.

Die Multiplikationsfunktion ist wirklich eine Reihe von zwei Funktionen. Die erste Funktion verwendet ein int-Argument und gibt eine andere Funktion zurück, wodurch x effektiv an einen bestimmten Wert gebunden wird. Diese Funktion akzeptiert auch ein int-Argument, das Sie sich als den Wert vorstellen können, der an y gebunden werden soll. Nach dem Aufrufen dieser zweiten Funktion werden x und y beide gebunden, sodass das Ergebnis das Produkt von x und y ist, wie im Körper von double definiert.

Um double zu erstellen, wird die erste Funktion in der Kette der Multiplikationsfunktionen ausgewertet, um die Multiplikation teilweise anzuwenden. Die resultierende Funktion erhält den Namen double. Wenn double ausgewertet wird, verwendet es sein Argument zusammen mit dem teilweise angewendeten Wert, um das Ergebnis zu erstellen.

dodgy_coder
quelle
33

Interessante Frage. Nach einigem Suchen gab "Partial Function Application is currying" die beste Erklärung, die ich gefunden habe. Ich kann das nicht praktisch sagen Unterschied für mich besonders offensichtlich ist, aber dann bin ich kein FP-Experte ...

Eine weitere nützlich aussehende Seite (von der ich gestehe, dass ich sie noch nicht vollständig gelesen habe) ist "Currying and Partial Application with Java Closures" .

Es sieht so aus, als wäre dies ein weit verbreitetes Begriffspaar, wohlgemerkt.

Jon Skeet
quelle
5
Der erste Link ist genau richtig für die Unterschiede. Hier ist eine andere, die ich nützlich fand: bit.ly/CurryingVersusPartialApplication
Jason Bunting
5
Currying hat mit Tupeln zu tun (eine Funktion, die ein Tupelargument verwendet, in eine Funktion umzuwandeln, die n separate Argumente akzeptiert, und umgekehrt). Teilanwendung ist die Fähigkeit, eine Funktion auf einige Argumente anzuwenden, wodurch eine neue Funktion für die verbleibenden Argumente erhalten wird. Es ist leicht zu merken, wenn Sie nur denken, dass Curry == mit Tupeln zu tun hat.
Don Stewart
9
Von Ihnen gepostete @ Jon-Links sind informativ, aber es ist besser, Ihre Antwort zu erweitern und hier weitere Informationen hinzuzufügen.
Zaheer Ahmed
11
Ich kann nicht glauben, dass Sie 20 Upvotes für ein paar Links und eine Zulassung erhalten haben, bei der Sie den Unterschied zwischen Curry und Teilanwendung nicht wirklich kennen. Gut gespielt, Sir.
AlienWebguy
16

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

Currying ist meistens von theoretischem Interesse: Man kann Berechnungen nur mit unären Funktionen ausdrücken (dh jede Funktion ist unär). In der Praxis und als Nebenprodukt ist es eine Technik, die viele nützliche (aber nicht alle) teilweise funktionale Anwendungen trivial machen kann, wenn die Sprache Curry-Funktionen hat. Auch hier ist es nicht das einzige Mittel, Teilanwendungen zu implementieren. Sie könnten also auf Szenarien stoßen, in denen die teilweise Anwendung auf andere Weise erfolgt, die Leute sie jedoch als Currying verwechseln.

(Beispiel Currying)

In der Praxis würde man nicht nur 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 }}}

anstatt

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

um des Currying willen.

Ji Han
quelle
1
Würden Sie sagen, dass Currying dann ein spezieller Fall einer teilweisen Anwendung ist?
SpoonMeiser
1
@SpoonMeiser, Nein, Currying ist kein spezieller Fall einer Teilanwendung: Eine Teilanwendung einer Funktion mit zwei Eingängen ist nicht dasselbe wie das Currying der Funktion. Siehe stackoverflow.com/a/23438430/632951 .
Pacerier
10

Currying ist eine Funktion eines Arguments, das eine Funktion übernimmt fund eine neue Funktion zurückgibt h. Beachten Sie, dass hein Argument aus nimmt Xund gibt eine Funktion , die Karten Yzu Z:

curry(f) = h 
f: (X x Y) -> Z 
h: X -> (Y -> Z)

Teilanwendung ist eine Funktion von zwei (oder mehr) Argumenten, die eine Funktion fund ein oder mehrere zusätzliche Argumente für feine neue Funktion übernimmt und diese zurückgibt g:

part(f, 2) = g
f: (X x Y) -> Z 
g: Y -> Z

Die Verwirrung entsteht, weil bei einer Funktion mit zwei Argumenten die folgende Gleichheit gilt:

partial(f, a) = curry(f)(a)

Beide Seiten ergeben dieselbe Funktion mit einem Argument.

Die Gleichheit gilt nicht für Funktionen mit höherer Arität, da in diesem Fall beim Currying eine Funktion mit einem Argument zurückgegeben wird, während bei einer Teilanwendung eine Funktion mit mehreren Argumenten zurückgegeben wird.

Der Unterschied liegt auch im Verhalten, während das Currying die gesamte ursprüngliche Funktion rekursiv transformiert (einmal für jedes Argument), ist die teilweise Anwendung nur eine Ersetzung in einem Schritt.

Quelle: Wikipedia Currying .

Roland
quelle
8

Der Unterschied zwischen Curry und Teilanwendung lässt sich am besten anhand des folgenden JavaScript-Beispiels veranschaulichen:

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

var partial = f.bind(null, 1);

6 === partial(2, 3);

Eine teilweise Anwendung führt zu einer Funktion kleinerer Arität; hat im obigen Beispiel feine Arität von 3, während partialnur eine Arität von 2 vorliegt. Noch wichtiger ist, dass eine teilweise angewendete Funktion das Ergebnis sofort beim Aufrufen zurückgibt, nicht eine andere Funktion in der Currykette. Also, wenn Sie so etwas sehenpartial(2)(3) , ist es in Wirklichkeit keine teilweise Anwendung.

Weiterführende Literatur:

gsklee
quelle
"Eine teilweise angewendete Funktion würde das Ergebnis sofort nach dem Aufrufen zurückgeben" - das ist nicht richtig, oder? Wenn ich eine Funktion teilweise anwende, gibt dieser Ausdruck eine Funktion zurück, nicht "ein Ergebnis". Ok, Sie haben wahrscheinlich gemeint, dass diese letztere Funktion, wenn sie mit den verbleibenden Argumenten aufgerufen wird, das Ergebnis zurückgibt, im Gegensatz dazu, einen Schritt nach unten in das Currying zu graben. Aber niemand sagt tatsächlich, dass Sie alle verbleibenden Argumente angeben müssen: Sie können das Ergebnis der Teilanwendung teilweise anwenden, und das wird wieder eine Funktion sein, kein "Ergebnis"
Maksim Gumerov
6

Einfache Antwort

Curry: Mit dieser Funktion können Sie eine Funktion aufrufen, in mehrere Aufrufe aufteilen und pro Aufruf ein Argument angeben.

Teilweise: Ermöglicht das Aufrufen einer Funktion, das Aufteilen in mehrere Aufrufe und das Bereitstellen mehrerer Argumente pro Aufruf.


Einfache Hinweise

Mit beiden können Sie eine Funktion aufrufen, die weniger Argumente liefert (oder besser kumulativ). Tatsächlich binden beide (bei jedem Aufruf) einen bestimmten Wert an bestimmte Argumente der Funktion.

Der wirkliche Unterschied kann gesehen werden, wenn die Funktion mehr als 2 Argumente hat.


Einfaches e (c) (Probe)

(in Javascript)

function process(context, success_callback, error_callback, subject) {...}

Warum immer die Argumente wie Kontext und Rückrufe übergeben, wenn sie immer gleich sind? Binden Sie einfach einige Werte für die Funktion

processSubject = _.partial(process, my_context, my_success, my_error)

und nenne es auf subject1 und foobar mit

processSubject('subject1');
processSubject('foobar');

Bequem, nicht wahr? 😉

Beim Currying müssten Sie jedes Mal ein Argument übergeben

curriedProcess = _.curry(process);
processWithBoundedContext = curriedProcess(my_context);
processWithCallbacks = processWithBoundedContext(my_success)(my_error); // note: these are two sequential calls

result1 = processWithCallbacks('subject1');
// same as: process(my_context, my_success, my_error, 'subject1');
result2 = processWithCallbacks('foobar'); 
// same as: process(my_context, my_success, my_error, 'foobar');

Haftungsausschluss

Ich habe alle akademischen / mathematischen Erklärungen übersprungen. Weil ich es nicht weiß. Vielleicht hat es geholfen 🙃

Kamafeather
quelle
4

Ich hatte diese Frage beim Lernen oft und wurde seitdem oft gestellt. Der einfachste Weg, den Unterschied zu beschreiben, ist, dass beide gleich sind :) Lassen Sie mich erklären ... es gibt offensichtlich Unterschiede.

Sowohl bei der teilweisen Anwendung als auch beim Currying werden Argumente für eine Funktion bereitgestellt, möglicherweise nicht alle auf einmal. Ein ziemlich kanonisches Beispiel ist das Hinzufügen von zwei Zahlen. Im Pseudocode (tatsächlich JS ohne Schlüsselwörter) kann die Basisfunktion die folgende sein:

add = (x, y) => x + y

Wenn ich eine "addOne" -Funktion wollte, könnte ich sie teilweise anwenden oder curry:

addOneC = curry(add, 1)
addOneP = partial(add, 1)

Jetzt ist es klar, sie zu benutzen:

addOneC(2) #=> 3
addOneP(2) #=> 3

Was ist der Unterschied? Nun, es ist subtil, aber eine teilweise Anwendung beinhaltet die Angabe einiger Argumente, und die zurückgegebene Funktion führt dann beim nächsten Aufruf die Hauptfunktion aus, während das Currying so lange wartet, bis alle erforderlichen Argumente vorhanden sind:

curriedAdd = curry(add) # notice, no args are provided
addOne = curriedAdd(1) # returns a function that can be used to provide the last argument
addOne(2) #=> returns 3, as we want

partialAdd = partial(add) # no args provided, but this still returns a function
addOne = partialAdd(1) # oops! can only use a partially applied function once, so now we're trying to add one to an undefined value (no second argument), and we get an error

Kurz gesagt, verwenden Sie eine Teilanwendung, um einige Werte vorab auszufüllen. Beachten Sie dabei, dass die Methode beim nächsten Aufruf ausgeführt wird und alle nicht bereitgestellten Argumente undefiniert bleiben. Verwenden Sie Currying, wenn Sie eine teilweise angewendete Funktion so oft wie nötig kontinuierlich zurückgeben möchten, um die Funktionssignatur zu erfüllen. Ein letztes erfundenes Beispiel:

curriedAdd = curry(add)
curriedAdd()()()()()(1)(2) # ugly and dumb, but it works

partialAdd = partial(add)
partialAdd()()()()()(1)(2) # second invocation of those 7 calls fires it off with undefined parameters

Hoffe das hilft!

UPDATE: Einige Sprachen oder lib-Implementierungen ermöglichen es Ihnen, eine Arität (Gesamtzahl der Argumente in der endgültigen Bewertung) an die teilweise Anwendungsimplementierung zu übergeben, die meine beiden Beschreibungen zu einem verwirrenden Durcheinander zusammenführen kann ... aber an diesem Punkt sind die beiden Techniken weitgehend austauschbar.

sonnig-mittal
quelle
3

Für mich muss die Teilanwendung eine neue Funktion erstellen, bei der die verwendeten Argumente vollständig in die resultierende Funktion integriert sind.

Die meisten funktionalen Sprachen implementieren Currying durch Rückgabe eines Verschlusses: Bei teilweiser Anwendung nicht unter Lambda auswerten. Damit eine teilweise Anwendung interessant ist, müssen wir einen Unterschied zwischen Curry und teilweiser Anwendung machen und die teilweise Anwendung als Curry plus Bewertung unter Lambda betrachten.

Taoufik Dachraoui
quelle
3

Ich könnte mich hier sehr irren, da ich keinen starken Hintergrund in theoretischer Mathematik oder funktionaler Programmierung habe, aber von meinem kurzen Streifzug durch FP scheint es, dass Currying dazu neigt, eine Funktion von N Argumenten in N Funktionen eines Arguments umzuwandeln. wohingegen eine teilweise Anwendung [in der Praxis] bei variadischen Funktionen mit einer unbestimmten Anzahl von Argumenten besser funktioniert. Ich weiß, dass einige der Beispiele in früheren Antworten dieser Erklärung widersprechen, aber es hat mir am meisten geholfen, die Konzepte zu trennen. Betrachten Sie dieses Beispiel (aus Gründen der Prägnanz in CoffeeScript geschrieben, ich entschuldige mich, wenn es weiter verwirrt, aber bitte bei Bedarf um Klarstellung):

# partial application
partial_apply = (func) ->
  args = [].slice.call arguments, 1
  -> func.apply null, args.concat [].slice.call arguments

sum_variadic = -> [].reduce.call arguments, (acc, num) -> acc + num

add_to_7_and_5 = partial_apply sum_variadic, 7, 5

add_to_7_and_5 10 # returns 22
add_to_7_and_5 10, 11, 12 # returns 45

# currying
curry = (func) ->
  num_args = func.length
  helper = (prev) ->
    ->
      args = prev.concat [].slice.call arguments
      return if args.length < num_args then helper args else func.apply null, args
  helper []

sum_of_three = (x, y, z) -> x + y + z
curried_sum_of_three = curry sum_of_three
curried_sum_of_three 4 # returns a function expecting more arguments
curried_sum_of_three(4)(5) # still returns a function expecting more arguments
curried_sum_of_three(4)(5)(6) # returns 15
curried_sum_of_three 4, 5, 6 # returns 15

Dies ist offensichtlich ein erfundenes Beispiel, aber beachten Sie, dass das teilweise Anwenden einer Funktion, die eine beliebige Anzahl von Argumenten akzeptiert, es uns ermöglicht, eine Funktion auszuführen, jedoch mit einigen vorläufigen Daten. Das Currying einer Funktion ist ähnlich, ermöglicht es uns jedoch, eine N-Parameter-Funktion in Teilen auszuführen, bis, aber nur bis alle N Parameter berücksichtigt sind.

Auch dies ist meine Einstellung zu Dingen, die ich gelesen habe. Wenn jemand anderer Meinung ist, würde ich mich über einen Kommentar zum Warum und nicht über eine sofortige Ablehnung freuen. Wenn das CoffeeScript schwer zu lesen ist, besuchen Sie bitte coffeescript.org, klicken Sie auf "try coffeescript" und fügen Sie meinen Code ein, um die kompilierte Version anzuzeigen, die (hoffentlich) sinnvoller sein kann. Vielen Dank!

sonnig-mittal
quelle
2

Ich gehe davon aus, dass die meisten Leute, die diese Frage stellen, bereits mit den Grundkonzepten vertraut sind, sodass sie nicht darüber sprechen müssen. Es ist die Überlappung, die den verwirrenden Teil ausmacht.

Möglicherweise können Sie die Konzepte vollständig nutzen, aber Sie verstehen sie zusammen als diese pseudoatomare amorphe konzeptuelle Unschärfe. Was fehlt, ist zu wissen, wo die Grenze zwischen ihnen liegt.

Anstatt zu definieren, was jeder ist, ist es einfacher, nur ihre Unterschiede hervorzuheben - die Grenze.

Currying ist, wenn Sie die Funktion definieren .

Teilanwendung ist, wenn Sie die Funktion aufrufen .

Die Anwendung ist mathematisch gesprochen, um eine Funktion aufzurufen.

Für eine teilweise Anwendung muss eine Curry-Funktion aufgerufen und eine Funktion als Rückgabetyp abgerufen werden.

Brennan Cheung
quelle
1

Es gibt hier andere gute Antworten, aber ich glaube, dass dieses Beispiel (nach meinem Verständnis) in Java für einige Leute von Nutzen sein könnte:

public static <A,B,X> Function< B, X > partiallyApply( BiFunction< A, B, X > aBiFunction, A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< X > partiallyApply( Function< A, X > aFunction, A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  A, Function< B, X >  > curry( BiFunction< A, B, X > bif ){
    return a -> partiallyApply( bif, a );
}

Beim Currying erhalten Sie eine Funktion mit einem Argument zum Erstellen von Funktionen, bei der die Teilanwendung eine Wrapper-Funktion erstellt, die ein oder mehrere Argumente fest codiert.

Wenn Sie kopieren und einfügen möchten, ist das Folgende lauter, aber benutzerfreundlicher, da die Typen milder sind:

public static <A,B,X> Function< ? super B, ? extends X > partiallyApply( final BiFunction< ? super A, ? super B, X > aBiFunction, final A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< ? extends X > partiallyApply( final Function< ? super A, X > aFunction, final A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  ? super A,  Function< ? super B, ? extends X >  > curry( final BiFunction< ? super A, ? super B, ? extends X > bif ){
    return a -> partiallyApply( bif, a );
}
Schlitten
quelle
Das Folgende gab mir den wichtigsten Einblick: "Beim Currying erhalten Sie eine Funktion mit einem Argument zum Erstellen von Funktionen, bei der die Teilanwendung eine Wrapper-Funktion erstellt, die ein oder mehrere Argumente fest codiert."
Roland
0

Beim Schreiben habe ich Currying und Uncurrying verwechselt. Sie sind inverse Transformationen von Funktionen. Es ist wirklich egal, wie du was nennst, solange du bekommst, was die Transformation und ihre Umkehrung darstellen.

Uncurrying ist nicht sehr klar definiert (oder vielmehr gibt es "widersprüchliche" Definitionen, die alle den Geist der Idee erfassen). Grundsätzlich bedeutet dies, eine Funktion, die mehrere Argumente akzeptiert, in eine Funktion umzuwandeln, die ein einzelnes Argument akzeptiert. Zum Beispiel,

(+) :: Int -> Int -> Int

Wie verwandeln Sie dies in eine Funktion, die ein einziges Argument akzeptiert? Du betrügst natürlich!

plus :: (Int, Int) -> Int

Beachten Sie, dass plus jetzt ein einziges Argument benötigt (das aus zwei Dingen besteht). Super!

Was ist der Sinn davon? Wenn Sie eine Funktion haben, die zwei Argumente akzeptiert, und Sie haben zwei Argumente, ist es schön zu wissen, dass Sie die Funktion auf die Argumente anwenden können und trotzdem das bekommen, was Sie erwarten. Tatsächlich ist die Installation bereits vorhanden, sodass Sie keine expliziten Mustervergleiche durchführen müssen. Alles was du tun musst, ist:

(uncurry (+)) (1,2)

Was ist also eine Teilfunktionsanwendung? Es ist eine andere Möglichkeit, eine Funktion in zwei Argumenten in eine Funktion mit einem Argument umzuwandeln. Es funktioniert jedoch anders. Nehmen wir noch einmal (+) als Beispiel. Wie können wir daraus eine Funktion machen, die ein einzelnes Int als Argument verwendet? Wir betrügen!

((+) 0) :: Int -> Int

Das ist die Funktion, die jedem Int Null hinzufügt.

((+) 1) :: Int -> Int

addiert 1 zu jedem Int. In jedem dieser Fälle wird (+) "teilweise angewendet".

keine Männer
quelle