Ich werde Ihnen eine detaillierte Analyse der hochentwickelten automatischen Currying-Methode in JS geben und den Prozess und die Prinzipien anhand von Codebeispielen analysieren. Bitte beziehen Sie sich darauf als Referenz.
Der folgende Inhalt analysiert die ausgefeilte automatische Currying-Implementierungsmethode in JS anhand von Codeerklärungen und Beispielen und analysiert die grundlegende Verwendung und das Wissen der Currying-Funktionen.
Was ist Curry?
In der Informatik besteht Currying darin, eine Funktion, die mehrere Parameter akzeptiert, in eine Funktion umzuwandeln, die einen einzelnen Parameter (den ersten Parameter der ursprünglichen Funktion) akzeptiert und die restlichen Parameter zurückgibt eine neue Funktion, die Argumente entgegennimmt und ein Ergebnis zurückgibt. Diese Technik wurde von Christopher Strachey nach dem Logiker Haskell Curry benannt, obwohl sie von Moses Schönfinkel und Gottlob Frege erfunden wurde.
Die Theorie scheint überwältigend? Egal, schauen wir uns zuerst den Code an:
Curriization-Anwendung
Angenommen, wir müssen eine Funktion implementieren, die ausgeführt wird einige Verarbeitungen für Listenelemente. Wenn Sie beispielsweise zu jedem Element in der Liste eines hinzufügen, denken Sie leicht:
const list = [0, 1, 2, 3]; list.map(elem => elem + 1);
Sehr einfach, oder? Was ist, wenn wir zwei weitere hinzufügen möchten?
const list = [0, 1, 2, 3]; list.map(elem => elem + 1); list.map(elem => elem + 2);
Es scheint etwas ineffizient zu sein. Kann die Verarbeitungsfunktion gekapselt werden?
Aber die Callback-Funktion von Map akzeptiert nur das aktuelle Element elem als Parameter. Es scheint, dass es keine Möglichkeit gibt, es zu kapseln ...
Sie denken vielleicht: Wenn Sie ein bekommen können teilweise konfigurierte Funktion Ganz gut, zum Beispiel:
// plus返回部分配置好的函数 const plus1 = plus(1); const plus2 = plus(2); plus1(5); // => 6 plus2(7); // => 9
Übergeben Sie eine Funktion wie diese in die Karte:
const list = [0, 1, 2, 3]; list.map(plus1); // => [1, 2, 3, 4] list.map(plus2); // => [2, 3, 4, 5]
Ist das nicht großartig? Auf diese Weise benötigen Sie, egal wie viel Sie hinzufügen, nur list.map(plus(x)), das die Kapselung perfekt implementiert und die Lesbarkeit erheblich verbessert!
Aber hier stellt sich die Frage: Wie implementiert man eine solche Plus-Funktion?
Hier kommt das Curryen zum Einsatz:
Zu sehen ist die Curry-Funktion
// 原始的加法函数 function origPlus(a, b) { return a + b; } // 柯里化后的plus函数 function plus(a) { return function(b) { return a + b; } } // ES6写法 const plus = a => b => a + b;
, die Curry-Plus-Funktion Akzeptiert zuerst einen Parameter a und gibt dann eine Funktion zurück, die einen Parameter b akzeptiert. Aufgrund des Abschlusses kann die zurückgegebene Funktion auf den Parameter a der übergeordneten Funktion zugreifen, sodass beispielsweise const plus2 = plus (2) äquivalent zu function sein kann plus2(b) { return 2 + b }, wodurch eine Teilkonfiguration erreicht wird.
Laienhaft ausgedrückt ist Currying ein Prozess der teilweisen Konfiguration einer Funktion mit mehreren Parametern, wobei jeder Schritt eine teilweise konfigurierte Funktion zurückgibt, die einen einzelnen Parameter akzeptiert. In einigen extremen Fällen müssen Sie eine Funktion möglicherweise mehrmals teilweise konfigurieren, z. B. durch mehrere Hinzufügungen:
multiPlus(1)(2)(3); // => 6
Sieht diese Schreibweise nicht seltsam aus? Aber wenn Sie in das große Loch der funktionalen JS-Programmierung geraten, wird dies die Norm sein.
Exquisite Implementierung des automatischen Curryings in JS
Currying ist ein sehr wichtiger Teil der funktionalen Programmierung (z. B. Haskell). ) Curry-Funktionen standardmäßig automatisch. Da JS dies jedoch nicht tut, müssen wir die automatische Currying-Funktion selbst implementieren.
Geben Sie zunächst den Code ein:
// ES5 function curry(fn) { function _c(restNum, argsList) { return restNum === 0 ? fn.apply(null, argsList) : function(x) { return _c(restNum - 1, argsList.concat(x)); }; } return _c(fn.length, []); } // ES6 const curry = fn => { const _c = (restNum, argsList) => restNum === 0 ? fn(...argsList) : x => _c(restNum - 1, [...argsList, x]); return _c(fn.length, []); } /***************** 使用 *********************/ var plus = curry(function(a, b) { return a + b; }); // ES6 const plus = curry((a, b) => a + b); plus(2)(4); // => 6
Auf diese Weise wird das automatische Currying erreicht!
Wenn Sie verstehen können, was passiert ist, dann herzlichen Glückwunsch! Der Chef, den dich alle nennen, bist du! , hinterlassen Sie ein „Gefällt mir“ und starten Sie Ihre funktionale Karriere (witzig
Wenn Sie nicht verstehen, was los ist, machen Sie sich keine Sorgen, ich helfe Ihnen jetzt, Ihre Gedanken zu ordnen.
Anforderungsanalyse
Wir benötigen eine Curry-Funktion, die eine zu currynde Funktion als Parameter akzeptiert, eine Funktion zum Empfangen eines Parameters zurückgibt und die empfangenen Parameter in eine Liste einfügt, wenn die Zahl Anzahl der Parameter ist ausreichend, führen Sie die ursprüngliche Funktion aus und geben Sie das Ergebnis zurück
Implementierung
Ein einfacher Gedanke kann sagen, dass die Anzahl der Schritte der Curry-Teilkonfigurationsfunktion beträgt gleich fn. Das heißt, die Plusfunktion mit zwei Parametern muss teilweise in zwei Schritten konfiguriert werden. Die Anzahl der Parameter der Funktion kann über fn.length ermittelt werden Geben Sie die Parameter einmal ein. Wenn keine Parameter übergeben werden müssen, rufen Sie fn.apply(null, argsList) auf, um dies zu erreichen (restNum, argsList), die Funktion akzeptiert zwei Parameter, einer ist die Anzahl der verbleibenden Parameter restNum und der andere ist die Liste der erhaltenen Parameter argsList. Die Funktion von _c besteht darin, festzustellen, ob Parameter vorhanden sind, die nicht übergeben wurden Wenn restNum den Wert Null hat, ist es an der Zeit, die ursprüngliche Funktion über fn.apply(null, argsList) auszuführen und das Ergebnis zurückzugeben, wenn noch Parameter übergeben werden müssen, das heißt, wenn restNum nicht Null ist , eine einzelne Parameterfunktion
function(x) { return _c(restNum - 1, argsList.concat(x)); }
Hier wird eine Schwanzrekursion gebildet, die Anzahl der verbleibenden Parameter wird um eins reduziert Parameter x wird zu argsList hinzugefügt und zum rekursiven Aufruf an _c übergeben. Wenn die Anzahl der Parameter nicht ausreicht, wird die ursprüngliche Funktion aufgerufen und zurückgegeben 🎜>
Jetzt sehen wir:function curry(fn) { function _c(restNum, argsList) { return restNum === 0 ? fn.apply(null, argsList) : function(x) { return _c(restNum - 1, argsList.concat(x)); }; } return _c(fn.length, []); // 递归开始 }
// ES6 const curry = fn => { const _c = (restNum, argsList) => restNum === 0 ? fn(...argsList) : x => _c(restNum - 1, [...argsList, x]); return _c(fn.length, []); }
function curry(fn) { const len = fn.length; return function judge(...args1) { return args1.length >= len ? fn(...args1): function(...args2) { return judge(...[...args1, ...args2]); } } } // 使用箭头函数 const curry = fn => { const len = fn.length; const judge = (...args1) => args1.length >= len ? fn(...args1) : (...args2) => judge(...[...args1, ...args2]); return judge; }
与本篇文章先前提到的方法对比的话,发现这种方法有两个问题:
依赖ES6的解构(函数参数中的 ...args1 与 ...args2);
性能稍差一点。
性能问题
做个测试:
console.time("curry"); const plus = curry((a, b, c, d, e) => a + b + c + d + e); plus(1)(2)(3)(4)(5); console.timeEnd("curry");
在我的电脑(Manjaro Linux,Intel Xeon E5 2665,32GB DDR3 四通道1333Mhz,Node.js 9.2.0)上:
本篇提到的方法耗时约 0.325ms
其他方法的耗时约 0.345ms
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
Das obige ist der detaillierte Inhalt vonSo implementieren Sie automatisches Currying in JS. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!