Terminale · Langages et programmation

Paradigmes impératif, fonctionnel et objet

Deux programmes peuvent calculer le même résultat tout en racontant leur travail très différemment. L’un décrit des modifications successives, un autre compose des transformations, un troisième fait collaborer des objets. Ces façons d’organiser le raisonnement sont des paradigmes de programmation.

SofienAvec SofienIngénieur et enseignant en informatique
Dans ce chapitre

Un cap pour ce chapitre

Ce que vous saurez faire

  • Reconnaître des caractéristiques impératives, fonctionnelles et objet.
  • Comparer plusieurs solutions d’un problème.
  • Justifier un choix sans déclarer un paradigme universellement supérieur.
Les bases utiles pour commencer

Décrire des changements avec l’impératif

Dans une approche impérative, le programme précise une suite d’instructions qui transforment un état. Un accumulateur initialement nul peut recevoir successivement les carrés des valeurs d’une liste. Les affectations, conditions et boucles rendent explicite l’ordre des opérations.

total = 0
for x in [2, 3, 4]:
    total = total + x * x

Cette forme est naturelle pour suivre une procédure étape par étape. Pour comprendre le résultat, on suit l’évolution de total : zéro, quatre, treize, puis vingt-neuf. Les états intermédiaires sont une aide, mais deviennent difficiles à suivre si beaucoup de variables partagées changent à plusieurs endroits.

Composer des transformations avec le fonctionnel

Une approche fonctionnelle privilégie l’expression de résultats par des fonctions et leur composition. Une fonction pure renvoie un résultat déterminé par ses arguments et n’a pas d’effet de bord observable. Elle est facile à tester isolément : les mêmes arguments donnent le même résultat dans le modèle considéré.

def carre(x):
    return x * x

total = sum(carre(x) for x in [2, 3, 4])

Cette expression met en avant « transformer chaque valeur puis additionner ». Python permet ce style sans être un langage exclusivement fonctionnel. Une fonction qui modifie une liste reçue n’est pas pure simplement parce qu’elle est écrite avec def.

Regrouper état et opérations avec les objets

Le paradigme objet organise des entités possédant des attributs et des méthodes. Un compteur peut conserver une valeur et proposer une méthode incrementer. Plusieurs instances partagent les mêmes règles tout en conservant des états distincts. Cette organisation convient notamment à la modélisation d’entités qui évoluent et interagissent.

Pour reprendre le même calcul de somme de carrés, un objet peut conserver le total et proposer une opération d’ajout. Chaque instance commence avec son propre total nul.

class SommeCarres:
    def __init__(self):
        self.total = 0

    def ajouter(self, valeur):
        self.total += valeur * valeur

calcul = SommeCarres()
for valeur in [2, 3, 4]:
    calcul.ajouter(valeur)
print(calcul.total)  # 29

Un objet n’est pas seulement un dictionnaire déguisé : son interface peut exprimer les opérations autorisées et préserver des règles. Pour une pile, empiler et dépiler définissent un usage cohérent. En NSI, classes, attributs, objets et méthodes suffisent à travailler cette approche ; héritage et polymorphisme ne sont pas des exigences de cette introduction.

Choisir en fonction de la tâche

Une petite boucle de recherche peut être très claire en impératif. Une chaîne de transformations de données peut se lire naturellement en style fonctionnel. Une simulation de véhicules, de capteurs ou de joueurs peut bénéficier d’objets représentant les entités. Ces exemples donnent des raisons de choisir, pas des règles absolues.

Un même programme peut combiner plusieurs paradigmes : un objet expose une méthode dont le corps utilise une boucle et appelle une fonction pure. Le langage autorise des styles ; il ne suffit pas de connaître son nom pour classer chaque programme. Pour justifier une réponse, citez des caractéristiques observables plutôt qu’une étiquette isolée.

Choisir un style implique aussi de préciser ce qui change et ce qui reste observable. Pour comparer deux versions, fixez la même entrée, le même résultat attendu et les mêmes obligations sur la mutation. Une fonction qui renvoie la bonne somme mais efface l’entrée ne respecte pas forcément le même contrat.

Exemple suivi : trois organisations pour le même traitement

On veut totaliser les carrés des seules valeurs positives de [-2,3,0,4]. En impératif, un accumulateur part de zéro et reçoit le carré de chaque valeur vérifiant x > 0. Il devient 9 puis 25. En style fonctionnel, on exprime la transformation puis l’agrégation avec sum(x*x for x in valeurs if x > 0). Les valeurs négatives et zéro sont filtrées ; les carrés restants sont additionnés.

En objet, une instance peut conserver un total et proposer une méthode qui ajoute une mesure positive après l’avoir mise au carré. Ce choix devient utile si les mesures arrivent successivement et si plusieurs calculs indépendants coexistent. Pour un calcul unique de quatre nombres, la classe ajoute des éléments à comprendre ; elle ne garantit pas une meilleure solution. Comparez la correspondance avec le problème, l’interface, les effets observables et la facilité des tests.

OrganisationInformation mise en avantÉtat conservé
ImpérativeAccumuler au fil du parcoursTotal local
FonctionnelleFiltrer, transformer, agrégerRésultat calculé depuis les arguments
ObjetAjouter des mesures à une instanceTotal propre à chaque instance

Exemple suivi : repérer un état caché

Une fonction reçoit x, ajoute x à une liste globale puis renvoie la somme de cette liste. Avec une liste globale vide, l’appel sur 2 renvoie 2 ; le second appel sur 2 renvoie 4. Le résultat dépend donc de l’histoire du programme. Une fonction carre(x) renvoyant x*x conserve au contraire le même résultat pour les mêmes arguments et ne modifie aucun état extérieur.

Pour rendre le premier traitement explicite, on peut recevoir une liste en paramètre et renvoyer une nouvelle liste contenant l’élément ajouté. L’appelant choisit ensuite de conserver ou non ce nouvel état. On peut aussi encapsuler l’historique dans un objet dont l’interface annonce la mutation. Les deux organisations rendent le changement identifiable, mais seule la première peut être pure si elle ne modifie pas l’entrée. La présence de def, d’une compréhension ou d’un objet ne suffit pas à conclure sans examiner les effets.

À vous de faire varier les choses

Reconnaissez le choix de conception

Classez la caractéristique dominante décrite, sans supposer que les autres paradigmes sont interdits dans le programme.

Lire les associations expliquées
Un accumulateur est modifié à chaque tour de boucle. : Impératif
Le raisonnement suit une succession de changements d’état.
Une fonction pure transforme une valeur sans modifier son environnement. : Fonctionnel
La transformation est décrite par ses arguments et son résultat.
Chaque joueur est une instance avec son score et ses méthodes. : Objet
Les états et opérations sont regroupés par entité.
Une suite de fonctions transforme un tableau puis agrège les résultats. : Fonctionnel
La composition des transformations domine cette description.
Des instructions déplacent une case active en modifiant ses coordonnées. : Impératif
La procédure est décrite par modifications successives.
La classe Pile propose empiler et dépiler pour plusieurs instances. : Objet
Une interface et des états sont portés par des objets.
Une simulation crée deux capteurs dont les historiques évoluent séparément au moyen des mêmes méthodes. : Objet
La classe porte les règles communes ; chaque instance conserve son état. Les méthodes peuvent contenir des boucles.
Une fonction reçoit les mesures et renvoie leur moyenne sans modifier l’entrée ni lire une variable globale. : Fonctionnel
Le résultat dépend des arguments et aucun effet extérieur n’est produit. Ce contrat permet un test isolé.
Un curseur avance dans une grille et actualise ses deux coordonnées à chaque instruction. : Impératif
La description suit l’évolution explicite d’un état. L’existence possible d’un objet grille ne change pas la caractéristique décrite.

Justifiez une classification par les caractéristiques du code, et acceptez les combinaisons de styles.

De la compréhension à l’autonomie

À vous de résoudre

Cherchez d’abord par vous-même. Vérifiez les résultats demandés, utilisez les indices si nécessaire, puis comparez votre méthode à la correction.

Exercice 1 · Comprendre#

Retrouver l’état modifié

Dans la boucle de somme des carrés, quelle variable matérialise l’accumulation et quelles valeurs successives prend-elle ?

Indice 1

Commencez avant la première itération.

Indice 2

Ajoutez successivement 4, 9 et 16.

Comprendre la correction

La variable total vaut 0, puis 4, puis 13, puis 29. Le programme impératif décrit les modifications successives de cet accumulateur. La valeur x change aussi au fil du parcours, mais ne conserve pas le résultat cumulé.

Exercice 2 · Analyser#

Reconnaître une fonction pure

Comparez f(x)=x*x et une fonction qui ajoute x à une liste globale puis renvoie sa longueur. Laquelle respecte la pureté décrite ici ?

Indice 1

Une donnée extérieure est-elle modifiée ?

Indice 2

Le résultat dépend-il seulement de x ?

Comprendre la correction

La fonction carré respecte cette pureté. L’autre modifie une liste globale et son résultat dépend de son contenu antérieur. Deux appels avec le même x peuvent donc renvoyer des longueurs différentes. Le mot fonction ne suffit pas à garantir une approche pure.

Exercice 3 · Concevoir#

Choisir une organisation

Une simulation comporte vingt capteurs, chacun avec une mesure et une opération de mise à jour. Pourquoi une classe Capteur peut-elle être utile ?

Indice 1

Distinguez règles communes et états individuels.

Indice 2

Pensez à l’interface proposée à la simulation.

Comprendre la correction

La classe regroupe les règles de mise à jour et l’interface commune ; chaque instance conserve sa propre mesure. La simulation peut manipuler plusieurs capteurs avec les mêmes opérations sans mélanger leurs états. Une autre représentation reste possible, mais cet argument justifie ici le choix objet.

Exercice 4 · Justifier#

Corriger une classification trop rapide

Un élève affirme qu’une méthode contenant une boucle for prouve que le programme ne peut pas être objet. Que lui répondez-vous ?

Indice 1

Le paradigme décrit une organisation à plusieurs niveaux.

Indice 2

Le corps d’une méthode doit tout de même réaliser des opérations.

Comprendre la correction

Une méthode peut utiliser des instructions impératives tout en appartenant à une organisation objet. Les paradigmes se combinent. Il faut examiner le rôle des objets, attributs et interfaces dans l’ensemble du programme plutôt que classer tout le projet d’après une seule boucle.

Exercice 5 · Problème de synthèse#

Problème : comparer deux contrats

A reçoit une liste et renvoie la somme des carrés positifs sans mutation. B calcule la même somme mais supprime auparavant les valeurs négatives dans la liste reçue. Sur [-2,3,0,4], donnez le résultat des deux et les contenus conservés par l’appelant. Les fonctions sont-elles interchangeables selon le contrat de A ?

Indice 1

Le résultat numérique ne décrit pas tous les effets.

Indice 2

Une autre partie du programme peut encore utiliser les valeurs négatives.

Comprendre la correction

Les deux renvoient 25. Après A, l’appelant conserve [-2,3,0,4] ; après B, il voit [3,0,4]. B viole l’absence de mutation exigée par A. Les deux traitements ne sont donc pas interchangeables selon ce contrat, même si un test vérifiant seulement le nombre retourné les jugerait identiques.

Exercice 6 · Problème de synthèse#

Problème : séparer deux instances

Deux objets SommeCarres, a et b, commencent avec total=0. On appelle successivement a.ajouter(2), b.ajouter(3), a.ajouter(4). Donnez les deux totaux. Pourquoi placer un même total global derrière les deux objets serait-il contraire à l’intention ? Proposez un test distinguant les versions.

Indice 1

Chaque instance conserve son propre attribut.

Indice 2

Les carrés de 2 et 4 vont uniquement dans a.

Comprendre la correction

Les totaux attendus sont 20 pour a et 9 pour b. Un total global confondrait les deux historiques et atteindrait 29 pour les deux lectures si les méthodes y accédaient. Le scénario proposé constitue déjà un test : il vérifie les deux attributs après des appels entrelacés. Une classe utile regroupe des règles communes avec des états distincts.

Exercice 7 · Problème de synthèse#

Problème : justifier une architecture mixte

Une simulation possède plusieurs capteurs, chacun avec son historique. Un calcul transforme une liste de mesures en moyenne, et un programme principal répète les acquisitions. Proposez une place pour les objets, les fonctions pures et les boucles. Donnez un exemple de test indépendant du temps réel.

Indice 1

Associez l’état durable aux entités.

Indice 2

Le calcul d’un indicateur peut recevoir des données déjà connues.

Comprendre la correction

Des objets peuvent porter les historiques des capteurs ; une fonction pure calcule un indicateur sur une liste sans mutation ; une boucle organise les acquisitions. Pour tester la moyenne, on peut fournir [2,4,9] et attendre 5 sans capteur réel. L’ensemble combine des paradigmes à différents niveaux. La justification porte sur les responsabilités et les dépendances, pas sur l’obligation d’un style unique.

Les erreurs qui méritent un détour

Assimiler toute fonction à la programmation fonctionnelle.
Une fonction peut modifier des états et être utilisée dans tous les styles.
Chercher un paradigme meilleur dans tous les cas.
La structure du problème et la lisibilité motivent le choix.

La fiche à garder

L’essentiel à retenir

  • L’impératif décrit des changements d’état.
  • Le fonctionnel privilégie des transformations et leur composition.
  • L’objet regroupe état et opérations dans des entités.

Le prochain pas

Retrouver le catalogue de Terminale

Ce chapitre s’appuie sur le programme officiel de Terminale (PDF, nouvel onglet). Les explications et exercices sont proposés pour l’apprentissage.