Maison >interface Web >js tutoriel >Programmation fonctionnelle pratique avec Ramda.js
Cet article a été examiné par Yaphi Berhanu, Vildan Softic, Jani Hartikainen et Dan Prince. Merci à tous les évaluateurs de pairs SitePoint pour avoir obtenu le contenu de sitepoint à son meilleur!
L'un des charmes de JavaScript est ses caractéristiques de programmation fonctionnelle. Dès le début, les fonctions étaient des citoyens de première classe dans le monde JavaScript. Cela permet d'écrire un code élégant et expressif qui peut être facilement assemblé à bien des égards. Cependant, avoir la capacité d'effectuer une programmation fonctionnelle seule ne peut pas implémenter automatiquement la programmation fonctionnelle. Ramda.js est une bibliothèque très populaire (avec plus d'étoiles 4K sur GitHub) que nous pouvons utiliser pour nous aider à démarrer avec JavaScript pour la programmation fonctionnelle.
Points clés
R.map
, R.prop
et R.curry
, simplifiant le fonctionnement des structures de données et améliorant la lisibilité et la maintenabilité du code. débutant
Pour tirer le meilleur parti de Ramda.js, nous devons être familiers avec ses avantages en créant un petit projet Node.js. Nous pouvons simplement l'installer via le Node Package Manager (NPM).
<code class="language-bash">npm install ramda</code>
Habituellement, nous importerons simplement les fonctionnalités de la bibliothèque dans l'espace de noms R. De cette façon, tous les appels à la méthode Ramda auront le préfixe R.
<code class="language-javascript">var R = require('ramda');</code>
Bien sûr, rien ne peut nous empêcher d'utiliser Ramda.js dans le code frontal. Dans le navigateur, nous avons juste besoin d'inclure le chemin correct vers la copie de la bibliothèque. Cela peut être aussi simple que l'extrait de code HTML suivant.
<code class="language-html"></code>
Ramda.js n'utilise aucune fonctionnalité DOM ou Node.js. Il s'agit d'une bibliothèque / extension de langue et s'appuie sur les structures et les algorithmes déjà exposés par l'exécution JavaScript (comme standardisé dans ECMAScript 5). Prêt à mener des recherches approfondies? Jetons un coup d'œil à l'application pratique de certaines fonctions!
concept
Le concept le plus important de la programmation fonctionnelle est le concept de fonctions pures. Les fonctions pures sont idempotentes et ne changent aucun état. D'un point de vue mathématique, cela a du sens, car les fonctions comme le péché (x) semblent très naturelles et ne dépendent aucun état externe. En plus des fonctions pures, nous voulons également avoir des fonctions à paramètre unique. Ils sont les plus primitifs. Une fonction de paramètre zéro signifie généralement que l'état externe sera modifié, ce n'est donc pas une fonction pure. Mais dans des langages comme JavaScript, nous avons généralement des fonctions avec plusieurs paramètres.
La possibilité d'avoir des fonctions d'ordre supérieur (c'est-à-dire des fonctions qui peuvent prendre les fonctions en tant que fonctions d'entrée et émettent en tant que sortie) combinées avec des fermetures (capturer des variables locales) nous fournit un bon moyen: le curry. Le curry est un processus dans lequel une fonction avec plusieurs paramètres (en supposant n) est converti en une fonction avec un seul paramètre, qui renvoie une autre fonction avec un seul paramètre. Cela se poursuivra jusqu'à ce que tous les paramètres requis soient collectés. Supposons que nous voulons utiliser la fonction d'assistance Ramda.js pour écrire un seul wrapper de paramètres qui teste si ses paramètres sont des chaînes. Le code suivant fera le travail.
<code class="language-bash">npm install ramda</code>
Cela peut être fait plus facilement avec le curry. Étant donné que R.is fait partie de Ramda.js, si nous fournissons moins de paramètres que ceux requis par la fonction, la bibliothèque renverra automatiquement une fonction de curry:
<code class="language-javascript">var R = require('ramda');</code>
C'est plus expressif. Depuis que nous avons appelé R.is avec un seul argument, nous avons obtenu une fonction. Dans le deuxième appel (rappelez-vous, l'appel de fonction d'origine prend deux paramètres), nous obtenons le résultat. Mais que se passe-t-il si nous n'avons pas utilisé les fonctions d'assistance Ramda.js au début? Supposons que nous avons défini la fonction suivante quelque part dans le code:
<code class="language-html"></code>
Il s'agit d'un polynôme complet de second ordre. Il a quatre paramètres qui permettent toutes les valeurs possibles. Mais généralement, nous voulons simplement changer x pour un ensemble fixe de paramètres a, b et c. Voyons comment le convertir en utilisant Ramda.js:
<code class="language-javascript">function isString (test) { return R.is(String, test); } var result = isString('foo'); //=> true</code>
De même, nous pouvons simplement utiliser l'évaluation des paramètres pour créer un alias pour un sous-ensemble spécifique. Par exemple, l'équation X - 1 peut être obtenue par:
<code class="language-javascript">var isString = R.is(String); var result = isString('foo'); //=> true</code>
Dans le cas où le nombre de paramètres n'est pas donné par les paramètres de notre fonction, nous devons utiliser Curryn et spécifier explicitement le nombre de paramètres. Le curry est au cœur de Ramda.js, mais la bibliothèque ne semble pas si intéressante sans plus. Un autre concept important dans la programmation fonctionnelle est l'invariance.
Le moyen le plus simple d'empêcher une fonction de modifier son état est d'utiliser simplement une structure de données qui ne peut pas être modifiée. Pour les objets simples, nous avons besoin d'un accessoire en lecture seule, par exemple:
n'est pas autorisé. En plus de déclarer des propriétés en lecture seule, nous pouvons également les convertir en fonctions Getter:<code class="language-javascript">var quadratic = (a, b, c, x) => x * x * a + x * b + c; quadratic(1, 0, 0, 2); //=> 4 quadratic(1, 0, 0)(2); //=> TypeError: quadratic(..) is not a function</code>
C'est bien mieux maintenant, cependant, l'objet peut toujours être modifié. Cela signifie que quelqu'un peut ajouter une définition personnalisée de la fonction getx, par exemple:
<code class="language-javascript">var quadratic = R.curry((a, b, c, x) => x * x * a + x * b + c); quadratic(1, 0, 0, 2); //=> 4 quadratic(1, 0, 0)(2); //=> 4</code>
<code class="language-bash">npm install ramda</code>
La meilleure façon d'atteindre l'invariance est d'utiliser Object.Freeze. Combiné avec le mot clé const, nous pouvons introduire une variable invariante qui ne peut pas être modifiée.
<code class="language-javascript">var R = require('ramda');</code>
Un autre exemple implique des listes. L'ajout d'un élément à une liste immuable nécessite de copier la liste d'origine et d'ajouter un nouvel élément à la fin. Bien sûr, nous pouvons également utiliser la connaissance de l'invariance de l'objet d'origine pour optimiser la mise en œuvre. De cette façon, nous pouvons remplacer la copie par une simple référence. Essentiellement, cela pourrait se transformer en une liste liée. Nous devons réaliser que les tableaux JavaScript standard sont mutables et doivent donc être copiés pour garantir l'exactitude. Des méthodes telles que A SPEND () fonctionnent sur les tableaux JavaScript et renvoient ces tableaux. L'opération est idempotent; si nous appelons la fonction avec les mêmes arguments plusieurs fois, nous obtiendrons toujours le même résultat.
<code class="language-html"></code>
Il existe également une méthode de suppression qui renvoie le tableau donné mais ne contient pas l'entrée spécifiée. Il fonctionne comme suit:
<code class="language-javascript">function isString (test) { return R.is(String, test); } var result = isString('foo'); //=> true</code>
Puisqu'il a un nombre flexible de paramètres, nous avons besoin de la fonction Curryn mentionnée précédemment pour appliquer la curryisation. Il existe également des fonctions d'assistance générales utiles disponibles.
Méthodes pratiques
Le concept le plus important de toutes les fonctions d'assistance est que les paramètres sont ordonnés de faciliter le curry. Plus le paramètre est fréquemment modifié, moins il est probable que ce soit avant les autres paramètres.
Bien sûr, vous pouvez trouver des fonctions communes dans Ramda.js, telles que la somme et la gamme:
<code class="language-javascript">var isString = R.is(String); var result = isString('foo'); //=> true</code>
Pour les fonctions d'assistance Range (), nous pouvons créer un wrapper à l'aide de curry:
<code class="language-javascript">var quadratic = (a, b, c, x) => x * x * a + x * b + c; quadratic(1, 0, 0, 2); //=> 4 quadratic(1, 0, 0)(2); //=> TypeError: quadratic(..) is not a function</code>
Et si nous voulons l'envelopper avec un maximum fixe (exclusif)? Ramda.js répond à nos besoins en utilisant des paramètres spéciaux représentés par R .__:
<code class="language-javascript">var quadratic = R.curry((a, b, c, x) => x * x * a + x * b + c); quadratic(1, 0, 0, 2); //=> 4 quadratic(1, 0, 0)(2); //=> 4</code>
De plus, Ramda.js essaie d'utiliser une "meilleure" solution pour fournir des alternatives aux fonctions de noyau JavaScript telles que Array.prototype.map. La solution "meilleure" a un ordre de paramètres différent et le curry de la boîte. Pour la fonction de carte, comme indiqué ci-dessous:
<code class="language-javascript">var xOffset = quadratic(0, 1, -1); xOffset(0); //=> -1 xOffset(1); //=> 0</code>
Une autre utilité utile est la fonction d'hélice, qui essaie d'obtenir la valeur de la propriété spécifiée. Si la propriété donnée n'existe pas, un non-défini est retourné. Cela peut être ambigu si la valeur n'est en effet pas définie, mais dans la pratique, nous nous en soucions rarement.
<code class="language-javascript">var position = { x: 5, y: 9 }; position.x = 10; // works!</code>
Si la méthode précédemment introduite ne vous convainc pas que Ramda.js peut fournir quelque chose d'utile, la méthode suivante peut être plus intéressante. Cette fois, nous ne discuterons pas d'un exemple spécifique, mais examinerons plutôt les scénarios sélectionnés arbitraires. Supposons que nous ayons deux listes et que nous voulons les connecter. L'utilisation de la fonction zip est en fait très simple. Cependant, les résultats habituels (tableaux d'éléments, qui sont eux-mêmes des tableaux à double valeur) peuvent ne pas être le résultat que nous voulons. C'est là que la fonction Zipwith entre en jeu. Il mappe les valeurs à une seule valeur en utilisant des fonctions arbitraires.
<code class="language-javascript">var position = (function (x, y) { return { getX: () => { return x; }, getY: () => { return y; } }; })(5, 9); position.getX() = 10; // does not work!</code>
De même, nous pouvons introduire un produit DOT au vecteur:
<code class="language-bash">npm install ramda</code>
Nous compressons deux tableaux par multiplication (générant [1, 4, 9]) et passons le résultat à la fonction de somme. Dans tous les cas, l'utilisation d'objets énumérables est un sujet important. Il n'est pas surprenant que Ramda.js offre de nombreuses fonctions d'assistance utiles. Nous avons introduit R.MAP pour appliquer des fonctions à chaque élément. De même, certaines fonctions d'assistance réduisent le nombre d'éléments. Une seule valeur peut être générée par la fonction filtrante la plus générale (génère un autre tableau) ou par la fonction de réduction.
Il existe des fonctions d'assistance utiles pour le fonctionnement du tableau. Par exemple, en utilisant la chaîne, nous pouvons facilement fusionner les tableaux. Supposons que nous ayons une fonction PrimeFactorisation qui utilise les nombres comme entrée et donne un tableau avec des facteurs premiers comme sortie, nous pouvons combiner le résultat de l'application de la fonction avec un ensemble de nombres comme suit:
<code class="language-javascript">var R = require('ramda');</code>
Jusqu'à présent, tout s'est bien passé. La plus grande question est maintenant: quels sont les avantages de notre travail quotidien en utilisant ces concepts introduits par Ramda.js? Supposons que nous ayons l'extrait de code suivant (déjà très beau).
<code class="language-html"></code>
Comment utiliser Ramda.js pour le rendre plus lisible? Eh bien, la première ligne est assez bonne. La deuxième ligne est déjà très déroutante. Ce que nous voulons vraiment, c'est extraire la propriété des poteaux des paramètres fournis. Enfin, nous avons une troisième ligne légèrement déroutante. Ici, nous essayons d'itérer tous les articles (fournis par les paramètres). Encore une fois, son seul but est d'extraire des propriétés spécifiques. Et la solution suivante?
<code class="language-javascript">function isString (test) { return R.is(String, test); } var result = isString('foo'); //=> true</code>
C'est probablement la meilleure solution pour la lisibilité grâce à la programmation fonctionnelle compatible Ramda.js. Cependant, nous devons noter que la syntaxe "Fat Arrow" introduite dans ECMAScript 6 conduit également à un code très concis et facile à lire:
<code class="language-javascript">var isString = R.is(String); var result = isString('foo'); //=> true</code>
C'est presque aussi facile à lire sans aucune connaissance Ramda.js. De plus, nous réduisons le nombre d'abstractions - cela ne bénéficiera que les performances et la maintenabilité.
Lens
Enfin, nous devons également discuter des fonctions d'assistance d'objet utiles. Il convient de mentionner ici que la fonction de l'objectif est. Un objectif est un objet spécial qui peut être transmis avec un objet ou un tableau à certaines fonctions Ramda.js. Il permet à ces fonctions de récupérer ou de convertir des données à partir de propriétés ou d'index spécifiques d'un objet ou d'un tableau, respectivement. Supposons que nous ayons un objet avec deux clés x et y - tout comme l'exemple d'invariance donné au début de l'article. Au lieu d'envelopper l'objet dans un autre objet avec des méthodes Getter et Setter, nous pouvons créer un objectif pour "se concentrer" sur les propriétés d'intérêt. Pour créer une lentille qui accède à l'attribut x d'un objet, nous pouvons effectuer ce qui suit:
<code class="language-javascript">var quadratic = (a, b, c, x) => x * x * a + x * b + c; quadratic(1, 0, 0, 2); //=> 4 quadratic(1, 0, 0)(2); //=> TypeError: quadratic(..) is not a function</code>
Bien que PROP soit un Getter standard (cela a été introduit), Assoc est une fonction de setter (trois valeurs Syntaxe: clé, valeur, objet). Nous pouvons maintenant utiliser les fonctions de Ramda.js pour accéder aux propriétés définies par cet objectif.
<code class="language-bash">npm install ramda</code>
Notez que cette opération ne touche pas l'objet de position donné (que nous le congelons ou non). Il convient de noter que l'ensemble n'est qu'un cas particulier de Over, ce qui est similaire mais prend des fonctions au lieu de valeurs arbitraires. La fonction sera ensuite utilisée pour convertir la valeur. Par exemple, l'appel suivant multiplie la coordonnée x par 3:
<code class="language-javascript">var R = require('ramda');</code>
Ramda.js, Lodash ou autre?
Une question raisonnable est bien sûr pourquoi Ramda.js est choisi - pourquoi n'utilisons-nous pas Lodash ou quelque chose? Bien sûr, on pourrait soutenir que Ramda.js est mis à jour, donc ça doit être mieux, mais c'est loin de la vérité. La vérité est que Ramda.js est construit avec des principes fonctionnels à l'esprit - avec une nouvelle approche du placement et de la sélection des paramètres (pour les bibliothèques JavaScript). Par exemple, l'itérateur de liste dans Ramda.js ne passe que des éléments par défaut, pas des listes. D'un autre côté, la norme pour les autres bibliothèques (comme Lodash) est de passer des éléments et des index des fonctions de rappel. Cela semble être un léger problème, mais cela vous empêche d'utiliser des fonctions intégrées pratiques comme parseInt () (qui prend un deuxième argument facultatif), tout en utilisant Ramda.js, cela fonctionne bien. En fin de compte, ce que vous choisissez dépend des exigences spécifiques ou de l'expérience et / ou des connaissances de l'équipe, mais il y a certainement de bonnes raisons d'obtenir Ramda.js l'attention qu'elle mérite.
lecture complémentaire
Conclusion
La programmation fonctionnelle ne doit pas être considérée comme une panacée. Au lieu de cela, il doit être considéré comme un ajout naturel à notre boîte à outils existante, qui nous offre une plus grande composabilité, une plus grande flexibilité et une plus grande tolérance / rotabilité des défauts. Les bibliothèques JavaScript modernes ont essayé de profiter de certains concepts fonctionnels. Ramda.js est un outil puissant qui étend votre propre bibliothèque d'utilité fonctionnelle. Que pensez-vous de la programmation fonctionnelle? Dans quels aspects pensez-vous que cela fonctionne bien? S'il vous plaît laissez-moi savoir dans les commentaires!
FAQ (FAQ) sur la programmation fonctionnelle avec Ramda
Ramda est une bibliothèque fonctionnelle pratique pour les programmeurs JavaScript. Il est conçu pour le style de programmation fonctionnelle, ce qui facilite la création de pipelines fonctionnels et ne modifie jamais les données utilisateur. Le principal avantage de l'utilisation de Ramda est qu'il met l'accent sur l'invariance et les fonctions sans effet secondaire. Cela signifie que la fonction ne modifie pas ses données d'entrée, ce qui rend votre code plus facile à prévoir et à tester. Les fonctions de Ramda sont automatiquement curry, ce qui vous permet de créer facilement de nouvelles fonctions à partir d'anciennes fonctions sans vous perdre dans un tas de supports ou un enfer de rappel.
Contrairement à d'autres bibliothèques JavaScript, Ramda est conçu pour prendre en charge la programmation fonctionnelle et ne change pas de données. Il fournit un ensemble de fonctions pratiques qui curry par défaut, ce qui signifie qu'elles sont destinées à être utilisées ensemble pour créer de nouvelles fonctions. Ceci est très différent des bibliothèques comme sous-traitant ou Lodash, qui ne sont pas semblables à un curry par défaut et vous obligent généralement à écrire du code supplémentaire pour obtenir les mêmes résultats. L'API de Ramda est également plus cohérente et plus facile à utiliser, en mettant l'accent sur la simplicité et la lisibilité.
Oui, Ramda peut être utilisé avec d'autres bibliothèques et frameworks JavaScript. Il s'agit d'une bibliothèque autonome qui ne dépend d'aucune autre bibliothèque ou frameworks, et ne modifie pas les prototypes d'objets JavaScript. Il est sûr de travailler avec d'autres bibliothèques ou cadres sans se soucier des conflits ou des effets secondaires inattendus. Que vous utilisiez jQuery, React, Angular, Vue ou toute autre bibliothèque ou cadre, vous pouvez utiliser Ramda pour aider à écrire un code plus propre et plus fonctionnel.
Ramda est un excellent outil pour les débutants dans la programmation fonctionnelle. Sa conception d'API est simple et intuitive, et ses conventions de dénomination sont claires et cohérentes. La documentation est également très détaillée et il existe de nombreux exemples pour commencer. Mais, comme tout nouvel outil ou paradigme, il y a une courbe d'apprentissage. Il peut prendre un certain temps pour s'habituer à la pensée fonctionnelle et comprendre comment utiliser efficacement les fonctions de Ramda. Cependant, avec la pratique, vous constaterez que cela peut simplifier considérablement votre code et le rendre plus facile à comprendre.
RAMDA traite les valeurs nulles et non définies comme des valeurs nulles, similaires à la façon dont d'autres langages de programmation fonctionnelle les gèrent. Cela signifie que vous pouvez passer en toute sécurité nul ou indéfini aux fonctions de Ramda sans causer d'erreurs ou d'exceptions. Cependant, c'est une bonne idée de toujours vérifier les valeurs nulles ou non définies avant de les transmettre à une fonction pour éviter un comportement inattendu.
Oui, Ramda peut être utilisé dans les environnements Node.js. Il s'agit d'une bibliothèque commune qui peut être utilisée dans les navigateurs et Node.js. Vous pouvez l'installer via NPM et en avoir besoin dans votre module Node.js comme n'importe quel autre package.
Ramda n'a pas de support de fonctionnement asynchrone intégré, car il s'agit principalement d'une bibliothèque synchrone. Cependant, vous pouvez l'utiliser avec d'autres bibliothèques qui prennent en charge les opérations asynchrones, telles que les promesses ou asynchrones / attend. Vous pouvez également utiliser les fonctions de Ramda dans les fonctions asynchrones ou dans les rappels.
Ramda est un projet open source et est invité à contribuer à tout moment. Vous pouvez contribuer en signalant des bogues, en suggérant de nouvelles fonctionnalités, en améliorant la documentation ou en soumettant des demandes de traction. Avant de contribuer, il est préférable de lire le guide de contribution sur la page Ramda Github.
Oui, Ramda maintient activement et met régulièrement à jour. Le personnel de maintenance s'est engagé à maintenir la bibliothèque à jour et à résoudre tout problème ou erreur qui survient. Vous pouvez consulter la page GitHub pour les dernières mises à jour et versions.
Oui, Ramda est licencié sous une licence MIT, ce qui signifie que vous pouvez l'utiliser pour des projets commerciaux. Cependant, c'est une bonne idée de toujours lire le contrat de licence complet pour comprendre vos droits et responsabilités.
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!