Heim  >  Artikel  >  Web-Frontend  >  So implementieren Sie die exquisite automatische Currying-Funktion in JS

So implementieren Sie die exquisite automatische Currying-Funktion in JS

小云云
小云云Original
2017-12-13 09:27:541213Durchsuche

Dieser Artikel gibt Ihnen eine detaillierte Analyse der exquisiten automatischen Currying-Methode in JS und analysiert den Prozess und die Prinzipien anhand von Codebeispielen. Ich hoffe, er kann Ihnen helfen.

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 einige Verarbeitungen für Listenelemente durchführt, zum Beispiel Lassen Sie jedes Element in der Liste Eins hinzufügen, dann ist es leicht, sich Folgendes auszudenken:

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 kann eine Teilkonfiguration erhalten. Eine gute Funktion ist in Ordnung, zum Beispiel:

// plus返回部分配置好的函数
const plus1 = plus(1);
const plus2 = plus(2);
plus1(5); // => 6
plus2(7); // => 9

Übergeben Sie eine solche Funktion in die Karte:

const list = [0, 1, 2, 3];
list.map(plus1); // => [1, 2, 3, 4]
list.map(plus2); // => [2, 3, 4, 5]

Ist es 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 Curry zum Einsatz:

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;

OK Wie Sie sehen können, ist die 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, also zum Beispiel: const plus2 = plus(2). äquivalent zur Funktion 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

Diese Schreibweise sieht 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, 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 Ideen zu klären.

Anforderungen Analyse

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 der Parameter die Zahl ist 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 gleich der Anzahl ist Parameter von 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 Wenn in einer Parameterliste argsList keine Parameter übergeben werden, wird fn.apply(null, argsList) aufgerufen, um die ursprüngliche Funktion auszuführen _c(restNum, argsList) akzeptiert die Funktion zwei Parameter, einer ist die Anzahl der verbleibenden Parameter restNum und der andere ist die Liste der erhaltenen Parameter argsList; wurde übergeben. Wenn restNum Null ist, ist es Zeit zu übergeben. fn.apply(null, argsList) führt die ursprüngliche Funktion aus und gibt das Ergebnis zurück, wenn noch Parameter übergeben werden müssen, d. h. wenn restNum nicht Null ist , eine Einzelparameterfunktion

function(x) {
 return _c(restNum - 1, argsList.concat(x));
}
Dies bildet eine Schwanzrekursion, nachdem die Funktion einen Parameter akzeptiert hat. Die Anzahl der verbleibenden Parameter restNum wird um eins reduziert, und der neue Parameter x wird zu argsList hinzugefügt und an _c übergeben. Das Ergebnis ist, dass die Einzelparameterfunktion für den Empfang verantwortlich ist Wenn genügend Parameter vorhanden sind, wird die ursprüngliche Funktion aufgerufen und zurückgegeben:

klar?

Die ES6-Schreibmethode sieht aufgrund der Verwendung von Syntaxzucker wie Array-Destrukturierung und Pfeilfunktionen viel einfacher aus~

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, []); // 递归开始
}

Vergleich mit anderen Methoden

Es gibt eine weitere häufig verwendete Methode:
// ES6
const curry = fn => {
 const _c = (restNum, argsList) => restNum === 0 ?
  fn(...argsList) : x => _c(restNum - 1, [...argsList, x]);

 return _c(fn.length, []);
}


Verglichen Bei der zuvor in diesem Artikel erwähnten Methode wurde festgestellt, dass diese Methode zwei Probleme aufweist:

Abhängigkeit ES6-Destrukturierung (...args1 und...args2 in Funktionsparametern);

性能稍差一点。

性能问题

做个测试:

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

差的这一点猜测是闭包的原因。由于闭包的访问比较耗性能,而这种方式形成了两个闭包:fn 和 len,前面提到的方法只形成了 fn 一个闭包,所以造成了这一微小的差距。

相关推荐:

详解JavaScript函数柯里化

js柯里化的实例详解

javascript中有趣的反柯里化深入分析_基础知识

Das obige ist der detaillierte Inhalt vonSo implementieren Sie die exquisite automatische Currying-Funktion in JS. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn