321 lines
8.4 KiB
Markdown
321 lines
8.4 KiB
Markdown
|
|
# Corrigé de l'exercice — Cuisson des pâtes
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Énoncé
|
||
|
|
|
||
|
|
Nous voulons faire cuire des pâtes. Écrire la recette sous forme de code Python selon trois paradigmes différents.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Version impérative
|
||
|
|
|
||
|
|
Liste d'instructions étape par étape, avec modification d'état.
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Version impérative : liste d'instructions étape par étape
|
||
|
|
|
||
|
|
def cuire_pates_imperatif():
|
||
|
|
"""Fait cuire des pâtes de manière impérative."""
|
||
|
|
|
||
|
|
# État initial
|
||
|
|
eau = "froide"
|
||
|
|
pates = "crues"
|
||
|
|
sel = False
|
||
|
|
|
||
|
|
# Étape 1 : Remplir la casserole
|
||
|
|
print("Remplissage de la casserole...")
|
||
|
|
quantite_eau = 1.5 # litres
|
||
|
|
|
||
|
|
# Étape 2 : Faire chauffer l'eau
|
||
|
|
print("Chauffage de l'eau...")
|
||
|
|
eau = "chaude"
|
||
|
|
|
||
|
|
# Étape 3 : Attendre l'ébullition
|
||
|
|
print("Attente de l'ébullition...")
|
||
|
|
eau = "bouillante"
|
||
|
|
|
||
|
|
# Étape 4 : Saler l'eau
|
||
|
|
print("Ajout du sel...")
|
||
|
|
sel = True
|
||
|
|
|
||
|
|
# Étape 5 : Ajouter les pâtes
|
||
|
|
print("Ajout des pâtes...")
|
||
|
|
pates = "en cuisson"
|
||
|
|
|
||
|
|
# Étape 6 : Attendre le temps de cuisson
|
||
|
|
temps_cuisson = 10 # minutes
|
||
|
|
print(f"Cuisson pendant {temps_cuisson} minutes...")
|
||
|
|
|
||
|
|
# Étape 7 : Égoutter
|
||
|
|
print("Égouttage des pâtes...")
|
||
|
|
pates = "cuites"
|
||
|
|
|
||
|
|
print(f"Résultat : pâtes {pates}")
|
||
|
|
return pates
|
||
|
|
|
||
|
|
|
||
|
|
# Exécution
|
||
|
|
resultat = cuire_pates_imperatif()
|
||
|
|
```
|
||
|
|
|
||
|
|
**Caractéristiques :**
|
||
|
|
- Modification d'état (variables `eau`, `pates`, `sel`)
|
||
|
|
- Instructions séquentielles
|
||
|
|
- Effets de bord (print)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Version fonctionnelle
|
||
|
|
|
||
|
|
Une fonction pure qui prend un état et retourne un nouvel état, sans modification.
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Version fonctionnelle : fonctions pures et composition
|
||
|
|
|
||
|
|
def remplir_casserole(etat):
|
||
|
|
"""Retourne un nouvel état avec la casserole remplie."""
|
||
|
|
return {**etat, "eau": "froide", "quantite_eau": 1.5}
|
||
|
|
|
||
|
|
|
||
|
|
def chauffer_eau(etat):
|
||
|
|
"""Retourne un nouvel état avec l'eau chauffée."""
|
||
|
|
return {**etat, "eau": "chaude"}
|
||
|
|
|
||
|
|
|
||
|
|
def faire_bouillir(etat):
|
||
|
|
"""Retourne un nouvel état avec l'eau bouillante."""
|
||
|
|
return {**etat, "eau": "bouillante"}
|
||
|
|
|
||
|
|
|
||
|
|
def saler(etat):
|
||
|
|
"""Retourne un nouvel état avec le sel ajouté."""
|
||
|
|
return {**etat, "sel": True}
|
||
|
|
|
||
|
|
|
||
|
|
def ajouter_pates(etat):
|
||
|
|
"""Retourne un nouvel état avec les pâtes en cuisson."""
|
||
|
|
return {**etat, "pates": "en cuisson"}
|
||
|
|
|
||
|
|
|
||
|
|
def cuire(etat, duree):
|
||
|
|
"""Retourne un nouvel état après cuisson."""
|
||
|
|
return {**etat, "temps_cuisson": duree, "pates": "cuites"}
|
||
|
|
|
||
|
|
|
||
|
|
def egoutter(etat):
|
||
|
|
"""Retourne un nouvel état avec les pâtes égouttées."""
|
||
|
|
return {**etat, "egouttees": True}
|
||
|
|
|
||
|
|
|
||
|
|
# Composition de fonctions
|
||
|
|
def cuire_pates_fonctionnel(etat_initial):
|
||
|
|
"""
|
||
|
|
Fait cuire des pâtes de manière fonctionnelle.
|
||
|
|
Chaque étape retourne un nouvel état sans modifier l'ancien.
|
||
|
|
"""
|
||
|
|
etat = remplir_casserole(etat_initial)
|
||
|
|
etat = chauffer_eau(etat)
|
||
|
|
etat = faire_bouillir(etat)
|
||
|
|
etat = saler(etat)
|
||
|
|
etat = ajouter_pates(etat)
|
||
|
|
etat = cuire(etat, 10)
|
||
|
|
etat = egoutter(etat)
|
||
|
|
return etat
|
||
|
|
|
||
|
|
|
||
|
|
# Version encore plus fonctionnelle avec reduce
|
||
|
|
import functools
|
||
|
|
|
||
|
|
def cuire_pates_reduce(etat_initial):
|
||
|
|
"""Version avec reduce pour composer les fonctions."""
|
||
|
|
etapes = [
|
||
|
|
remplir_casserole,
|
||
|
|
chauffer_eau,
|
||
|
|
faire_bouillir,
|
||
|
|
saler,
|
||
|
|
ajouter_pates,
|
||
|
|
lambda e: cuire(e, 10),
|
||
|
|
egoutter
|
||
|
|
]
|
||
|
|
return functools.reduce(lambda etat, f: f(etat), etapes, etat_initial)
|
||
|
|
|
||
|
|
|
||
|
|
# Exécution
|
||
|
|
etat_initial = {"pates": "crues", "sel": False}
|
||
|
|
resultat = cuire_pates_fonctionnel(etat_initial)
|
||
|
|
print(f"État final : {resultat}")
|
||
|
|
|
||
|
|
# Vérification : l'état initial n'a pas été modifié
|
||
|
|
print(f"État initial préservé : {etat_initial}")
|
||
|
|
```
|
||
|
|
|
||
|
|
**Caractéristiques :**
|
||
|
|
- Pas de modification d'état (immutabilité)
|
||
|
|
- Fonctions pures (même entrée → même sortie)
|
||
|
|
- Composition de fonctions
|
||
|
|
- L'état initial n'est jamais modifié
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Version orientée objet
|
||
|
|
|
||
|
|
Une classe `Pates` avec des attributs et une méthode `cuire()`.
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Version orientée objet : classe Pates avec méthode cuire()
|
||
|
|
|
||
|
|
class Casserole:
|
||
|
|
"""Représente une casserole pour la cuisson."""
|
||
|
|
|
||
|
|
def __init__(self):
|
||
|
|
self.eau = None
|
||
|
|
self.temperature = "froide"
|
||
|
|
self.sel = False
|
||
|
|
|
||
|
|
def remplir(self, quantite=1.5):
|
||
|
|
"""Remplit la casserole d'eau."""
|
||
|
|
self.eau = quantite
|
||
|
|
print(f"Casserole remplie avec {quantite}L d'eau")
|
||
|
|
|
||
|
|
def chauffer(self):
|
||
|
|
"""Chauffe l'eau jusqu'à ébullition."""
|
||
|
|
self.temperature = "chaude"
|
||
|
|
print("Chauffage en cours...")
|
||
|
|
self.temperature = "bouillante"
|
||
|
|
print("L'eau bout !")
|
||
|
|
|
||
|
|
def saler(self):
|
||
|
|
"""Ajoute du sel dans l'eau."""
|
||
|
|
self.sel = True
|
||
|
|
print("Sel ajouté")
|
||
|
|
|
||
|
|
|
||
|
|
class Pates:
|
||
|
|
"""Représente des pâtes à cuire."""
|
||
|
|
|
||
|
|
def __init__(self, type_pates="spaghetti", quantite=500):
|
||
|
|
self.type = type_pates
|
||
|
|
self.quantite = quantite # en grammes
|
||
|
|
self.etat = "crues"
|
||
|
|
self.temps_cuisson = 0
|
||
|
|
|
||
|
|
def cuire(self, casserole, duree=10):
|
||
|
|
"""
|
||
|
|
Fait cuire les pâtes dans une casserole.
|
||
|
|
|
||
|
|
:param casserole: (Casserole) la casserole à utiliser
|
||
|
|
:param duree: (int) temps de cuisson en minutes
|
||
|
|
"""
|
||
|
|
# Vérifications
|
||
|
|
if casserole.eau is None:
|
||
|
|
raise ValueError("La casserole doit contenir de l'eau !")
|
||
|
|
if casserole.temperature != "bouillante":
|
||
|
|
raise ValueError("L'eau doit être bouillante !")
|
||
|
|
|
||
|
|
print(f"Ajout de {self.quantite}g de {self.type} dans la casserole")
|
||
|
|
self.etat = "en cuisson"
|
||
|
|
|
||
|
|
print(f"Cuisson pendant {duree} minutes...")
|
||
|
|
self.temps_cuisson = duree
|
||
|
|
|
||
|
|
self.etat = "cuites"
|
||
|
|
print(f"Les {self.type} sont cuites !")
|
||
|
|
|
||
|
|
def egoutter(self):
|
||
|
|
"""Égoutte les pâtes."""
|
||
|
|
if self.etat != "cuites":
|
||
|
|
raise ValueError("Les pâtes doivent être cuites avant d'être égouttées !")
|
||
|
|
print("Égouttage des pâtes...")
|
||
|
|
self.etat = "prêtes"
|
||
|
|
|
||
|
|
def __str__(self):
|
||
|
|
return f"Pates({self.type}, {self.quantite}g, état={self.etat})"
|
||
|
|
|
||
|
|
|
||
|
|
# Exécution
|
||
|
|
def preparer_pates_oo():
|
||
|
|
"""Prépare des pâtes en utilisant la POO."""
|
||
|
|
|
||
|
|
# Création des objets
|
||
|
|
casserole = Casserole()
|
||
|
|
pates = Pates("fusilli", 400)
|
||
|
|
|
||
|
|
print(f"Début : {pates}")
|
||
|
|
|
||
|
|
# Préparation de la casserole
|
||
|
|
casserole.remplir(2.0)
|
||
|
|
casserole.chauffer()
|
||
|
|
casserole.saler()
|
||
|
|
|
||
|
|
# Cuisson des pâtes
|
||
|
|
pates.cuire(casserole, duree=12)
|
||
|
|
pates.egoutter()
|
||
|
|
|
||
|
|
print(f"Fin : {pates}")
|
||
|
|
return pates
|
||
|
|
|
||
|
|
|
||
|
|
resultat = preparer_pates_oo()
|
||
|
|
```
|
||
|
|
|
||
|
|
**Caractéristiques :**
|
||
|
|
- Encapsulation (attributs et méthodes regroupés)
|
||
|
|
- Objets avec état interne
|
||
|
|
- Interactions entre objets (Pates et Casserole)
|
||
|
|
- Méthodes qui modifient l'état de l'objet
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Tableau comparatif
|
||
|
|
|
||
|
|
| Aspect | Impératif | Fonctionnel | Orienté Objet |
|
||
|
|
|--------|-----------|-------------|---------------|
|
||
|
|
| **État** | Variables modifiées | États immuables | Attributs d'objets |
|
||
|
|
| **Données** | Variables simples | Dictionnaires/tuples | Objets |
|
||
|
|
| **Actions** | Instructions séquentielles | Fonctions composées | Méthodes |
|
||
|
|
| **Modification** | Sur place | Nouvel état retourné | Via méthodes |
|
||
|
|
| **Réutilisabilité** | Faible | Moyenne | Forte |
|
||
|
|
| **Testabilité** | Difficile | Facile | Moyenne |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Bonus : Version lambda
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Version ultra-fonctionnelle avec lambdas et reduce
|
||
|
|
|
||
|
|
import functools
|
||
|
|
|
||
|
|
# Chaque étape est une lambda qui transforme l'état
|
||
|
|
etapes = [
|
||
|
|
lambda e: {**e, "eau": 1.5},
|
||
|
|
lambda e: {**e, "temperature": "bouillante"},
|
||
|
|
lambda e: {**e, "sel": True},
|
||
|
|
lambda e: {**e, "pates": "en cuisson"},
|
||
|
|
lambda e: {**e, "temps": 10, "pates": "cuites"},
|
||
|
|
lambda e: {**e, "egouttees": True}
|
||
|
|
]
|
||
|
|
|
||
|
|
# Composition avec reduce
|
||
|
|
cuire_pates_lambda = lambda etat: functools.reduce(
|
||
|
|
lambda e, f: f(e),
|
||
|
|
etapes,
|
||
|
|
etat
|
||
|
|
)
|
||
|
|
|
||
|
|
# Exécution
|
||
|
|
resultat = cuire_pates_lambda({"pates": "crues"})
|
||
|
|
print(resultat)
|
||
|
|
# {'pates': 'cuites', 'eau': 1.5, 'temperature': 'bouillante',
|
||
|
|
# 'sel': True, 'temps': 10, 'egouttees': True}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
Auteurs : Florian Mathieu, Enzo Frémeaux, Thimothée Decooster
|
||
|
|
|
||
|
|
Licence CC BY NC
|
||
|
|
|
||
|
|
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.
|