Aller au contenu

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

Licence Creative Commons
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.