Différences
Ci-dessous, les différences entre deux révisions de la page.
| Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
| cnam:utc503:td3 [2026/10/20 02:16] – [3.3 - Rendre transparent] jcheron | cnam:utc503:td3 [2026/10/20 09:17] (Version actuelle) – jcheron | ||
|---|---|---|---|
| Ligne 1: | Ligne 1: | ||
| - | ====== TD 3 : Fondamentaux de la programmation fonctionnelle | + | ====== TD 3 : Pf en JS ====== |
| <WRAP info round> | <WRAP info round> | ||
| Ligne 21: | Ligne 21: | ||
| | '' | | '' | ||
| - | Gardez ce tableau sous les yeux : '' | + | '' |
| </ | </ | ||
| Ligne 30: | Ligne 30: | ||
| ==== 1.1 - Écrire une fonction pure ==== | ==== 1.1 - Écrire une fonction pure ==== | ||
| - | * Écrire '' | + | * Écrire '' |
| * Écrire les tests suivants : | * Écrire les tests suivants : | ||
| Ligne 76: | Ligne 76: | ||
| </ | </ | ||
| - | <WRAP tip round> | ||
| - | **Indice pour D** : une fonction pure ne peut pas "aller chercher" | ||
| - | </ | ||
| ==== 1.4 - Pour aller plus loin : l' | ==== 1.4 - Pour aller plus loin : l' | ||
| Ligne 186: | Ligne 183: | ||
| ===== Exercice 3 : Transparence référentielle ===== | ===== Exercice 3 : Transparence référentielle ===== | ||
| - | **Définition | + | **Définition** : une expression est référentiellement transparente si on peut la remplacer par sa valeur sans changer le comportement du programme. |
| - | ==== 3.1 - Substitution | + | ==== 3.1 - Substitution |
| - | Soit : | + | * Écrire %%add(a, b)%% et %%mul(a, b)%%. |
| + | * Soit l' | ||
| + | |||
| + | <sxh javascript> | ||
| + | add(mul(2, 3), mul(2, 3)) | ||
| + | </ | ||
| + | |||
| + | * 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> | <sxh javascript> | ||
| - | const add = (a, b) => a + b; | + | const x = mul(2, 3); |
| - | const mul = (a, b) => a * b; | + | add(x, x) |
| - | const result = add(mul(2, 3), mul(4, 5)) + add(mul(2, 3), 1); | + | |
| </ | </ | ||
| - | * Faire l' | + | * Pourquoi cette factorisation est-elle sûre ? |
| - | * Identifier les sous-expressions répétées. Que pouvez-vous en faire (extraire dans une constante) ? Pourquoi cette factorisation est-elle sûre ici ? | + | |
| - | ==== 3.2 - Substitution qui casse ==== | + | ==== 3.2 - Détecter les ruptures de transparence |
| - | Pour chaque extrait, remplacez l' | + | 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> | <sxh javascript> | ||
| Ligne 209: | Ligne 212: | ||
| let counter = 0; | let counter = 0; | ||
| const next = () => ++counter; | const next = () => ++counter; | ||
| - | const x = next() + next(); | ||
| // B | // B | ||
| const rand = () => Math.random(); | const rand = () => Math.random(); | ||
| - | const y = rand() === rand(); | ||
| // C | // C | ||
| - | const log = (s) => { console.log(s); return s; }; | + | const now = () => Date.now(); |
| - | const z = log(" | + | |
| - | // D | + | // D (TAX est modifiable ailleurs dans le programme) |
| - | const getUser | + | let TAX = 0.2; |
| + | const addTax | ||
| + | |||
| + | // E | ||
| + | const first = (arr) => arr[0]; | ||
| + | |||
| + | // F | ||
| + | const logAndAdd = (a, b) => { | ||
| + | console.log(a); | ||
| + | return a + b; | ||
| + | }; | ||
| </ | </ | ||
| Ligne 228: | Ligne 238: | ||
| * Réécrivez **A** pour qu'il soit référentiellement transparent (indice : l' | * Réécrivez **A** pour qu'il soit référentiellement transparent (indice : l' | ||
| - | * Réécrivez **B** pour rendre le hasard déterministe : %%random(seed)%% retourne un couple %%[valeur, nouvelleGraine]%% (générateur congruentiel linéaire simple, par ex. %%(seed * 1664525 + 1013904223) % 2**32%%). Montrez que %%random(42)%% donne toujours le même résultat. | + | * 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 | ||
| + | </ | ||
| + | |||
| + | * Montrez que %%random(42)%% donne toujours le même résultat. | ||
| ==== 3.4 - Mémoïsation : l' | ==== 3.4 - Mémoïsation : l' | ||
| Ligne 269: | Ligne 285: | ||
| * Écrire %%curry(fn)%% qui transforme %%f(a, b, c)%% en %%f(a)(b)(c)%% (à arité fixe, via %%fn.length%%). | * Écrire %%curry(fn)%% qui transforme %%f(a, b, c)%% en %%f(a)(b)(c)%% (à arité fixe, via %%fn.length%%). | ||
| * Écrire %%partial(fn, | * Écrire %%partial(fn, | ||
| - | * Utiliser sur %%const add3 = (a, b, c) => a + b + c;%% et sur %%const formatPrice = (currency, decimals, value) => ...%%. | + | * Utiliser sur les fonctions suivantes : |
| + | |||
| + | <sxh javascript> | ||
| + | const add3 = (a, b, c) => a + b + c; | ||
| + | const formatPrice = (currency, decimals, value) => /* à compléter */; | ||
| + | </ | ||
| * Créer %%formatEUR%% et %%formatUSD2%% à partir de %%formatPrice%%. | * Créer %%formatEUR%% et %%formatUSD2%% à partir de %%formatPrice%%. | ||
| Ligne 276: | Ligne 298: | ||
| * Écrire %%compose(...fns)%% (exécution de droite à gauche) et %%pipe(...fns)%% (gauche à droite). | * Écrire %%compose(...fns)%% (exécution de droite à gauche) et %%pipe(...fns)%% (gauche à droite). | ||
| * Construire %%slugify%% en composant : %%trim%%, %%toLowerCase%%, | * Construire %%slugify%% en composant : %%trim%%, %%toLowerCase%%, | ||
| - | * Test à satisfaire : %%console.assert(slugify(" | + | |
| + | <sxh javascript> | ||
| + | console.assert(slugify(" | ||
| + | </ | ||
| * Vérifier que %%compose(f, | * Vérifier que %%compose(f, | ||
| ==== 4.5 - Pièges des méthodes natives ==== | ==== 4.5 - Pièges des méthodes natives ==== | ||
| - | * Prédire puis exécuter : %%[" | + | * Prédire puis exécuter : |
| + | |||
| + | <sxh javascript> | ||
| + | [" | ||
| + | </ | ||
| + | |||
| + | * Expliquer le résultat (arguments passés par %%map%% au callback). Corriger. | ||
| * Pourquoi %%forEach%% est-il // | * Pourquoi %%forEach%% est-il // | ||
| * %%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) ? | * %%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) ? | ||
| Ligne 303: | Ligne 335: | ||
| **Contexte** : vous développez le calcul d'un panier pour un site e-commerce. Toutes les fonctions doivent être **pures** et **sans mutation**. | **Contexte** : vous développez le calcul d'un panier pour un site e-commerce. Toutes les fonctions doivent être **pures** et **sans mutation**. | ||
| - | Jeu de données de départ : | + | <sxh javascript> |
| - | + | const products = [ | |
| - | ^ id ^ name ^ price ^ category ^ stock ^ | + | |
| - | | 1 | Clavier | + | { id: 2, name: "Souris", |
| - | | 2 | Souris | + | { id: 3, name: "Livre JS", price: |
| - | | 3 | Livre JS | 40 | book | 12 | | + | { id: 4, name: "Cahier", |
| - | | 4 | Cahier | + | { id: 5, name: " |
| - | | 5 | Écran | 250 | tech | 2 | | + | ]; |
| - | + | </ | |
| - | À transcrire en JS sous la forme d'un tableau d' | + | |
| ==== 5.1 - Version impérative (point de départ) ==== | ==== 5.1 - Version impérative (point de départ) ==== | ||
| - | On vous donne ce code, à recopier et exécuter | + | On vous donne ce code : |
| - | * %%let total = 0;%% | + | <sxh javascript> |
| - | * %%for (let i = 0; i < products.length; | + | let total = 0; |
| - | * %% if (products[i].stock > 0) {%% | + | for (let i = 0; i < products.length; |
| - | * %% | + | if (products[i].stock > 0) { |
| - | * %% total += products[i].price; | + | products[i].price = products[i].price * 0.9; |
| - | * %% }%% | + | total += products[i].price; |
| - | * %%}%% | + | } |
| - | * %%console.log(" | + | } |
| - | + | console.log(" | |
| - | Questions : | + | </ |
| * Lister **tous** les problèmes (au moins 4 : mutation, effet de bord, mélange calcul/ | * Lister **tous** les problèmes (au moins 4 : mutation, effet de bord, mélange calcul/ | ||
| Ligne 351: | Ligne 382: | ||
| Chaque extension doit **réutiliser** les briques existantes, pas dupliquer du code : | Chaque extension doit **réutiliser** les briques existantes, pas dupliquer du code : | ||
| - | * %%totalByCategory(products, | + | * %%totalByCategory(products, |
| + | |||
| + | <sxh javascript> | ||
| + | { tech: ..., book: ..., office: ... } | ||
| + | </ | ||
| * %%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. | * %%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, | * %%receipt(products, | ||
| Ligne 371: | Ligne 407: | ||
| * Rendre le taux de remise // | * Rendre le taux de remise // | ||
| * Écrire %%updateStock(products, | * Écrire %%updateStock(products, | ||
| - | * Implémenter un //panier// comme un **réducteur** : %%cartReducer(state, | + | * Implémenter un //panier// comme un **réducteur** : %%cartReducer(state, |
| + | |||
| + | <sxh javascript> | ||
| + | { type: " | ||
| + | { type: " | ||
| + | </ | ||
| + | |||
| + | * Rejouez une liste d' | ||
| <WRAP todo round> | <WRAP todo round> | ||