Maison  >  Article  >  interface Web  >  Comment implémenter la fonction de curry automatique exquise dans JS

Comment implémenter la fonction de curry automatique exquise dans JS

小云云
小云云original
2017-12-13 09:27:541213parcourir

Cet article vous donne une analyse détaillée de la méthode exquise de curry automatique dans JS et analyse le processus et les principes à travers des exemples de code. Veuillez vous y référer et l'étudier, j'espère qu'il pourra vous aider.

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 accablante ? Peu importe, jetons d'abord un coup d'œil au code :

Application de curriisation

Supposons que nous devions implémenter une fonction qui effectue un traitement sur les éléments de la liste, par exemple, laissez chaque élément de la liste en ajouter un, alors c'est facile d'y penser :

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

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 peut obtenir une configuration partielle Une bonne fonction est très bien, par exemple :

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

Passez une telle fonction 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;

OK 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 Bar semble étrange. ? 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 manière, 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 ! , laisse un like et commence ta carrière fonctionnelle (drôle

Si tu ne comprends pas ce qui se passe, ne t'inquiète pas, je vais commencer à t'aider à faire le tri dans tes idé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 place les paramètres reçus dans une liste lorsque le paramètre Quand. le nombre 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 au. nombre de paramètres de fn, 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 mettre. le paramètre à chaque fois qu'un paramètre est passé. Dans une liste de paramètres argsList, s'il n'y a aucun paramètre à passer, alors fn.apply(null, argsList) est appelé pour exécuter la fonction d'origine. Pour y parvenir, nous avons besoin d'un paramètre interne. fonction de jugement _c(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 ; n'ont pas été transmis. Lorsque restNum est nul, il est temps de passer fn.apply(null, argsList) exécute la fonction d'origine et renvoie le résultat S'il y a encore des paramètres à transmettre, c'est-à-dire lorsque restNum est transmis. pas zéro, une fonction à paramètre unique

function(x) {
 return _c(restNum - 1, argsList.concat(x));
}
pour continuer à recevoir des paramètres. Cela forme une récursion de queue une fois que la fonction a accepté un. paramètre, le nombre de paramètres restants restNum est réduit de un et le nouveau paramètre x est ajouté à argsList et passé à _c. Le résultat est que lorsque le nombre de paramètres est insuffisant, la fonction à paramètre unique est responsable. pour recevoir de nouveaux paramètres est renvoyé Lorsqu'il y a suffisamment de paramètres, la fonction d'origine est appelée et renvoyée. Regardez maintenant :

Est-ce que ça démarre. pour devenir clair ?

La méthode d'écriture ES6 semble beaucoup plus simple en raison de l'utilisation de sucres de syntaxe tels que la déstructuration de tableaux et les fonctions fléchées~
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, []); // 递归开始
}

Comparaison avec d'autres méthodes

Il existe une autre méthode couramment utilisée :
// ES6
const curry = fn => {
 const _c = (restNum, argsList) => restNum === 0 ?
  fn(...argsList) : x => _c(restNum - 1, [...argsList, x]);

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


Par rapport à la méthode évoquée précédemment dans cet article, on constate que cette méthode présente deux problèmes :

Déstructuration des dépendances ES6 (...args1 et...args2 dans les paramètres de la fonction

性能稍差一点。

性能问题

做个测试:

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中有趣的反柯里化深入分析_基础知识

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