Maison  >  Article  >  interface Web  >  Comment implémenter le curry automatique dans JS

Comment implémenter le curry automatique dans JS

亚连
亚连original
2018-06-21 13:59:491404parcourir

Je vais vous donner une analyse détaillée de la méthode exquise de curry automatique dans JS et analyser le processus et les principes à travers des exemples de code. Veuillez vous y référer pour référence.

Le contenu suivant analyse la méthode sophistiquée d'implémentation du curry automatique dans JS à travers une explication du code et des exemples, et analyse l'utilisation et la connaissance de base des fonctions de curry.

Qu'est-ce que le curry ?

En informatique, le curry consiste à transformer une fonction qui accepte plusieurs paramètres en une fonction qui accepte un seul paramètre (le premier paramètre de la fonction d'origine), et renvoie les paramètres restants. La technique de. une nouvelle fonction qui prend des arguments et renvoie un résultat. Cette technique a été nommée par Christopher Strachey en l'honneur du logicien Haskell Curry, bien qu'elle ait été inventée par Moses Schnfinkel et Gottlob Frege.

La théorie semble écrasante ? Peu importe, jetons d'abord un œil au code :

Application de curriisation

Supposons que nous devions implémenter une fonction qui exécute quelques traitements sur les éléments de la liste, Par exemple, si vous en ajoutez un à chaque élément de la liste, il est facile de penser :

const list = [0, 1, 2, 3];
list.map(elem => elem + 1);

C'est très simple, non ? Et si nous voulons en ajouter 2 de plus ?

const list = [0, 1, 2, 3];
list.map(elem => elem + 1);
list.map(elem => elem + 2);

Cela semble un peu inefficace. La fonction de traitement peut-elle être encapsulée ?

Mais la fonction de rappel de map n'accepte que l'élément actuel elem comme paramètre. Il semble qu'il n'y ait aucun moyen de l'encapsuler...

Vous pensez peut-être : si vous pouvez obtenir un. fonction partiellement configurée Très bien, par exemple :

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

Passez une fonction comme celle-ci dans la carte :

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

N'est-ce pas génial ? De cette façon, peu importe ce que vous ajoutez, vous n'avez besoin que de list.map(plus(x)), qui implémente parfaitement l'encapsulation et améliore considérablement la lisibilité !

Mais voici la question : Comment mettre en œuvre une telle fonction plus ?

C'est ici que le curry s'avère utile :

Fonction curry

// 原始的加法函数
function origPlus(a, b) {
 return a + b;
}
// 柯里化后的plus函数
function plus(a) {
 return function(b) {
  return a + b;
 }
}
// ES6写法
const plus = a => b => a + b;

Oui Comme vous pouvez le voir, le La fonction curry plus accepte d'abord un paramètre a, puis renvoie une fonction qui accepte un paramètre b. En raison de la fermeture, la fonction renvoyée peut accéder au paramètre a de la fonction parent, par exemple : const plus2 = plus(2) peut être. équivalent à function plus2(b) { return 2 + b }, réalisant ainsi une configuration partielle.

En termes simples, le currying est un processus de configuration partielle d'une fonction multi-paramètres, chaque étape renvoyant une fonction partiellement configurée qui accepte un seul paramètre. Dans certains cas extrêmes, vous devrez peut-être configurer partiellement une fonction à plusieurs reprises, comme par exemple plusieurs ajouts :

multiPlus(1)(2)(3); // => 6

Cette façon d'écrire semble étrange, non ? Mais si vous tombez dans le grand gouffre de la programmation fonctionnelle JS, ce sera la norme.

Implémentation exquise du curry automatique dans JS

Le curry est une partie très importante de la programmation fonctionnelle de nombreux langages fonctionnels (par exemple Haskell. ) curry automatiquement les fonctions par défaut. Cependant, JS ne le fait pas, nous devons donc implémenter nous-mêmes la fonction de curry automatique.

Entrez d'abord le code :

// 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

De cette façon, le curryage automatique est réalisé !

Si vous pouvez comprendre ce qui s'est passé, alors félicitations ! Le patron que tout le monde vous appelle, c’est vous ! , laissez un like et commencez votre carrière fonctionnelle (drôle

Si vous ne comprenez pas ce qui se passe, ne vous inquiétez pas, je vais vous aider à faire le tri dans vos pensées maintenant.

Analyse des exigences

Nous avons besoin d'une fonction curry, qui accepte une fonction à curry comme paramètre, renvoie une fonction pour recevoir un paramètre et met les paramètres reçus dans une liste, lorsque le nombre de paramètres est suffisant, exécutez la fonction d'origine et renvoyez le résultat

Implémentation

Une simple pensée peut dire que le nombre d'étapes de la fonction de configuration partielle au curry est. égal à fn. Le nombre de paramètres, c'est-à-dire que la fonction plus avec deux paramètres doit être partiellement configurée en deux étapes. Le nombre de paramètres de la fonction peut être obtenu via fn.length L'idée générale est de passer. les paramètres une fois. Placez le paramètre dans une liste de paramètres argsList. S'il n'y a aucun paramètre à transmettre, appelez fn.apply(null, argsList) pour exécuter la fonction d'origine. Pour y parvenir, nous avons besoin d'une fonction de jugement interne. (restNum, argsList), la fonction accepte deux paramètres, l'un est le nombre de paramètres restants restNum et l'autre est la liste des paramètres obtenus argsList ; la fonction de _c est de déterminer s'il existe des paramètres qui n'ont pas été transmis ; . Lorsque restNum est à zéro, il est temps d'exécuter la fonction d'origine via fn.apply(null, argsList) et de renvoyer le résultat. S'il y a encore des paramètres à passer, c'est-à-dire lorsque restNum n'est pas nul. La fonction à paramètre unique

pour continuer à recevoir des paramètres. Une récursion de queue est formée ici. Une fois que la fonction a accepté un paramètre, le nombre de paramètres restants restNum est réduit de un et le nombre de paramètres restants est réduit de un. le nouveau paramètre x est ajouté à argsList et transmis à _c pour un appel récursif, lorsque le nombre de paramètres est insuffisant, la fonction à paramètre unique responsable de la réception des nouveaux paramètres est renvoyée. Lorsqu'il y a suffisamment de paramètres, la fonction d'origine est appelée et renvoyée. .

function(x) {
 return _c(restNum - 1, argsList.concat(x));
}
Regardons cela maintenant :

Est-ce que cela ne commence pas à devenir clair

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, []); // 递归开始
}
La méthode d'écriture ES6 semble beaucoup plus simple en raison de son utilisation ? de sucre syntaxique comme la déstructuration de tableaux et les fonctions de flèche, mais les idées sont les mêmes ~

et Comparaison d'autres méthodes

// ES6
const curry = fn => {
 const _c = (restNum, argsList) => restNum === 0 ?
  fn(...argsList) : x => _c(restNum - 1, [...argsList, x]);

 return _c(fn.length, []);
}
Il existe une autre méthode couramment utilisée :

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

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在Vue中有关SPA首屏加载优化(详细教程)

在Angular中有关管道操作符(|)用法

VS Code编辑器中关于修改选中文字或代码颜色操作

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn