Maison >interface Web >js tutoriel >Une introduction à la programmation fonctionnelle raisonnablement pure
Lorsque vous apprenez à programmer, vous êtes d'abord initié à la programmation procédurale; C'est là que vous contrôlez une machine en lui alimentant une liste séquentielle de commandes. Après avoir compris quelques principes fondamentaux linguistiques comme les variables, les affectations, les fonctions et les objets, vous pouvez bricoler ensemble un programme qui réalise ce que vous vous êtes mis à faire - et vous vous sentez comme un sorcier absolu.
Le processus de devenir un meilleur programme . Au fur et à mesure que vous devenez un meilleur programmeur, vous rédigerez des fonctions plus petites, obtiendrez une meilleure réutilisation de votre code, rédigerez des tests pour votre code et vous gagnerez la confiance que les programmes que vous écrivez continueront à faire ce que vous l'intendez. Personne n'aime trouver et corriger des bogues dans le code, donc devenir un meilleur programmeur consiste également à éviter certaines choses sujettes aux erreurs. Apprendre quoi éviter vient de l'expérience ou d'étudier les conseils de ceux qui sont plus expérimentés, comme Douglas Crockford explique en Javascript: les bonnes parties.
La programmation fonctionnelle nous donne des moyens de réduire la complexité de nos programmes en les réduisant dans leurs formes les plus simples: des fonctions qui se comportent comme des fonctions mathématiques pures. Apprendre les principes de la programmation fonctionnelle est un excellent ajout à vos compétences et vous aidera à écrire des programmes plus simples avec moins de bogues.
Les concepts clés de la programmation fonctionnelle sont des fonctions pures, des valeurs immuables, une composition et des effets secondaires apprivoisés.
Une fonction pure est une fonction qui, étant donné la même entrée, renvoie toujours la même sortie et n'a pas d'effet secondaire observable.
<span>// pure </span><span>function add(a<span>, b</span>) { </span> <span>return a + b; </span><span>} </span>
Cette fonction est pure . Il ne dépend ni ne modifie aucun état en dehors de la fonction et il toujours renvoie la même valeur de sortie pour la même entrée.
<span>// impure </span><span>var minimum = 21; </span><span>var checkAge = function(age) { </span> <span>return age >= minimum; // if minimum is changed we're cactus </span><span>}; </span>
Cette fonction est impure car elle repose sur un état mutable externe en dehors de la fonction.
Si nous déplaçons cette variable à l'intérieur de la fonction, elle devient pure et nous pouvons être certains que notre fonction vérifiera correctement notre âge à chaque fois .
<span>// pure </span><span>var checkAge = function(age) { </span> <span>var minimum = 21; </span> <span>return age >= minimum; </span><span>}; </span>
Les fonctions pures n'ont pas effets latéraux . Voici quelques-uns importants à garder à l'esprit:
Vous devez être conscient des méthodes de mutator sur les tableaux et les objets qui modifient les objets sous-tendants, un exemple de ceci est la différence entre les méthodes d'épissage et de tranche de l'arraie.
<span>// impure, splice mutates the array </span><span>var firstThree = function(arr) { </span> <span>return arr.splice(0,3); // arr may never be the same again </span><span>}; </span> <span>// pure, slice returns a new array </span><span>var firstThree = function(arr) { </span> <span>return arr.slice(0,3); </span><span>}; </span>
Si nous évitons de muter les méthodes sur les objets transmis à nos fonctions, notre programme devient plus facile à raisonner, nous pouvons raisonnablement nous attendre à ce que nos fonctions ne changent pas de choses sous nous.
<span>let items = ['a','b','c']; </span><span>let newItems = pure(items); </span><span>// I expect items to be ['a','b','c'] </span>
Les fonctions pures ont quelques avantages sur leurs homologues impurs:
Parce que les résultats des fonctions purs sont mis à cache, nous pouvons les mémoriser des opérations si coûteuses que la première fois que les fonctions sont appelées. Par exemple, la mémorisation des résultats de la recherche d'un grand indice produirait de grandes améliorations de performances sur les rediffusions.
réduire nos programmes à des fonctions pures peut considérablement réduire la complexité de nos programmes. Cependant, nos programmes fonctionnels peuvent également finir par nécessiter une aide de Rain Man pour comprendre si nous poussons l'abstraction fonctionnelle trop loin.
<span>import _ from 'ramda'; </span><span>import $ from 'jquery'; </span> <span>var Impure = { </span> <span>getJSON: _.curry(function(callback<span>, url</span>) { </span> $<span>.getJSON(url, callback); </span> <span>}), </span> <span>setHtml: _.curry(function(sel<span>, html</span>) { </span> <span>$(sel).html(html); </span> <span>}) </span><span>}; </span> <span>var img = function (url) { </span> <span>return $('<img />', { src: url }); </span><span>}; </span> <span>var url = function (t) { </span> <span>return 'http://api.flickr.com/services/feeds/photos_public.gne?tags=' + </span> t <span>+ '&format=json&jsoncallback=?'; </span><span>}; </span> <span>var mediaUrl = _.compose(_.prop('m'), _.prop('media')); </span><span>var mediaToImg = _.compose(img, mediaUrl); </span><span>var images = _.compose(_.map(mediaToImg), _.prop('items')); </span><span>var renderImages = _.compose(Impure.setHtml("body"), images); </span><span>var app = _.compose(Impure.getJSON(renderImages), url); </span><span>app("cats"); </span>
prenez une minute pour digérer le code ci-dessus.
à moins que vous ayez un arrière-plan dans la programmation fonctionnelle de ces abstractions (curry, utilisation excessive de la composition et de l'hélice) sont vraiment difficiles à suivre, tout comme le flux d'exécution. Le code ci-dessous est plus facile à comprendre et à modifier, il décrit également beaucoup plus clairement le programme que l'approche purement fonctionnelle ci-dessus et c'est moins de code.
<span>// pure </span><span>function add(a<span>, b</span>) { </span> <span>return a + b; </span><span>} </span>
ou, cette API alternative utilisant des abstractions comme Fetch and Promise nous aide à clarifier encore plus la signification de nos actions asynchrones.
<span>// impure </span><span>var minimum = 21; </span><span>var checkAge = function(age) { </span> <span>return age >= minimum; // if minimum is changed we're cactus </span><span>}; </span>
REMARQUE: Fetch et Promise sont des normes à venir afin qu'elles nécessitent des polyfills à utiliser aujourd'hui.
La demande Ajax et les opérations DOM ne seront jamais pures, mais nous pourrions faire une pure fonction à partir du reste, cartographiant la réponse JSON à un éventail d'images - excusons la dépendance à JQuery pour l'instant.
<span>// pure </span><span>var checkAge = function(age) { </span> <span>var minimum = 21; </span> <span>return age >= minimum; </span><span>}; </span>
Notre fonction est de faire deux choses maintenant:
La manière «fonctionnelle» de le faire est de créer des fonctions distinctes pour ces deux tâches et nous pouvons utiliser la composition pour passer la réponse d'une fonction dans l'autre.
<span>// impure, splice mutates the array </span><span>var firstThree = function(arr) { </span> <span>return arr.splice(0,3); // arr may never be the same again </span><span>}; </span> <span>// pure, slice returns a new array </span><span>var firstThree = function(arr) { </span> <span>return arr.slice(0,3); </span><span>}; </span>
Compose Renvoie une fonction qui est la composition d'une liste de fonctions, chacune consommant la valeur de retour de la fonction qui suit.
Voici ce que fait Compose, passant la réponse des URL dans notre fonction d'images.
<span>let items = ['a','b','c']; </span><span>let newItems = pure(items); </span><span>// I expect items to be ['a','b','c'] </span>
Il est utile de lire les arguments à composer de droite à gauche pour comprendre la direction du flux de données.
En réduisant notre programme à des fonctions pures, il nous donne une plus grande capacité à les réutiliser à l'avenir, ils sont beaucoup plus simples à tester et ils sont auto-documentés. L'inconvénient est que lorsqu'il est utilisé excessivement (comme dans le premier exemple), ces abstractions fonctionnelles peuvent rendre les choses plus complexes ce qui n'est certainement pas ce que nous voulons. La question la plus importante à poser quand le refactoring de code est la suivante:
Le code est-il plus facile à lire et à comprendre?
Maintenant, je n'essaie pas du tout d'attaquer la programmation fonctionnelle. Chaque développeur devrait faire un effort concerté pour apprendre les fonctions fondamentales qui vous permettent de résumer les modèles communs dans la programmation en code déclaratif beaucoup plus concis, ou comme Marijn Haverbeke le dit ..
un programmeur armé d'un répertoire de fonctions fondamentales et, plus important encore, les connaissances sur la façon de les utiliser, est beaucoup plus efficace que celle qui part de zéro. - JavaScript éloquent, Marijn Haverbeke
Voici une liste de fonctions essentielles que chaque développeur JavaScript doit apprendre et maîtriser. C'est aussi un excellent moyen de se réprimer vos compétences en JavaScript pour écrire chacune de ces fonctions à partir de zéro.
Arrays
Fonctions
Examinons certaines étapes pratiques que nous pouvons prendre pour améliorer le code ci-dessous en utilisant des concepts de programmation fonctionnelle.
<span>// pure </span><span>function add(a<span>, b</span>) { </span> <span>return a + b; </span><span>} </span>
Réduire la dépendance des fonctions à l'état partagé
Cela peut sembler évident et trivial, mais j'écris toujours des fonctions qui accèdent et modifient beaucoup d'état en dehors d'eux-mêmes, cela les rend plus difficiles à tester et plus sujets à l'erreur.
<span>// impure </span><span>var minimum = 21; </span><span>var checkAge = function(age) { </span> <span>return age >= minimum; // if minimum is changed we're cactus </span><span>}; </span>
Utilisez des abstractions de langage plus lisibles comme ForEach pour itérer
<span>// pure </span><span>var checkAge = function(age) { </span> <span>var minimum = 21; </span> <span>return age >= minimum; </span><span>}; </span>
Utilisez des abstractions de niveau supérieur comme la carte pour réduire la quantité de code
<span>// impure, splice mutates the array </span><span>var firstThree = function(arr) { </span> <span>return arr.splice(0,3); // arr may never be the same again </span><span>}; </span> <span>// pure, slice returns a new array </span><span>var firstThree = function(arr) { </span> <span>return arr.slice(0,3); </span><span>}; </span>
Réduisez les fonctions à leurs formes les plus simples
<span>let items = ['a','b','c']; </span><span>let newItems = pure(items); </span><span>// I expect items to be ['a','b','c'] </span>
Supprimer le code jusqu'à ce qu'il cesse de fonctionner
Nous n'avons pas du tout besoin d'une fonction pour une tâche aussi simple, le langage nous fournit des abstractions suffisantes pour l'écrire textuellement.
<span>import _ from 'ramda'; </span><span>import $ from 'jquery'; </span> <span>var Impure = { </span> <span>getJSON: _.curry(function(callback<span>, url</span>) { </span> $<span>.getJSON(url, callback); </span> <span>}), </span> <span>setHtml: _.curry(function(sel<span>, html</span>) { </span> <span>$(sel).html(html); </span> <span>}) </span><span>}; </span> <span>var img = function (url) { </span> <span>return $('<img />', { src: url }); </span><span>}; </span> <span>var url = function (t) { </span> <span>return 'http://api.flickr.com/services/feeds/photos_public.gne?tags=' + </span> t <span>+ '&format=json&jsoncallback=?'; </span><span>}; </span> <span>var mediaUrl = _.compose(_.prop('m'), _.prop('media')); </span><span>var mediaToImg = _.compose(img, mediaUrl); </span><span>var images = _.compose(_.map(mediaToImg), _.prop('items')); </span><span>var renderImages = _.compose(Impure.setHtml("body"), images); </span><span>var app = _.compose(Impure.getJSON(renderImages), url); </span><span>app("cats"); </span>
La possibilité de tester simplement nos programmes est un avantage clé des fonctions pures, donc dans cette section, nous allons configurer un harnais de test pour notre module Flickr que nous envisageons plus tôt.
Lancez un terminal et que votre éditeur de texte soit prêt et prêt, nous utiliserons Mocha comme coureur de test et Babel pour compiller notre code ES6.
<span>var app = (tags)=> { </span> <span>let url = <span>`http://api.flickr.com/services/feeds/photos_public.gne?tags=<span>${tags}</span>&format=json&jsoncallback=?`</span> </span> $<span>.getJSON(url, (data)=> { </span> <span>let urls = data.items.map((item)=> item.media.m) </span> <span>let images = urls.map((url)=> $('<img />', { src: url }) ) </span> <span>$(document.body).html(images) </span> <span>}) </span><span>} </span><span>app("cats") </span>
Mocha a un tas de fonctions pratiques comme décrire et pour briser nos tests et nos crochets tels que avant et après pour les tâches de configuration et de démontage. Assert est un package de nœud de base qui peut effectuer des tests d'égalité simples, affirmer et affirmer.
Écrivons notre premier test dans le test / exemple.js
<span>let flickr = (tags)=> { </span> <span>let url = <span>`http://api.flickr.com/services/feeds/photos_public.gne?tags=<span>${tags}</span>&format=json&jsoncallback=?`</span> </span> <span>return fetch(url) </span> <span>.then((resp)=> resp.json()) </span> <span>.then((data)=> { </span> <span>let urls = data.items.map((item)=> item.media.m ) </span> <span>let images = urls.map((url)=> $('<img />', { src: url }) ) </span> <span>return images </span> <span>}) </span><span>} </span><span>flickr("cats").then((images)=> { </span> <span>$(document.body).html(images) </span><span>}) </span>ouvrir package.json et modifier le script "test" à ce qui suit
<span>let responseToImages = (resp)=> { </span> <span>let urls = resp.items.map((item)=> item.media.m ) </span> <span>let images = urls.map((url)=> $('<img />', { src: url })) </span> <span>return images </span><span>} </span>Ensuite, vous devriez pouvoir exécuter le test NPM à partir de la ligne de commande pour confirmer que tout fonctionne comme prévu.
<span>let urls = (data)=> { </span> <span>return data.items.map((item)=> item.media.m) </span><span>} </span><span>let images = (urls)=> { </span> <span>return urls.map((url)=> $('<img />', { src: url })) </span><span>} </span><span>let responseToImages = _.compose(images, urls) </span>boom.
Remarque: vous pouvez également ajouter un drapeau -W à la fin de cette commande si vous voulez que Mocha surveillait les modifications et exécutera automatiquement les tests, ils fonctionneront considérablement plus rapidement lors des rediffusions.
<span>let responseToImages = (data)=> { </span> <span>return images(urls(data)) </span><span>} </span>Tester notre module Flickr
<span>let items = ['a', 'b', 'c']; </span><span>let upperCaseItems = ()=> { </span> <span>let arr = []; </span> <span>for (let i = 0, ii = items.length; i < ii; i++) { </span> <span>let item = items[i]; </span> arr<span>.push(item.toUpperCase()); </span> <span>} </span> items <span>= arr; </span><span>} </span>Notre module exposent deux méthodes: Flickr à consommer publiquement et une fonction privée _Responsetoimages afin que nous puissions tester cela isolément.
Nous avons quelques nouvelles dépendances: jQuery, souligner et polyfills pour le fetch et la promesse. Pour tester ceux que nous pouvons utiliser JSDom pour polyfilluer la fenêtre et le document des objets DOM et nous pouvons utiliser le package Sinon pour déborder l'API Fetch.
<span>// pure </span><span>function add(a<span>, b</span>) { </span> <span>return a + b; </span><span>} </span>
Ouvrir Test / _SetUp.js et nous configurerons JSDom avec nos globaux dont notre module dépend.
<span>// impure </span><span>var minimum = 21; </span><span>var checkAge = function(age) { </span> <span>return age >= minimum; // if minimum is changed we're cactus </span><span>}; </span>
Nos tests peuvent passer dans le test / flickr.js où nous ferons des affirmations sur la sortie de nos fonctions donnée par des entrées prédéfinies. Nous «Stub» ou remplacez la méthode globale de fetch pour intercepter et simuler la demande HTTP afin que nous puissions exécuter nos tests sans frapper directement l'API Flickr.
<span>// pure </span><span>var checkAge = function(age) { </span> <span>var minimum = 21; </span> <span>return age >= minimum; </span><span>}; </span>
Exécutez à nouveau nos tests avec le test NPM et vous devriez voir trois assurer les tiques vertes.
<span>// impure, splice mutates the array </span><span>var firstThree = function(arr) { </span> <span>return arr.splice(0,3); // arr may never be the same again </span><span>}; </span> <span>// pure, slice returns a new array </span><span>var firstThree = function(arr) { </span> <span>return arr.slice(0,3); </span><span>}; </span>
ouf! Nous avons testé avec succès notre petit module et les fonctions qui le composent, l'apprentissage des fonctions pures et comment utiliser la composition fonctionnelle en cours de route. Nous avons séparé le pur de l'impur, il est lisible, composé de petites fonctions, et il est bien testé. Le code est plus facile à lire, à comprendre et à modifier que l'exemple déraisonnablement pur ci-dessus et c'est mon seul objectif lorsque le code de refactorisation.
Fonctions pures, utilisez-les.
-
C'est tout pour le moment! Merci d'avoir lu et j'espère que vous avez trouvé que cela une bonne introduction à la programmation fonctionnelle, à la refactorisation et aux tests en JavaScript. C'est un paradigme intéressant qui fait des vagues pour le moment, en grande partie en raison de la popularité croissante de bibliothèques comme React, Redux, Elm, Cycle et Reactivex qui encouragent ou appliquent ces modèles.
sauter, l'eau est chaude.
Les fonctions pures sont un concept fondamental dans la programmation fonctionnelle. Ce sont des fonctions qui produisent toujours la même sortie pour la même entrée et n'ont aucun effet secondaire. Cela signifie qu'ils ne modifient aucun État en dehors de leur portée ou dépendent d'un état externe. Cela les rend prévisibles et faciles à tester, car il vous suffit de considérer l'entrée et la sortie, sans vous soucier des facteurs externes. Les fonctions pures favorisent également la réutilisabilité et la lisibilité du code, ce qui rend votre code plus facile à comprendre et à entretenir.
La programmation fonctionnelle est un paradigme de programmation qui traite le calcul comme l'évaluation des fonctions mathématiques et évite de changer l'état et les données mutables. Cela contraste avec la programmation impérative, où les programmes sont composés de déclarations qui modifient l'état global lorsqu'ils sont exécutés. La programmation fonctionnelle favorise les abstractions de niveau supérieur, comme les fonctions en tant que citoyens de première classe, et encourage la programmation avec des expressions au lieu des déclarations. Cela conduit à un style de programmation plus déclaratif et expressif qui est plus facile à raisonner.
La mise en œuvre de fonctions pures en javascript implique de s'assurer que votre fonction produit la même sortie pour la même entrée et ne produit aucun effet secondaire. Voici un exemple:
Fonction Ajouter (a, b) {
Renvoie un b;
}
Dans cet exemple, la fonction ADD est une fonction pure car elle renvoie toujours le Même résultat compte tenu des mêmes arguments et ne modifie aucun état externe.
Les fonctions pures offrent plusieurs avantages en JavaScript. Ils rendent votre code plus prévisible et plus facile à tester et à déboguer, car il vous suffit de considérer l'entrée et la sortie de la fonction. Ils rendent également votre code plus lisible et maintenable, car ils favorisent un style de programmation clair et simple. De plus, les fonctions pures sont très réutilisables et composables, vous permettant de créer des fonctionnalités plus complexes avec moins de code.
Comment la programmation fonctionnelle est-elle liée à la concurrence et au parallélisme?
La composition de la fonction est un concept fondamental dans la programmation fonctionnelle. Il s'agit de combiner deux fonctions ou plus pour créer une nouvelle fonction. Le résultat d'une fonction est utilisé comme entrée à la fonction suivante. Cela vous permet de créer des fonctionnalités complexes à partir de fonctions simples, favorisant la réutilisabilité et la lisibilité du code.
L'immuabilité est un principe clé de la programmation fonctionnelle. Cela signifie qu'une fois une structure de données créée, elle ne peut pas être modifiée. Au lieu de cela, si vous souhaitez modifier une structure de données, vous en créez une nouvelle avec les modifications souhaitées. Cela évite les effets secondaires et rend votre code plus sûr et plus facile à raisonner.
Dans la programmation fonctionnelle, l'état est traité avec soin pour éviter les effets secondaires. Au lieu de changer l'état, la programmation fonctionnelle utilise souvent des fonctions pures qui renvoient un nouvel état. Cela rend l'État prévisible et facile à gérer. Certains langages de programmation fonctionnelle offrent également des fonctionnalités avancées pour la gestion de l'État, telles que les Monades dans Haskell.
La programmation fonctionnelle peut être utilisée dans un large éventail d'applications . Il est particulièrement utile dans les situations où la concurrence et le parallélisme sont importants, comme dans l'informatique multi-core et distribuée. La programmation fonctionnelle est également couramment utilisée dans le traitement des données et l'analyse, où les fonctions pures et l'immuabilité peuvent aider à assurer l'intégrité des données. De plus, les concepts de programmation fonctionnelle sont de plus en plus adoptés dans le développement frontal, avec des cadres populaires comme React.js en utilisant un style fonctionnel pour le développement des composants.
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!