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:09] – [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 227: | Ligne 237: | ||
| ==== 3.3 - Rendre transparent ==== | ==== 3.3 - Rendre transparent ==== | ||
| - | * Réécrivez A pour qu'il soit référentiellement transparent (indice : l' | + | * Réécrivez |
| - | * Réécrivez B pour rendre le hasard déterministe : '' | + | * Réécrivez |
| + | |||
| + | <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' | ||
| - | * Écrire | + | * Écrire |
| - | * Appliquer à une fonction coûteuse (ex : '' | + | * Appliquer à une fonction coûteuse (ex : %%fib%% récursif naïf, comparez les temps pour %%fib(35)%%). |
| - | * **Test crucial** : appliquer | + | * **Test crucial** : appliquer |
| + | <WRAP todo round> | ||
| + | === Points de discussion === | ||
| + | |||
| + | * Pourquoi la mémoïsation n' | ||
| + | * 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 // | ||
| + | * Une fonction peut être pure sans que son //appel// soit " | ||
| + | * 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 ? | ||
| + | </ | ||
| ===== Exercice 4 : Fonctions d' | ===== Exercice 4 : Fonctions d' | ||
| Ligne 245: | Ligne 269: | ||
| Sans utiliser les méthodes natives correspondantes, | Sans utiliser les méthodes natives correspondantes, | ||
| - | * '' | + | * %%myMap(arr, fn)%% ; |
| - | * '' | + | * %%myFilter(arr, |
| - | * '' | + | * %%myReduce(arr, |
| - | Puis **réimplémenter | + | Puis **réimplémenter |
| ==== 4.2 - Fonctions qui retournent des fonctions (closures) ==== | ==== 4.2 - Fonctions qui retournent des fonctions (closures) ==== | ||
| - | * '' | + | * %%multiplier(n)%% retourne une fonction qui multiplie son argument par %%n%%. Utiliser pour créer |
| - | * '' | + | * %%greaterThan(n)%% retourne un prédicat. L' |
| - | * '' | + | * %%counter()%% retourne une fonction qui retourne 1, 2, 3... à chaque appel. **Cette fonction est-elle pure ?** Où est stocké l' |
| ==== 4.3 - Currying et application partielle ==== | ==== 4.3 - Currying et application partielle ==== | ||
| - | * Écrire | + | * Écrire |
| - | * Écrire | + | * Écrire |
| - | * Utiliser sur : '' | + | * Utiliser sur les fonctions suivantes |
| - | * Créer | + | |
| + | <sxh javascript> | ||
| + | const add3 = (a, b, c) => a + b + c; | ||
| + | const formatPrice = (currency, decimals, value) => /* à compléter */; | ||
| + | </ | ||
| + | |||
| + | * Créer | ||
| ==== 4.4 - Composition ==== | ==== 4.4 - Composition ==== | ||
| - | * Écrire | + | * Écrire |
| - | * Construire | + | * Construire |
| <sxh javascript> | <sxh javascript> | ||
| Ligne 273: | Ligne 303: | ||
| </ | </ | ||
| - | * Vérifier que '' | + | * Vérifier que %%compose(f, compose(g, h))%% et %%compose(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 : |
| - | * Pourquoi | + | |
| - | * '' | + | <sxh javascript> |
| + | [" | ||
| + | </ | ||
| + | |||
| + | * Expliquer le résultat (arguments passés par %%map%% au callback). Corriger. | ||
| + | * Pourquoi | ||
| + | * %%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 ==== | ==== 4.6 - Pour aller plus loin ==== | ||
| - | * Écrire | + | * Écrire |
| - | * Écrire | + | * Écrire |
| <WRAP todo round> | <WRAP todo round> | ||
| === Points de discussion === | === Points de discussion === | ||
| - | * Dans 4.1, qu' | + | * Dans 4.1, qu' |
| * Dans 4.2, où vit l' | * Dans 4.2, où vit l' | ||
| * 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) ? | * 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 ? |
| </ | </ | ||
| Ligne 325: | Ligne 361: | ||
| * 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/ | ||
| - | * Exécuter deux fois d' | + | * Exécuter deux fois d' |
| ==== 5.2 - Briques élémentaires pures ==== | ==== 5.2 - Briques élémentaires pures ==== | ||
| Ligne 331: | Ligne 367: | ||
| Écrire et tester chacune isolément : | Écrire et tester chacune isolément : | ||
| - | * '' | + | * %%applyDiscount(rate)(product)%% : retourne un **nouveau** produit avec le prix réduit (currying : le taux d' |
| - | * '' | + | * %%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 ==== | ==== 5.3 - Assemblage ==== | ||
| - | * Écrire | + | * Écrire |
| - | * Vérifier : le total attendu est 310.5 pour 10 % de remise sur l' | + | * Vérifier : le total attendu est **337.5** pour 10 % de remise sur l' |
| ==== 5.4 - Extensions métier ==== | ==== 5.4 - Extensions métier ==== | ||
| Ligne 346: | 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, |
| - | * '' | + | |
| - | * '' | + | <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. | ||
| + | * %%receipt(products, | ||
| ==== 5.5 - Analyse de la pureté (partie rédigée) ==== | ==== 5.5 - Analyse de la pureté (partie rédigée) ==== | ||
| Ligne 355: | Ligne 396: | ||
| ^ Fonction ^ Pure ? ^ Mutation ? ^ Transparente ? ^ Remplaçable par sa valeur ? ^ | ^ Fonction ^ Pure ? ^ Mutation ? ^ Transparente ? ^ Remplaçable par sa valeur ? ^ | ||
| - | | '' | + | | %%applyDiscount%% | | | | | |
| - | | '' | + | | %%cartTotal%% | | | | | |
| - | | '' | + | | %%receipt%% | | | | | |
| | fonction d' | | fonction d' | ||
| - | Ensuite, **démontrer** la transparence référentielle de '' | + | 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' |
| ==== 5.6 - Pour aller plus loin ==== | ==== 5.6 - Pour aller plus loin ==== | ||
| - | * Rendre le taux de remise // | + | * Rendre le taux de remise // |
| - | * Écrire | + | * Écrire |
| - | * Implémenter un //panier// comme un **réducteur** : '' | + | * Implémenter un //panier// comme un **réducteur** : %%cartReducer(state, |
| + | |||
| + | <sxh javascript> | ||
| + | { type: " | ||
| + | { type: " | ||
| + | </ | ||
| + | |||
| + | * Rejouez une liste d' | ||
| <WRAP todo round> | <WRAP todo round> | ||
| Ligne 374: | Ligne 423: | ||
| * Où sont passés les effets de bord ? Pourquoi les repousser à la // | * Où sont passés les effets de bord ? Pourquoi les repousser à la // | ||
| * 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 ? | * 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 ? | ||
| - | * La version fonctionnelle parcourt plusieurs fois le tableau ('' | + | * La version fonctionnelle parcourt plusieurs fois le tableau (%%filter%%, %%map%%, %%reduce%%). Est-ce un problème ? Comment un // |
| * Dans 5.6 (réducteur), | * Dans 5.6 (réducteur), | ||
| </ | </ | ||
| - | |||
| - | ===== Barème indicatif ===== | ||
| - | |||
| - | ^ Critère ^ Points ^ | ||
| - | | Exercices 1 à 3 : correction du code et des tests | 6 | | ||
| - | | Exercice 4 : implémentations et compréhension des closures/ | ||
| - | | Exercice 5 : qualité de la décomposition, | ||
| - | | Réponses aux questions de réflexion (justifiées, | ||