Secrets de la cuisine JavaScript: épices

Jetez un œil aux extraits de code suivants qui résolvent le même problème et réfléchissez à celui que vous préférez.
Voici le premier:Voici le deuxième:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(int => isEven(int)) .filter(int => isBiggerThan(3, int)) .map(int => int + 1) .map(int => toChar(int)) .filter(char => !isVowel(char)) .join('') // 'fhjl' 
 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(isEven) .filter(isBiggerThan(3)) .map(plus(1)) .map(toChar) .filter(not(isVowel)) .join('') // 'fhjl' 
«Je parie que la deuxième option est bien meilleure lisibilité que la première», explique l'auteur du document, dont nous publions aujourd'hui la traduction. Selon lui, tout est dans les arguments des méthodes filter() et map() .



Aujourd'hui, nous allons parler de la façon de recycler le code similaire au premier exemple afin qu'il ressemble au code du second. L'auteur de l'article promet qu'après avoir compris comment cela fonctionne, vous vous relierez à vos programmes d'une manière nouvelle et vous ne pourrez pas ignorer ce qui semblait tout à fait normal et ne nécessitait pas d'amélioration.

Fonction simple


Considérons une simple fonction sum() qui ajoute les nombres qui lui sont passés:

 const sum = (a, b) => a + b sum(1, 2) // 3 

Nous le csum() donnant à la nouvelle fonction le nom csum() :

 const csum = a => b => a + b csum(1)(2) // 3 

Sa nouvelle version fonctionne exactement de la même manière que l'original, la seule différence est la façon dont cette nouvelle fonction est appelée. A savoir, la fonction sum() prend deux paramètres à la fois, et csum() prend les mêmes paramètres un par un. En fait, lors de l'appel à csum() , deux fonctions sont appelées. En particulier, considérez la situation lorsque csum() appelée, en lui passant le numéro 1 et rien d'autre:

 csum(1) // b => 1 + b 

Un tel appel à csum() conduit au fait qu'il renvoie une fonction qui peut accepter le deuxième argument numérique passé à csum() lors de son appel habituel, et renvoie le résultat de l'ajout d'un à cet argument. Appelez cette fonction plusOne() :

 const plusOne = csum(1) plusOne(2) // 3 

Travailler avec des tableaux


En JavaScript, vous pouvez travailler avec des tableaux en utilisant une variété de méthodes spéciales. Disons que la méthode map() est utilisée pour appliquer la fonction qui lui est passée à chaque élément du tableau.

Par exemple, pour augmenter de 1 chaque élément d'un tableau entier (plus précisément, pour former un nouveau tableau contenant des éléments du tableau d'origine augmentés de 1), vous pouvez utiliser la construction suivante:

 [1, 2, 3].map(x => x + 1) // [2, 3, 4] 

En d'autres termes, ce qui se passe peut être décrit comme suit: la fonction x => x + 1 prend un entier et retourne le nombre qui le suit dans une série d'entiers. En utilisant la fonction plusOne() décrite ci-dessus, cet exemple peut être réécrit comme suit:

 [1, 2, 3].map(x => plusOne(x)) // [2, 3, 4] 

Ici, il vaut la peine de ralentir et de réfléchir à ce qui se passe. Si vous faites cela, vous pouvez voir que dans le cas considéré, les constructions x => plusOne(x) et plusOne (notez que dans cette situation il n'y a pas de crochets après le nom de la fonction) sont équivalentes. Afin de mieux comprendre cela, considérez la fonction otherPlusOne() :

 const otherPlusOne = x => plusOne(x) otherPlusOne(1) // 2 

Le résultat de cette fonction sera le même que celui obtenu par un simple appel à plusOne() déjà connu de nous:

 plusOne(1) // 2 

Pour la même raison, nous pouvons parler de l'équivalence des deux constructions suivantes. Voici le premier que nous avons déjà vu:

 [1, 2, 3].map(x => plusOne(x)) // [2, 3, 4] 

Voici le deuxième:

 [1, 2, 3].map(plusOne) // [2, 3, 4] 

De plus, rappelez-vous comment la fonction plusOne() été créée:

 const plusOne = csum(1) 

Cela nous permet de réécrire notre construction avec map() comme suit:

 [1, 2, 3].map(csum(1)) // [2, 3, 4] 

Maintenant, en utilisant la même technique, isBiggerThan() . Si vous le souhaitez, essayez de le faire vous-même, puis lisez la suite. Cela éliminera l'utilisation de constructions inutiles lors de l'utilisation de la méthode filter() . Tout d'abord, apportons le code à ce formulaire:

 const isBiggerThan = (threshold, int) => int > threshold [1, 2, 3, 4].filter(int => isBiggerThan(3, int)) 

Ensuite, en nous débarrassant de tout ce qui est superflu, nous obtenons le code que vous avez déjà vu au tout début de ce matériel:

 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(isEven) .filter(isBiggerThan(3)) .map(plus(1)) .map(toChar) .filter(not(isVowel)) .join('') // 'fhjl' 

Nous considérons maintenant deux règles simples qui vous permettent d'écrire du code dans le style discuté ici.

Règle numéro 1


Les deux constructions suivantes sont équivalentes:

 […].map(x => fnc(x)) […].map(fnc) 

Règle numéro 2


Les rappels peuvent toujours être réécrits pour réduire le nombre d'arguments utilisés pour l'appeler:

 const fnc = (x, y, z) => … […].map(x => fnc(x, y, z)) const fnc = (y, z) => x => … […].map(fnc(y, z)) 

Si vous avez vous-même écrit la fonction isBiggerThan() , vous avez probablement déjà eu recours à une telle transformation. Supposons que nous ayons besoin de nombres supérieurs à 3 pour passer à travers un filtre.

 const isBiggerThan = (threshold, int) => int > threshold […].filter(int => isBiggerThan(3, int)) 

Maintenant, nous isBiggerThan() fonction isBiggerThan() afin qu'elle puisse être utilisée dans la méthode filter() et ne pas utiliser la construction int=> :

 const isBiggerThan = threshold => int => int > threshold […].map(isBiggerThan(3)) 

Exercice


Supposons que nous ayons le fragment de code suivant:

 const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 keepGreatestChar('b', 'f') // 'f' //   'f'   'b' 

Maintenant, basé sur la fonction keepGreatestChar() , créez la fonction keepGreatestCharBetweenBAnd() . Nous avons besoin que, en l'appelant, nous ne puissions lui passer qu'un seul argument, alors qu'il comparera le caractère qui lui est passé avec le caractère b . Cette fonction peut ressembler à ceci:

 const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 const keepGreatestCharBetweenBAnd = char => keepGreatestChar('b', char) keepGreatestCharBetweenBAnd('a') // 'b' //   'b'   'a' 

Maintenant, écrivez la greatestCharInArray() fonctionCharInArray greatestCharInArray() qui, en utilisant la fonction keepGreatestChar() dans la méthode de tableau keepGreatestChar() , vous permet de rechercher le "plus grand" caractère et n'a pas besoin d'arguments. Commençons par ce code:

 const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 const greatestCharInArray = array => array.reduce((acc, char) => acc > char ? acc : char, 'a') greatestCharInArray(['a', 'b', 'c', 'd']) // 'd' 

Pour résoudre ce problème, implémentez la fonction creduce() , qui peut être utilisée dans la greatestCharInArray() fonctionCharInArray greatestCharInArray() , qui permet, dans l'application pratique de cette fonction, de ne rien lui passer sauf le tableau dans lequel trouver le caractère avec le plus grand code.

La fonction creduce() doit être suffisamment universelle pour pouvoir être utilisée pour résoudre tout problème nécessitant l'utilisation des capacités de la méthode standard de tableau creduce() . En d'autres termes, la fonction doit prendre un rappel, une valeur initiale et un tableau pour fonctionner. Par conséquent, vous devriez obtenir une fonction avec laquelle le fragment de code suivant fonctionnera:

 const greatestCharInArray = creduce(keepGreatestChar, 'a') greatestCharInArray(['a', 'b', 'c', 'd']) // 'd' 

Résumé


Peut-être que vous vous demandez maintenant pourquoi les méthodes, traitées conformément à la méthodologie présentée ici, ont des noms commençant par le caractère c . Le caractère c est un acronyme pour curried, et nous avons expliqué comment les fonctions curried aident à améliorer la lisibilité du code. Il convient de noter qu'ici nous ne nous sommes pas efforcés de respecter strictement les principes de la programmation fonctionnelle, mais nous pensons que l'application pratique de ce qui a été discuté ici nous permet d'améliorer le code. Si le sujet du currying en JavaScript est intéressant pour vous - il est recommandé de lire le 4ème chapitre de ce livre sur la programmation fonctionnelle, et, en général, depuis que vous êtes arrivé à ce point - lisez tout ce livre. De plus, si vous êtes nouveau dans la programmation fonctionnelle, consultez ce matériel de démarrage.

Chers lecteurs! Utilisez-vous le curry de fonction dans le développement JavaScript?

Source: https://habr.com/ru/post/fr415059/


All Articles