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.
# 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.
# 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().
# 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
# 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
Ce cours est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International.