cnam:utc503:td3

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentes Révision précédente
Prochaine révision
Révision précédente
cnam:utc503:td3 [2025/08/12 02:35] – modification externe 127.0.0.1cnam:utc503:td3 [2026/10/20 09:17] (Version actuelle) – jcheron
Ligne 1: Ligne 1:
-====== TD 3 ====== +====== TD 3 : Pf en JS ======
-===== Exercice 1 : Fonctions Pures =====+
  
-==== 1 - Création d'une fonction pure simple ====+<WRAP info round> 
 +**Objectifs du TD**
  
-  * Écrire une fonction pure qui prend un tableau de nombres et retourne un nouveau tableau avec chaque nombre doublé.+À la fin de ce TD, vous saurez : 
 +  * définir précisément la pureté, l'immutabilité et la transparence référentielle, et les vérifier sur du code réel ; 
 +  * identifier les effets de bord cachés (y compris ceux qui sont subtils) ; 
 +  * écrire des fonctions d'ordre supérieur, des closures et des fonctions curryfiées ; 
 +  * composer des fonctions pour construire des traitements de données complexes ; 
 +  * refactoriser du code impératif en code fonctionnel.
  
-==== 2 - Test de la pureté ====+**Prérequis** : syntaxe JS (fonctions fléchées, destructuration, spread).\\ 
 +**Consigne générale** : pour chaque exercice, écrivez des tests avec ''console.assert'' (ou Jest si disponible). Un code sans test n'est pas considéré comme terminé. 
 +</WRAP> 
 + 
 +<WRAP tip round> 
 +**Mini-référence : mutateurs vs non-mutateurs sur les tableaux** 
 + 
 +^ Mutent le tableau ^ Ne mutent pas ^ 
 +| ''push'', ''pop'', ''shift'', ''unshift'', ''splice'', ''sort'', ''reverse'', ''fill'' | ''map'', ''filter'', ''reduce'', ''slice'', ''concat'', ''[...arr]'', ''toSorted'', ''toReversed'', ''with'' | 
 + 
 +''sort'' et ''reverse'' sont des pièges classiques. 
 +</WRAP> 
 + 
 +===== Exercice 1 : Fonctions pures ===== 
 + 
 +**Définition à tester** : une fonction est pure si (1) pour les mêmes arguments elle retourne toujours le même résultat, et (2) elle ne produit aucun effet observable en dehors de son résultat. 
 + 
 +==== 1.1 - Écrire une fonction pure ==== 
 + 
 +  * Écrire ''double(numbers)'' qui retourne un **nouveau** tableau où chaque nombre est doublé.  
 +  * Écrire les tests suivants : 
 + 
 +<sxh javascript> 
 +const input = [1, 2, 3]; 
 +const output = double(input); 
 +console.assert(JSON.stringify(output) === "[2,4,6]"); 
 +console.assert(JSON.stringify(input) === "[1,2,3]");  // l'entrée n'a pas changé 
 +console.assert(output !== input);                      // c'est un nouveau tableau 
 +console.assert(JSON.stringify(double([])) === "[]");   // cas limite 
 +</sxh> 
 + 
 +==== 1.2 - Vérifier la pureté ==== 
 + 
 +  * Appeler ''double'' 1000 fois avec la même entrée et vérifier que le résultat est toujours identique (''JSON.stringify'' pour comparer). 
 +  * Sans modifier le code, ajoutez un test qui prouve que ''double'' ne modifie pas son argument (''Object.freeze'' sur l'entrée : la fonction ne doit pas lever d'erreur en mode strict). 
 +  * Compléter ce tableau pour ''double'' : 
 + 
 +^ Critère ^ Respecté ? ^ Justification ^ 
 +| Même entrée → même sortie | | | 
 +| Ne modifie pas ses arguments | | | 
 +| Ne lit aucun état externe | | | 
 +| Ne produit aucun effet observable (console, réseau, DOM, fichier...) | | | 
 + 
 +==== 1.3 - Quatre fonctions impures, quatre sources d'impureté ==== 
 + 
 +Pour chacune des fonctions ci-dessous, **identifiez la cause exacte d'impureté** parmi : //dépendance à un état externe//, //mutation d'argument//, //effet observable//, //non-déterminisme//. Puis **réécrivez-la en version pure**. 
 + 
 +<sxh javascript> 
 +// A 
 +let total = 0; 
 +function addToTotal(n) { total += n; return total; } 
 + 
 +// B 
 +function addItem(cart, item) { cart.push(item); return cart; } 
 + 
 +// C 
 +function greet(name) { 
 +  console.log("Bonjour " + name); 
 +  return name.length; 
 +} 
 + 
 +// D 
 +function isExpired(deadline) { return new Date() > deadline; } 
 +</sxh> 
 + 
 + 
 +==== 1.4 - Pour aller plus loin : l'impureté cachée ====
  
-  * Tester la fonction avec différents ensembles de données. +  * Cette fonction est-elle pure ? Justifiez en écrivant un test qui le démontre ou l'infirme.
-  * Identifier les caractéristiques qui font que la fonction est pure.+
  
-==== 3 - Exemple d'une fonction impure ====+<sxh javascript> 
 +function normalize(users) { 
 +  return users.map(u => { u.name = u.name.trim(); return u; }); 
 +} 
 +</sxh>
  
-  * Créer une fonction qui modifie une variable extérieure (effet de bord) et expliquer pourquoi elle est impure.+  * Corrigez-la. Que constatez-vous sur la différence entre //"retourne un nouveau tableau"// et //"ne modifie rien"// ?
  
 <WRAP todo round> <WRAP todo round>
 === Points de discussion === === Points de discussion ===
  
-  * Pourquoi éviter les effets de bord ? +  * Dans 1.3, laquelle des quatre sources d'impureté est la plus difficile à repérer à la lecture du code ? Pourquoi ? 
-  * Quels types de bugs ou de problèmes peuvent survenir dans un code non pur ?+  * Une fonction qui fait ''console.log'' est-elle "dangereuse" ? Où placer les fonctions impures dans une application si on ne peut pas les supprimer ? 
 +  * Dans 1.4, ''map'' a-t-il rendu la fonction pure ? Que dit cet exemple sur l'usage "magique" des méthodes fonctionnelles ? 
 +  * Pourquoi une fonction pure est-elle triviale à tester, contrairement à A, B, C et D ?
 </WRAP> </WRAP>
 +
 ===== Exercice 2 : Immutabilité ===== ===== Exercice 2 : Immutabilité =====
  
-==== 1 - Ajout d'un élément sans modification du tableau ====+**Objectif** : produire des "versions modifiées" de données sans jamais les muter, y compris pour des structures imbriquées.
  
-  * Écrire une fonction qui ajoute un élément à un tableau sans modifier le tableau d'origine.+==== 2.1 - Tableaux : les quatre opérations de base ====
  
-==== 2 - Comparaison avec une approche mutable ====+Écrire quatre fonctions pures, sans mutateur, qui retournent un nouveau tableau :
  
-  * Modifier directement un tableau en utilisant `.push()` et observer les changements sur le tableau d'origine.+  * ''append(arr, x)'' : ajoute à la fin ; 
 +  * ''removeAt(arr, i)'' : retire l'élément à l'indice ''i'' ; 
 +  * ''replaceAt(arr, i, x)'' : remplace l'élément à l'indice ''i'' ; 
 +  * ''sortedCopy(arr)'' : retourne une copie triée par ordre croissant **sans** utiliser ''toSorted'' (utilisez le spread).
  
-==== 3 - Modification immutable d'un objet ====+Vérifier avec ''Object.freeze(arr)'' que votre code ne tente jamais de muter l'entrée.
  
-  * Écrire une fonction qui modifie une propriété d'un objet sans altérer l'original.+==== 2.2 - Observer la mutation ==== 
 + 
 +  * Exécuter ce code et **prédire le résultat avant** de lancer : 
 + 
 +<sxh javascript> 
 +const a = [3, 1, 2]; 
 +const b = a; 
 +b.push(4); 
 +const c = a.sort(); 
 +console.log(a, b, c); 
 +console.log(a === b, a === c); 
 +</sxh> 
 + 
 +  * Expliquer en 3 phrases ce qu'est une //référence partagée// et pourquoi ''a'', ''b'' et ''c'' sont liés. 
 + 
 +==== 2.3 - Objets : mises à jour immuables ==== 
 + 
 +Soit : 
 + 
 +<sxh javascript> 
 +const user = { 
 +  name: "Alice", 
 +  address: { city: "Paris", zip: "75001" }, 
 +  tags: ["admin", "dev"] 
 +}; 
 +</sxh> 
 + 
 +Écrire **sans muter** ''user'' : 
 + 
 +  * ''rename(user, newName)'' ; 
 +  * ''moveTo(user, newCity)'' : modifie ''address.city'' (attention à l'imbrication) ; 
 +  * ''addTag(user, tag)'' ; 
 +  * ''removeTag(user, tag)''. 
 + 
 +Pour chaque fonction, tester que l'original est inchangé **et** que les parties non modifiées sont partagées (''newUser.tags === user.tags'' pour ''rename''). 
 + 
 +==== 2.4 - Le piège de la copie superficielle ==== 
 + 
 +  * Que fait ce code ? Démontrez le bug par un test. 
 + 
 +<sxh javascript> 
 +const copy = { ...user }; 
 +copy.address.city = "Lyon"; 
 +console.log(user.address.city); 
 +</sxh> 
 + 
 +  * Corrigez de deux façons : (a) par copie imbriquée avec spread, (b) avec ''structuredClone''. Comparez : quelle approche partage le plus de structure ? Laquelle est la plus coûteuse ? 
 + 
 +==== 2.5 - Pour aller plus loin : mise à jour profonde générique ==== 
 + 
 +  * Écrire ''setIn(obj, path, value)'' qui met à jour une valeur à un chemin donné (ex : ''["address", "city"]'') de façon immuable. 
 + 
 +<sxh javascript> 
 +const updated = setIn(user, ["address", "city"], "Lyon"); 
 +console.assert(updated.address.city === "Lyon"); 
 +console.assert(user.address.city === "Paris"); 
 +console.assert(updated.tags === user.tags); 
 +</sxh>
  
 <WRAP todo round> <WRAP todo round>
 === Points de discussion === === Points de discussion ===
  
-  * Pourquoi l'immutabilité est-elle essentielle en programmation fonctionnelle ? +  * Dans 2.2, quel bug concret une référence partagée peut-elle provoquer dans une application où deux composants utilisent le même tableau ? 
-  * Quels avantages cela présente-t-il pour la gestion des états dans des applications complexes ?+  * ''Object.freeze'' est-il suffisant pour garantir l'immutabilité ? Testez-le sur ''user.address''. 
 +  * Copier toute la structure à chaque modification est-il coûteux ? Comment le //partage structurel// (cf. ''updated.tags === user.tags'') limite-t-il ce coût ? Quel lien avec Immer, Immutable.js ou Redux ? 
 +  * Citez un cas où la mutation locale est acceptable (ex : accumulateur dans un ''reduce'' non exposé). À quelle condition préserve-t-elle la pureté //vue de l'extérieur// ?
 </WRAP> </WRAP>
-===== Exercice 3 : Transparence Référentielle ===== 
  
-==== 1 - Transparence dans une fonction simple ====+===== Exercice 3 : Transparence référentielle =====
  
-  * Créer une fonction qui calcule la somme de deux nombres. +**Définition** : une expression est référentiellement transparente si on peut la remplacer par sa valeur sans changer le comportement du programme.
-  * Remplacer les appels de fonction dans le code par les valeurs retournées et tester le comportement.+
  
-==== 2 - Transparence dans des expressions complexes ====+==== 3.1 - Substitution sur des fonctions simples ====
  
-  * Introduire des fonctions imbriquées (somme et multiplication par exemple) et remplacer les appels par leurs valeurs. +  * Écrire %%add(a, b)%% et %%mul(a, b)%%. 
-  * Tester si le programme reste identique.+  * Soit l'expression suivante :
  
-==== 3 - Absence de transparence référentielle ====+<sxh javascript> 
 +add(mul(2, 3), mul(2, 3)) 
 +</sxh>
  
-  * Créer une fonction avec des effets de bord (ex: `Math.random()` ou modification d'une variable globale) et expliquer pourquoi elle n'est pas transparente.+  * Remplacer **à la main**, étape par étape, chaque appel par sa valeur (modèle de substitution) et vérifier que le résultat ne change pas. 
 +  * Factoriser : 
 + 
 +<sxh javascript> 
 +const x = mul(2, 3); 
 +add(x, x) 
 +</sxh> 
 + 
 +  * Pourquoi cette factorisation est-elle sûre ? 
 + 
 +==== 3.2 - Détecter les ruptures de transparence ==== 
 + 
 +Pour chacune des fonctions suivantes, dire si elle est transparente et justifier. Tester en appelant deux fois de suite avec les mêmes arguments. 
 + 
 +<sxh javascript> 
 +// A 
 +let counter = 0; 
 +const next = () => ++counter; 
 + 
 +// B 
 +const rand = () => Math.random(); 
 + 
 +// C 
 +const now = () => Date.now(); 
 + 
 +// D (TAX est modifiable ailleurs dans le programme) 
 +let TAX = 0.2; 
 +const addTax = (price) => price * (1 + TAX); 
 + 
 +// E 
 +const first = (arr) => arr[0]; 
 + 
 +// F 
 +const logAndAdd = (a, b) => { 
 +  console.log(a); 
 +  return a + b; 
 +}; 
 +</sxh> 
 + 
 +Pour chaque cas, reformulez : //quelle information supplémentaire (hors arguments) la fonction utilise-t-elle ou produit-elle ?// 
 + 
 +==== 3.3 - Rendre transparent ==== 
 + 
 +  * Réécrivez **A** pour qu'il soit référentiellement transparent (indice : l'état doit devenir une entrée //et// une sortie). 
 +  * Réécrivez **B** pour rendre le hasard déterministe : %%random(seed)%% retourne un couple %%[valeur, nouvelleGraine]%% (générateur congruentiel linéaire simple, par exemple : 
 + 
 +<sxh javascript> 
 +(seed * 1664525 + 1013904223) % 2**32 
 +</sxh> 
 + 
 +  * Montrez que %%random(42)%% donne toujours le même résultat. 
 + 
 +==== 3.4 - Mémoïsation : l'application directe ==== 
 + 
 +  * Écrire %%memoize(fn)%% qui retourne une version de %%fn%% mettant en cache ses résultats (clé : %%JSON.stringify(args)%%). 
 +  * Appliquer à une fonction coûteuse (ex : %%fib%% récursif naïf, comparez les temps pour %%fib(35)%%). 
 +  * **Test crucial** : appliquer %%memoize%% à la fonction %%next%% de 3.2.A et à %%rand%% de 3.2.B. Que se passe-t-il ? Pourquoi est-ce incorrect ?
  
 <WRAP todo round> <WRAP todo round>
 === Points de discussion === === Points de discussion ===
  
-  * Pourquoi la transparence référentielle est-elle cruciale pour rendre un code plus facile à lire et à raisonner ? +  * Pourquoi la mémoïsation n'est-elle //valide// que sur des fonctions référentiellement transparentes ? Lien avec 3.4. 
-  * Comment la transparence référentielle permet-elle d'optimiser ou de simplifier un code lors du développement ?+  * En 3.1, la factorisation de %%mul(2, 3)%% est sûre. Dans un code avec effets de bord, serait-elle toujours sûre ? Quelle conséquence pour le //refactoring// et pour les optimisations d'un compilateur ? 
 +  * Une fonction peut être pure sans que son //appel// soit "inoffensif" (ex : très coûteuse). La transparence référentielle garantit-elle les performances ? 
 +  * Une fonction qui lit une constante globale (%%const TAX = 0.2%%) est-elle référentiellement transparente ? Et si la constante est un objet mutable ?
 </WRAP> </WRAP>
 +
 ===== Exercice 4 : Fonctions d'ordre supérieur ===== ===== Exercice 4 : Fonctions d'ordre supérieur =====
  
-==== 1 - Application d'une fonction à chaque élément d'un tableau ====+**Définition** : une fonction d'ordre supérieur prend une fonction en argument et/ou retourne une fonction.
  
-  * Écrire une fonction d’ordre supérieur qui prend une fonction et l’applique à chaque élément d’un tableau.+==== 4.1 - Reconstruire les classiques ====
  
-==== 2 - Création de fonctions retournant des fonctions ====+Sans utiliser les méthodes natives correspondantes, implémenter (avec une boucle ou de la récursion) :
  
-  * Écrire une fonction qui retourne une autre fonction (par exemple, une fonction qui crée un multiplicateur).+  * %%myMap(arr, fn)%% ; 
 +  * %%myFilter(arr, pred)%% ; 
 +  * %%myReduce(arr, fn, init)%%.
  
-==== 3 - Utilisation de fonctions d'ordre supérieur intégrées ====+Puis **réimplémenter %%myMap%% et %%myFilter%% uniquement à partir de %%myReduce%%**. Ce que vous découvrez : %%reduce%% est la primitive universelle de traitement de tableau.
  
-  * Expérimenter avec les méthodes comme `filter()`, `reduce()`, et `forEach()` pour transformer des tableaux.+==== 4.2 - Fonctions qui retournent des fonctions (closures) ==== 
 + 
 +  * %%multiplier(n)%% retourne une fonction qui multiplie son argument par %%n%%. Utiliser pour créer %%double%%, %%triple%% et les passer à %%myMap%%. 
 +  * %%greaterThan(n)%% retourne un prédicat. L'utiliser avec %%filter%%. 
 +  * %%counter()%% retourne une fonction qui retourne 1, 2, 3... à chaque appel. **Cette fonction est-elle pure ?** Où est stocké l'état ? Que dire de %%counter%% elle-même (celle qui crée) ? 
 + 
 +==== 4.3 - Currying et application partielle ==== 
 + 
 +  * Écrire %%curry(fn)%% qui transforme %%f(a, b, c)%% en %%f(a)(b)(c)%% (à arité fixe, via %%fn.length%%). 
 +  * Écrire %%partial(fn, ...args)%% qui fige les premiers arguments. 
 +  * Utiliser sur les fonctions suivantes : 
 + 
 +<sxh javascript> 
 +const add3 = (a, b, c) => a + b + c; 
 +const formatPrice = (currency, decimals, value) => /* à compléter */; 
 +</sxh> 
 + 
 +  * Créer %%formatEUR%% et %%formatUSD2%% à partir de %%formatPrice%%. 
 + 
 +==== 4.4 - Composition ==== 
 + 
 +  * Écrire %%compose(...fns)%% (exécution de droite à gauche) et %%pipe(...fns)%% (gauche à droite). 
 +  * Construire %%slugify%% en composant : %%trim%%, %%toLowerCase%%, remplacement des espaces par des tirets, suppression des caractères non alphanumériques. 
 + 
 +<sxh javascript> 
 +console.assert(slugify("  Hello World !  ") === "hello-world"); 
 +</sxh> 
 + 
 +  * Vérifier que %%compose(f, compose(g, h))%% et %%compose(compose(f, g), h)%% donnent le même résultat (associativité) sur un exemple. 
 + 
 +==== 4.5 - Pièges des méthodes natives ==== 
 + 
 +  * Prédire puis exécuter : 
 + 
 +<sxh javascript> 
 +["1", "2", "3"].map(parseInt) 
 +</sxh> 
 + 
 +  * Expliquer le résultat (arguments passés par %%map%% au callback). Corriger. 
 +  * Pourquoi %%forEach%% est-il //inadapté// à un style fonctionnel (que retourne-t-il ? quel est son seul intérêt ?) ? 
 +  * %%reduce%% sans valeur initiale sur un tableau vide : que se passe-t-il ? Quelle est la règle pour choisir une valeur initiale (élément neutre) ? 
 + 
 +==== 4.6 - Pour aller plus loin ==== 
 + 
 +  * Écrire %%once(fn)%% (n'exécute %%fn%% qu'une fois) et %%debounce%% ou %%retry(n, fn)%% : ces utilitaires sont des HOF "décorateurs" qui ajoutent un comportement sans modifier la fonction d'origine. 
 +  * Écrire %%groupBy(arr, keyFn)%% avec %%reduce%%, retournant un objet de tableaux, **sans muter l'accumulateur** (spread à chaque itération). Comparez avec la version qui mute l'accumulateur : quel est le coût, et pourquoi la mutation locale reste-t-elle défendable ici ?
  
 <WRAP todo round> <WRAP todo round>
 === Points de discussion === === Points de discussion ===
  
-  * En quoi les fonctions d’ordre supérieur facilitent-elles la modularité du code ? +  * Dans 4.1, qu'est-ce qui rend %%reduce%% plus "puissant" que %%map%% et %%filter%% ? Peut-on exprimer %%reduce%% avec %%map%% ? 
-  * Comment ce concept peut-il être utilisé pour encapsuler des comportements et les réutiliser dans plusieurs contextes ?+  * Dans 4.2, où vit l'état de la closure ? Comparer avec un objet. Quand une closure est-elle un bon outil d'encapsulation ? 
 +  * Le currying favorise-t-il la réutilisation ? Dans quel cas rend-il le code moins lisible (ordre des paramètres) ? Quelle règle pour ordonner les arguments (données en dernier) ? 
 +  * %%pipe%% vs enchaînement de méthodes (%%arr.map(...).filter(...)%%) : avantages et limites de chaque approche ?
 </WRAP> </WRAP>
-===== Exercice 5 : Combinaison des principes ===== 
  
-==== 1 - Manipulation de tableaux d'objets ====+===== Exercice 5 : Synthèse : panier d'achat ===== 
 + 
 +**Contexte** : vous développez le calcul d'un panier pour un site e-commerce. Toutes les fonctions doivent être **pures** et **sans mutation**. 
 + 
 +<sxh javascript> 
 +const products = [ 
 +  { id: 1, name: "Clavier",  price: 80,  category: "tech",   stock: 5 }, 
 +  { id: 2, name: "Souris",   price: 30,  category: "tech",   stock: 0 }, 
 +  { id: 3, name: "Livre JS", price: 40,  category: "book",   stock: 12 }, 
 +  { id: 4, name: "Cahier",   price: 5,   category: "office", stock: 100 }, 
 +  { id: 5, name: "Écran",    price: 250, category: "tech",   stock: 2 } 
 +]; 
 +</sxh> 
 + 
 +==== 5.1 - Version impérative (point de départ) ==== 
 + 
 +On vous donne ce code : 
 + 
 +<sxh javascript> 
 +let total = 0; 
 +for (let i = 0; i < products.length; i++) { 
 +  if (products[i].stock > 0) { 
 +    products[i].price = products[i].price * 0.9; 
 +    total += products[i].price; 
 +  } 
 +} 
 +console.log("Total : " + total); 
 +</sxh> 
 + 
 +  * Lister **tous** les problèmes (au moins 4 : mutation, effet de bord, mélange calcul/affichage, non-réutilisable, non-testable...). 
 +  * Exécuter deux fois d'affilée. Que constatez-vous sur %%products%% ? 
 + 
 +==== 5.2 - Briques élémentaires pures ==== 
 + 
 +Écrire et tester chacune isolément : 
 + 
 +  * %%applyDiscount(rate)(product)%% : retourne un **nouveau** produit avec le prix réduit (currying : le taux d'abord). Arrondir à 2 décimales. 
 +  * %%inStock(product)%% : prédicat. 
 +  * %%byCategory(cat)(product)%% : prédicat curryfié. 
 +  * %%getPrice(product)%% : sélecteur. 
 +  * %%sum(numbers)%% : somme via %%reduce%% (valeur initiale 0). 
 + 
 +==== 5.3 - Assemblage ==== 
 + 
 +  * Écrire %%cartTotal(products, rate)%% en composant les briques avec %%pipe%% : filtrer le stock, appliquer la remise, extraire les prix, sommer. 
 +  * Vérifier : le total attendu est **337.5** pour 10 % de remise sur l'exemple (tests unitaires obligatoires), et %%products%% est inchangé (%%Object.freeze%% profond sur les données). 
 + 
 +==== 5.4 - Extensions métier ==== 
 + 
 +Chaque extension doit **réutiliser** les briques existantes, pas dupliquer du code : 
 + 
 +  * %%totalByCategory(products, rate)%% : retourne un objet de la forme suivante (via %%reduce%%) : 
 + 
 +<sxh javascript> 
 +{ tech: ..., book: ..., office: ... } 
 +</sxh> 
 + 
 +  * %%applyBulkDiscount%% : une remise de 5 % supplémentaire si le total dépasse 200. Cette règle doit être une fonction pure séparée, composable. 
 +  * %%receipt(products, rate)%% : retourne un tableau de **lignes de texte** (chaînes) décrivant le ticket. **L'affichage (%%console.log%%) se fait en dehors**, dans une seule fonction impure en bout de chaîne. 
 + 
 +==== 5.5 - Analyse de la pureté (partie rédigée) ==== 
 + 
 +Pour chaque fonction écrite, remplir : 
 + 
 +^ Fonction ^ Pure ? ^ Mutation ? ^ Transparente ? ^ Remplaçable par sa valeur ? ^ 
 +| %%applyDiscount%% | | | | | 
 +| %%cartTotal%% | | | | | 
 +| %%receipt%% | | | | | 
 +| fonction d'affichage | | | | | 
 + 
 +Ensuite, **démontrer** la transparence référentielle de %%cartTotal%% : choisissez un appel, remplacez-le par sa valeur dans un autre calcul, et vérifiez l'équivalence.
  
-  * Travailler sur un tableau d'objets représentant des produits avec un prix. +==== 5.6 - Pour aller plus loin ====
-  * Appliquer une réduction à chaque produit tout en conservant l'immutabilité.+
  
-==== 2 - Calcul total avec `reduce` ====+  * Rendre le taux de remise //dépendant du client// (%%regular%% 0 %, %%premium%% 10 %, %%vip%% 20 %) sans %%if%%/%%switch%% en cascade : utilisez un objet de correspondance et la composition. 
 +  * Écrire %%updateStock(products, id, delta)%% qui retourne un nouveau tableau avec le stock mis à jour pour un produit (rappel exercice 2 : mise à jour immuable dans un tableau d'objets). 
 +  * Implémenter un //panier// comme un **réducteur** : %%cartReducer(state, action)%% où %%action%% vaut l'un des deux objets suivants :
  
-  * Calculer le prix total après réduction en utilisant `reduce`.+<sxh javascript> 
 +{ type: "ADD", product } 
 +{ type: "REMOVE", id } 
 +</sxh>
  
-==== 3 - Refactorisation pour pureté et réutilisabilité ====+  * Rejouez une liste d'actions avec %%reduce%%. Observez le lien avec Redux et le pattern "état = fonction des événements passés".
  
-  * Réécrire le code en appliquant les principes vus, comme découper en plus petites fonctions pures. 
  
 <WRAP todo round> <WRAP todo round>
 === Points de discussion === === Points de discussion ===
  
-  * Quel est l'impact de l'immuabilité dans cet exemple ? +  * Entre 5.1 et 5.3, qu'avez-vous gagné en testabilité ? Combien de tests faut-il pour la version impérative vs. pour chaque brique ? 
-  * Pouvez-vous voir où la transparence référentielle intervient dans ce code ? +  * Où sont passés les effets de bord ? Pourquoi les repousser à la //périphérie// du programme ("functional core, imperative shell") ? 
-  * Comment la combinaison de ces principes conduit-elle à un code plus fiable et facile à maintenir ? +  * Si un collègue ajoute une règle de remise (ex : Black Friday), quelles fonctions faut-il modifier dans la version impérative ? Dans la version composée ? 
-  * Quelles améliorations peuvent être apportées pour rendre le code plus fonctionnel ?+  * La version fonctionnelle parcourt plusieurs fois le tableau (%%filter%%, %%map%%, %%reduce%%). Est-ce un problème ? Comment un //transducer// ou un évaluateur paresseux y répond-il ? À partir de quelle taille de données cela compte-t-il vraiment ? 
 +  * Dans 5.6 (réducteur), pourquoi l'immutabilité rend-elle possible le "time travel debugging" et l'annulation (undo) ?
 </WRAP> </WRAP>
  • cnam/utc503/td3.1754958903.txt.gz
  • Dernière modification : il y a 15 mois
  • de 127.0.0.1