it-swarm.com.de

JavaScript-Curry: Was sind die praktischen Anwendungen?

Ich glaube nicht, dass ich mich noch mit dem Curry beschäftigt habe. Ich verstehe, was es tut und wie es geht. Ich kann mir einfach keine Situation vorstellen, in der ich sie verwenden würde.

Wo verwenden Sie Currying in JavaScript (oder wo werden die wichtigsten Bibliotheken verwendet)? DOM-Manipulation oder allgemeine Anwendungsentwicklungsbeispiele sind willkommen.

Eine der Antworten erwähnt die Animation. Funktionen wie slideUp, fadeIn nehmen ein Element als Argumente an und sind normalerweise eine curried-Funktion, die die Funktion höherer Ordnung mit der standardmäßig integrierten "Animationsfunktion" zurückgibt. Warum ist das besser als nur die höhere Funktion mit einigen Standardwerten anzuwenden?

Gibt es Nachteile bei der Verwendung?

Hier sind einige nützliche Ressourcen zum JavaScript-Currying:

Ich werde weitere hinzufügen, wenn sie in den Kommentaren auftauchen.


Nach den Antworten sind Currying und Teilanwendung im Allgemeinen Bequemlichkeitstechniken.

Wenn Sie häufig eine übergeordnete Funktion „verfeinern“, indem Sie sie mit derselben Konfiguration aufrufen, können Sie die übergeordnete Funktion zur Erstellung einfacher, übersichtlicher Hilfsmethoden verwenden (oder die teilweise Funktion von Resig verwenden).

166
Dave Nolan

@Hank Gay

Als Antwort auf den Kommentar von EmbiggensTheMind:

Ich kann mir keine Instanz vorstellen, bei der currying - allein - in JavaScript nützlich ist; Dies ist eine Technik zum Konvertieren von Funktionsaufrufen mit mehreren Argumenten in Ketten von Funktionsaufrufen mit einem einzigen Argument für jeden Aufruf. JavaScript unterstützt jedoch mehrere Argumente in einem einzigen Funktionsaufruf. 

In JavaScript - und ich nehme an, dass die meisten anderen tatsächlichen Sprachen (nicht Lambda-Kalkül) - mit der teilweisen Anwendung einhergehen. John Resig erklärt es besser , aber der Gist ist eine Logik, die auf zwei oder mehr Argumente angewendet wird, und Sie kennen nur die Werte für einige dieser Argumente. 

Sie können partielle Anwendung/Currying verwenden, um diese bekannten Werte zu korrigieren und eine Funktion zurückzugeben, die nur die Unbekannten akzeptiert und später aufgerufen wird, wenn Sie tatsächlich die Werte haben, die Sie übergeben möchten. Dies ist eine einfache Möglichkeit, um zu vermeiden, dass Sie sich wiederholen, wenn Sie dieselben JavaScript-integrierten Funktionen immer und immer wieder mit denselben Werten aufgerufen haben, außer einem. Um Johns Beispiel zu stehlen:

String.prototype.csv = String.prototype.split.partial(/,\s*/);
var results = "John, Resig, Boston".csv();
alert( (results[1] == "Resig") + " The text values were split properly" );
31
Hank Gay

Hier ist eine interessante UND praktische Verwendung von Currying in JavaScript, das Verschlüsse verwendet :

function converter(toUnit, factor, offset, input) {
    offset = offset || 0;
    return [((offset + input) * factor).toFixed(2), toUnit].join(" ");
}

var milesToKm = converter.curry('km', 1.60936, undefined);
var poundsToKg = converter.curry('kg', 0.45460, undefined);
var farenheitToCelsius = converter.curry('degrees C', 0.5556, -32);

milesToKm(10);            // returns "16.09 km"
poundsToKg(2.5);          // returns "1.14 kg"
farenheitToCelsius(98);   // returns "36.67 degrees C"

Dies basiert auf der Erweiterung curry von Function, obwohl Sie, wie Sie sehen, nur apply (nichts zu schickes) verwendet:

Function.prototype.curry = function() {
    if (arguments.length < 1) {
        return this; //nothing to curry with - return function
    }
    var __method = this;
    var args = toArray(arguments);
    return function() {
        return __method.apply(this, args.concat([].slice.apply(null, arguments)));
    }
}
110
Prisoner ZERO

Ich habe Funktionen gefunden, die functools.partial von Python ähneln und in JavaScript nützlicher sind:

function partial(fn) {
  return partialWithScope.apply(this,
    Array.prototype.concat.apply([fn, this],
      Array.prototype.slice.call(arguments, 1)));
}

function partialWithScope(fn, scope) {
  var args = Array.prototype.slice.call(arguments, 2);
  return function() {
    return fn.apply(scope, Array.prototype.concat.apply(args, arguments));
  };
}

Warum möchten Sie es verwenden? Eine häufige Situation, in der Sie dies verwenden möchten, ist, wenn Sie this in einer Funktion an einen Wert binden möchten:

var callback = partialWithScope(Object.function, obj);

Wenn nun Callback aufgerufen wird, zeigt this auf obj. Dies ist nützlich in Ereignissituationen oder um Platz zu sparen, da Code normalerweise kürzer wird.

Currying ähnelt teilweise, mit dem Unterschied, dass die Funktion, die das Currying zurückgibt, nur ein Argument akzeptiert (soweit ich das verstehe).

6
Armin Ronacher

Einverständnis mit Hank Gay - Dies ist in bestimmten funktionellen Programmiersprachen äußerst nützlich - weil es ein notwendiger Bestandteil ist. In Haskell können Sie zum Beispiel nicht einfach mehrere Parameter für eine Funktion übernehmen - dies ist bei der reinen Funktionsprogrammierung nicht möglich. Sie nehmen jeweils einen Parameter und bauen Ihre Funktion auf. In JavaScript ist es einfach nicht notwendig, obwohl es Beispiele wie "Converter" gibt. Hier ist derselbe Konvertercode, ohne dass ein Currying erforderlich ist:

var converter = function(ratio, symbol, input) {
    return (input*ratio).toFixed(2) + " " + symbol;
}

var kilosToPoundsRatio = 2.2;
var litersToUKPintsRatio = 1.75;
var litersToUSPintsRatio = 1.98;
var milesToKilometersRatio = 1.62;

converter(kilosToPoundsRatio, "lbs", 4); //8.80 lbs
converter(litersToUKPintsRatio, "imperial pints", 2.4); //4.20 imperial pints
converter(litersToUSPintsRatio, "US pints", 2.4); //4.75 US pints
converter(milesToKilometersRatio, "km", 34); //55.08 km

Ich wünsche mir, dass Douglas Crockford in "JavaScript: The Good Parts" eher die Geschichte und den tatsächlichen Einsatz von Currying erwähnt hat als seine beiläufigen Bemerkungen. Die längste Zeit, nachdem ich das gelesen hatte, war ich verblüfft, bis ich Funktionsprogrammierung studierte und merkte, dass es dort herkam.

Nach einigen weiteren Überlegungen kann ich sagen, dass es einen gültigen Anwendungsfall für das Currying in JavaScript gibt: Wenn Sie versuchen, mit rein funktionalen Programmiertechniken unter Verwendung von JavaScript zu schreiben. Scheint aber ein seltener Anwendungsfall zu sein.

5
Byron Katz

Hier ist ein Beispiel.

Ich instrumentiere eine Reihe von Feldern mit JQuery, damit ich sehen kann, was die Benutzer vorhaben. Der Code sieht so aus:

$('#foo').focus(trackActivity);
$('#foo').blur(trackActivity);
$('#bar').focus(trackActivity);
$('#bar').blur(trackActivity);

(Für Nicht-JQuery-Benutzer sage ich, dass jedes Mal, wenn ein paar Felder den Fokus verlieren oder verlieren, die Funktion trackActivity () aufgerufen werden soll. Ich könnte auch eine anonyme Funktion verwenden, aber ich müsste sie duplizieren 4 mal, also zog ich es raus und nannte es.)

Nun stellt sich heraus, dass eines dieser Felder anders behandelt werden muss. Ich möchte in der Lage sein, einen Parameter für einen dieser Aufrufe zu übergeben, der an unsere Tracking-Infrastruktur weitergeleitet wird. Mit Currying kann ich.

2
William Pietri

Es ist keine Magie oder irgendetwas ... nur eine angenehme Abkürzung für anonyme Funktionen.

partial(alert, "FOO!") entspricht function(){alert("FOO!");}

partial(Math.max, 0) entspricht function(x){return Math.max(0, x);}

Die Aufrufe der partiellen ( MochiKit - Terminologie. Ich denke, einige andere Bibliotheken geben Funktionen eine .curry-Methode, die dasselbe tut), sehen etwas schöner und weniger laut aus als die anonymen Funktionen.

2
Marijn

JavaScript-Funktionen werden in anderen Funktionssprachen als Lamda bezeichnet. Es kann verwendet werden, um eine neue API (leistungsfähigere oder komplexere Funktion) zu erstellen, die auf der einfachen Eingabe eines anderen Entwicklers basiert. Curry ist nur eine der Techniken. Sie können es verwenden, um eine vereinfachte API zu erstellen, um eine komplexe API aufzurufen. Wenn Sie der Entwickler sind, der die vereinfachte API verwendet (z. B. verwenden Sie jQuery, um einfache Manipulationen durchzuführen), müssen Sie Curry nicht verwenden. Wenn Sie jedoch die vereinfachte API erstellen möchten, ist Curry Ihr Freund. Sie müssen ein Javascript-Framework (wie jQuery, Mootools) oder eine Bibliothek schreiben, dann können Sie dessen Leistungsfähigkeit erkennen. Ich habe eine erweiterte Curry-Funktion unter http://blog.semanticsworks.com/2011/03/enhanced-curry-method.html geschrieben. Sie müssen nicht die Curry-Methode zum Currying verwenden, es hilft nur beim Currying, aber Sie können dies immer manuell tun, indem Sie eine Funktion A () {} schreiben, um eine andere Funktion B () {} zurückzugeben. Um es interessanter zu machen, verwenden Sie die Funktion B(), um eine andere Funktion C () zurückzugeben.

1
Fred Yang

Ich kenne den alten Thread, aber ich muss zeigen, wie dies in Javascript-Bibliotheken verwendet wird:

Ich werde die Bibliothek lodash.js verwenden, um diese Konzepte konkret zu beschreiben.

Beispiel:

var fn = function(a,b,c){ 
return a+b+c+(this.greet || ‘'); 
}

Teilanwendung:

var partialFnA = _.partial(fn, 1,3);

Currying: 

var curriedFn = _.curry(fn);

Bindung: 

var boundFn = _.bind(fn,object,1,3 );//object= {greet: ’!'}

verwendungszweck:

curriedFn(1)(3)(5); // gives 9 
or 
curriedFn(1,3)(5); // gives 9 
or 
curriedFn(1)(_,3)(2); //gives 9


partialFnA(5); //gives 9

boundFn(5); //gives 9!

unterschied:

nach dem Currying erhalten wir eine neue Funktion ohne voreingestellte Parameter.

nach teilweiser Anwendung erhalten wir eine Funktion, die mit einigen Parametern vorgebunden ist.

in der Bindung können wir einen Kontext binden, der verwendet wird, um "this" zu ersetzen, wenn der Standardwert einer Funktion nicht im Fensterbereich liegt.

Hinweis: Das Rad muss nicht neu erfunden werden. Teilanwendung/Bindung/Currying sind sehr verwandt. Sie können den Unterschied oben sehen. Verwenden Sie diese Bedeutung überall und die Leute werden erkennen, was Sie tun, ohne Probleme beim Verständnis zu haben. Außerdem müssen Sie weniger Code verwenden.

1
Shishir Arora

Für Bibliotheken, die es verwenden, gibt es immer Functional .

Wann ist es nützlich in JS? Wahrscheinlich ist es zu den gleichen Zeiten in anderen modernen Sprachen nützlich, aber ich kann mich nur bei Verwendung mit Teilanwendung sehen.

1
Hank Gay

Ich würde sagen, dass höchstwahrscheinlich alle Animationsbibliotheken in JS Currying verwenden. Anstatt für jeden Aufruf eine Menge betroffener Elemente und eine Funktion übergeben zu müssen, die beschreibt, wie sich das Element verhalten soll, an eine Funktion höherer Ordnung, die alle Zeitfunktionen sicherstellt, ist es für den Kunden im Allgemeinen einfacher, sie als öffentliche API zu veröffentlichen funktionieren wie "slideUp", "fadeIn", die nur Elemente als Argumente verwenden. Dies sind nur einige Funktionen, die die Funktion höherer Ordnung mit der standardmäßig eingebauten "Animationsfunktion" zurückgeben.

1
gizmo

Ich wollte nur einige Ressourcen für Functional.js hinzufügen:

Vortrag/Konferenz, in der einige Anwendungen erläutert werden http://www.youtube.com/watch?v=HAcN3JyQoyY

Funktionale.js-Bibliothek aktualisiert: https://github.com/loop-recur/FunctionalJS Einige nette Helfer (sorry neu hier, kein Ruf: p): /Loop-recur/PreludeJS

Ich habe diese Bibliothek in letzter Zeit häufig verwendet, um die Wiederholung in einer Hilfsbibliothek von js IRC Clients zu reduzieren. Es ist großartig - es hilft wirklich, den Code aufzuräumen und zu vereinfachen. 

Wenn Leistung zu einem Problem wird (diese Bibliothek ist jedoch ziemlich leicht), können Sie sie auch einfach mithilfe einer nativen Funktion neu schreiben.

0
megawac

Sie können native Bindung für eine schnelle, einzeilige Lösung verwenden

function clampAngle(min, max, angle) {
    var result, delta;
    delta = max - min;
    result = (angle - min) % delta;
    if (result < 0) {
        result += delta;
    }
    return min + result;
};

var clamp0To360 = clampAngle.bind(null, 0, 360);

console.log(clamp0To360(405)) // 45

0
cstuncsik

Noch ein Stich, von der Arbeit mit Versprechen.

(Disclaimer: JS noob, kommt aus der Python-Welt. Selbst dort wird currying nicht allzu oft verwendet, aber es kann sich als nützlich erweisen. Ich habe die Currying-Funktion (siehe Links)

Zuerst beginne ich mit einem Ajax-Anruf. Ich habe eine bestimmte Verarbeitung für den Erfolg, aber beim Scheitern möchte ich dem Benutzer nur die Rückmeldung geben, dass der Aufruf von etwas zu etwas Fehler geführt hat. In meinem aktuellen Code zeige ich das Fehlerfeedback in einem Bootstrap-Panel an, aber hier verwende ich nur die Protokollierung.

Ich habe meine Live-URL geändert, damit dies fehlschlägt.

function ajax_batch(e){
    var url = $(e.target).data("url");

    //induce error
    url = "x" + url;

    var promise_details = $.ajax(
        url,
        {
            headers: { Accept : "application/json" },
            // accepts : "application/json",
            beforeSend: function (request) {
                if (!this.crossDomain) {
                    request.setRequestHeader("X-CSRFToken", csrf_token);
                }
        },
        dataType : "json",
        type : "POST"}
    );
    promise_details.then(notify_batch_success, fail_status_specific_to_batch);
}

Um dem Benutzer mitzuteilen, dass ein Stapel fehlgeschlagen ist, muss ich diese Informationen in den Fehlerbehandler schreiben, da er nur eine Antwort vom Server erhält.

Ich habe die Informationen immer noch nur zur Codierungszeit zur Verfügung - in meinem Fall habe ich eine Reihe möglicher Stapel, aber ich weiß nicht, welche von ihnen fehlgeschlagen ist. Analyse der Serverantwort bezüglich der fehlgeschlagenen URL.

function fail_status_specific_to_batch(d){
    console.log("bad batch run, dude");
    console.log("response.status:" + d.status);
}

Machen wir das. Konsolenausgabe ist:

konsole:

bad batch run, dude utility.js (line 109) response.status:404

Lassen Sie uns nun ein wenig ändern und einen wiederverwendbaren generischen Fehlerbehandler verwenden, der aber zur Laufzeit curried ist, und zwar sowohl mit dem Aufrufkontext der zur Zeitpunkt der Codezeit bekannten Zeit als auch den Laufzeitinformationen, die von event verfügbar sind .

    ... rest is as before...
    var target = $(e.target).text();
    var context = {"user_msg": "bad batch run, dude.  you were calling :" + target};
    var contexted_fail_notification = curry(generic_fail, context); 

    promise_details.then(notify_batch_success, contexted_fail_notification);
}

function generic_fail(context, d){
    console.log(context);
    console.log("response.status:" + d.status);
}

function curry(fn) {
     var slice = Array.prototype.slice,
        stored_args = slice.call(arguments, 1);
     return function () {
        var new_args = slice.call(arguments),
              args = stored_args.concat(new_args);
        return fn.apply(null, args);
     };
}

konsole:

Object { user_msg="bad batch run, dude. you were calling :Run ACL now"} utility.js (line 117) response.status:404 utility.js (line 118)

Allgemein betrachtet scheint Currying angesichts der weit verbreiteten Verwendung von Rückrufen in JS ein durchaus nützliches Werkzeug zu sein.

https://javascriptweblog.wordpress.com/2010/04/05/curry-cooking-up-tastier-functions/http://www.drdobbs.com/open-source/currying -und-Teilfunktionen-in-javasc/231001821? pgno = 2

0
JL Peyret

Ich habe gerade ein jPaq-Beispiel geschrieben, das einige coole Anwendungen der Curry-Funktion zeigt. Überprüfen Sie es hier: Stringfunktionen auflernen

0
Chris West

Ich bin damit einverstanden, dass Sie gelegentlich den Ball ins Rollen bringen möchten, indem Sie eine Pseudo-Funktion erstellen, die immer den Wert des ersten Arguments enthält. Glücklicherweise stieß ich auf eine brandneue JavaScript-Bibliothek namens jPaq (h ttp: //jpaq.org/ ) das diese Funktionalität bietet. Das Beste an der Bibliothek ist die Tatsache, dass Sie Ihr eigenes Build herunterladen können, das nur den Code enthält, den Sie benötigen.

0