Chapitre 522 minDébutant
Trace d'exécution, jeux d'essai et débogage
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- dresser le tableau de trace d'un algorithme et en déduire son rôle ;
- construire un jeu d'essai avec des cas normaux et des cas limites ;
- automatiser tes tests avec
assert; - lire les erreurs de Python et déboguer avec méthode.
6 exercices corrigés
La trace d'exécution d'un algorithme est le tableau qui note, instruction après instruction ou tour de boucle après tour de boucle, la valeur de chaque variable ; le jeu d'essai est la liste des cas de test, avec pour chacun le résultat attendu. Ce chapitre montre comment faire une trace à la main, comment retrouver le rôle d'un algorithme inconnu, comment tester un programme Python et comment chercher une erreur méthodiquement.
Pourquoi faire une trace ?
Faire la trace, c'est jouer le rôle de la machine : exécuter l'algorithme à la main, sans rien sauter, en notant ce que contient chaque variable. C'est l'exercice le plus fréquent d'algorithmique, pour trois raisons :
- c'est la seule façon de comprendre un algorithme que tu n'as pas écrit, et le programme du BTS demande de savoir « reconstituer le rôle d'un algorithme » ;
- c'est la meilleure façon de vérifier le tien avant de le taper, puisqu'une partie de l'épreuve se fait sur table ;
- c'est la meilleure façon de trouver une erreur : on voit le tour où une variable prend une valeur inattendue.
La méthode du tableau de trace
- Une colonne par variable qui change, plus une colonne pour le tour de boucle ou l'instruction, et éventuellement une pour la condition testée.
- Une première ligne « avant » avec les valeurs initiales. Une variable pas encore affectée vaut
?. - Une ligne par tour (ou par instruction s'il n'y a pas de boucle). Recopie ce qui ne change pas, recalcule ce qui change, et note le calcul entre parenthèses si besoin.
- Teste la condition de boucle avant d'écrire une nouvelle ligne : c'est ce qui évite le tour de trop.
- Conclus par une phrase : « l'algorithme affiche… », puis, si c'est demandé, « il calcule… ».
Exemple 1 : une boucle et une condition
tab ← [12, 7, 18, 4, 11]
res ← 0
Pour i de 0 à 4 Faire
Si tab[i] MOD 2 = 0 Alors
res ← res + tab[i]
FinSi
FinPour
Afficher(res)
| i | tab[i] | tab[i] MOD 2 = 0 ? | res |
|---|---|---|---|
| avant | 0 | ||
| 0 | 12 | oui | 12 |
| 1 | 7 | non | 12 |
| 2 | 18 | oui | 30 |
| 3 | 4 | oui | 34 |
| 4 | 11 | non | 34 |
L'algorithme affiche 34. Reformulé : il calcule la somme des éléments pairs du tableau (12 + 18 + 4). C'est cette phrase de conclusion que le correcteur attend quand on te demande « que fait cet algorithme ? » : pas « il affiche 34 », qui ne vaut que pour ces données.
Exemple 2 : un TantQue à deux variables
// Entrées : a et b, entiers, a ≥ 0 et b > 0
q ← 0
r ← a
TantQue r ≥ b Faire
r ← r - b
q ← q + 1
FinTantQue
Afficher(q, r)
Avec a = 17 et b = 5 :
| Tour | r ≥ b ? | r | q |
|---|---|---|---|
| avant | 17 | 0 | |
| 1 | 17 ≥ 5 oui | 12 | 1 |
| 2 | 12 ≥ 5 oui | 7 | 2 |
| 3 | 7 ≥ 5 oui | 2 | 3 |
| fin | 2 ≥ 5 non | 2 | 3 |
L'algorithme affiche 3 et 2. On retrouve 17 = 5 × 3 + 2 : il calcule le quotient et le reste de la division euclidienne de a par b par soustractions successives, c'est-à-dire a DIV b et a MOD b. La dernière ligne, « fin », note le test qui fait sortir de la boucle : c'est elle qui prouve que tu n'as pas fait un tour de trop.
Exemple 3 : retrouver le rôle d'un algorithme
Fonction Mystere(n : entier) : entier
m ← 0
TantQue n > 0 Faire
m ← m × 10 + n MOD 10
n ← n DIV 10
FinTantQue
Retourner m
FinFonction
Trace de Mystere(1234) :
| Tour | n MOD 10 | m | n |
|---|---|---|---|
| avant | 0 | 1234 | |
| 1 | 4 | 0 × 10 + 4 = 4 | 123 |
| 2 | 3 | 4 × 10 + 3 = 43 | 12 |
| 3 | 2 | 43 × 10 + 2 = 432 | 1 |
| 4 | 1 | 432 × 10 + 1 = 4321 | 0 |
La fonction renvoie 4321. Un deuxième essai, Mystere(560), renvoie 65 (le zéro de tête disparaît). Elle renvoie l'entier écrit à l'envers : à chaque tour, n MOD 10 détache le dernier chiffre de n, m × 10 + … l'ajoute à droite de m, et n DIV 10 le retire de n.
La trace sur machine
Une fois l'algorithme programmé, la machine peut faire la trace pour toi. Trois techniques, de la plus simple à la plus puissante :
Les affichages de suivi. Ajoute un print dans la boucle, qui montre les variables à chaque tour. On l'enlève une fois le problème trouvé.
n = 1234
m = 0
while n > 0:
m = m * 10 + n % 10
n = n // 10
print("m =", m, "n =", n) # suivi, à retirer ensuite
print(m)
m = 4 n = 123
m = 43 n = 12
m = 432 n = 1
m = 4321 n = 0
4321
Le débogueur. Thonny, VS Code et la plupart des environnements ont un mode pas à pas : tu exécutes le programme une instruction à la fois, et un panneau affiche la valeur de chaque variable. Dans Thonny, lance le programme en mode débogage (l'icône en forme d'insecte), puis avance pas à pas ; dans VS Code, clique dans la marge pour poser un point d'arrêt sur une ligne, puis lance le débogage. Le programme s'arrête sur cette ligne et tu inspectes l'état.
Python Tutor. Ce site exécute un programme court et dessine, à chaque étape, les variables et les listes avec des flèches. C'est l'outil idéal pour comprendre les copies de listes (chapitre 7) et les appels de fonctions.
Le programme du BTS évoque précisément ces outils : selon le langage, on peut montrer les fonctions de suivi des variables et le débogage pas à pas.
Construire un jeu d'essai
Un jeu d'essai (ou jeu de test) est un ensemble de cas de test : pour chacun, les données d'entrée et le résultat attendu, calculé à la main avant d'exécuter. On compare ensuite avec le résultat obtenu. Un test dont on n'a pas prévu le résultat ne prouve rien : on finit toujours par trouver « logique » ce que la machine affiche.
Un bon jeu d'essai couvre trois familles de cas :
| Famille | Exemples | Ce qu'elle attrape |
|---|---|---|
| Cas normaux | Une valeur ordinaire de chaque branche | Les erreurs de logique grossières |
| Cas limites | Les frontières des conditions, 0, 1, une liste vide, une liste d'un seul élément, le premier et le dernier élément, des valeurs égales | Les < au lieu de ≤, les tours en trop ou en moins, les divisions par zéro |
| Cas d'erreur | Une saisie hors domaine (note de 25), un texte au lieu d'un nombre | L'absence de contrôle des entrées |
Exemple : jeu d'essai de la fonction qui renvoie le maximum d'une liste de notes.
| N° | Entrée | Pourquoi | Attendu | Obtenu | Conforme |
|---|---|---|---|---|---|
| 1 | [12, 15, 9] | Cas normal, maximum au milieu | 15 | 15 | oui |
| 2 | [18, 3, 7] | Maximum en premier | 18 | 18 | oui |
| 3 | [4, 6, 20] | Maximum en dernier | 20 | 20 | oui |
| 4 | [11] | Un seul élément | 11 | 11 | oui |
| 5 | [-3, -8] | Valeurs négatives | −3 | 0 | non |
| 6 | [] | Liste vide | message d'erreur | IndexError | non |
Les tests 5 et 6 ont trouvé deux défauts : le maximum était initialisé à 0 au lieu du premier élément (chapitre 7), et le cas de la liste vide n'était pas prévu. C'est exactement le rôle d'un jeu d'essai : les cas normaux passent presque toujours, les erreurs se cachent aux limites.
Automatiser les tests avec assert
L'instruction assert condition ne fait rien si la condition est vraie, et arrête le programme avec une AssertionError si elle est fausse. On s'en sert pour transformer un jeu d'essai en tests qui se relancent en une seconde après chaque modification. La fonction maximum utilise def et return, détaillés au chapitre 6.
def maximum(notes):
plus_grande = notes[0]
for note in notes:
if note > plus_grande:
plus_grande = note
return plus_grande
assert maximum([12, 15, 9]) == 15
assert maximum([18, 3, 7]) == 18
assert maximum([4, 6, 20]) == 20
assert maximum([11]) == 11
assert maximum([-3, -8]) == -3
print("Tous les tests passent")
Tous les tests passent
En SLAM, tu utiliseras des outils de tests unitaires (comme unittest ou pytest en Python, xUnit en C#, PHPUnit en PHP), qui reposent sur la même idée : une entrée, un résultat attendu, une comparaison automatique. Le cours sur les tests montre comment écrire un bon test unitaire.
Les erreurs que Python te signale
Quand Python rencontre une erreur, il affiche un message appelé traceback. Lis-le par la fin : la dernière ligne donne le type et la description de l'erreur, la ligne au-dessus indique le numéro de ligne fautif.
Traceback (most recent call last):
File "moyenne.py", line 4, in <module>
moyenne = total / nb
~~~~~~^~~~
ZeroDivisionError: division by zero
| Erreur | Cause habituelle | Exemple |
|---|---|---|
SyntaxError | Le code n'est pas du Python valide : deux-points oubliés, parenthèse non fermée, = au lieu de == dans un if | if x = 3: |
IndentationError | Un bloc mal décalé | Un corps de boucle pas indenté |
NameError | Variable ou fonction inconnue : faute de frappe, ou variable utilisée avant d'être affectée | print(totl) |
TypeError | Opération impossible entre ces types | "Total : " + 12 |
ValueError | Bon type, mauvaise valeur | int("douze") |
ZeroDivisionError | Division par zéro | total / 0 |
IndexError | Indice hors du tableau | t[5] sur une liste de 5 éléments |
KeyError | Clé absente d'un dictionnaire | stock["clavier"] sans cette clé |
Une erreur de syntaxe empêche le programme de démarrer. Les autres apparaissent à l'exécution, parfois seulement pour certaines données : d'où l'importance des cas limites.
Quand une erreur est prévisible (un utilisateur qui tape une lettre au lieu d'un nombre), on peut l'intercepter avec try et except :
while True:
saisie = input("Nombre de postes ? ")
try:
nb = int(saisie)
break
except ValueError:
print(saisie, "n'est pas un entier, recommence.")
print("Nombre de postes :", nb)
Les effets indésirables à connaître
Le programme cite une liste de pièges « aux conséquences spectaculaires ». Tu les as presque tous rencontrés :
| Piège | Exemple | Où dans le cours |
|---|---|---|
| Approximation des réels | 0.1 + 0.2 == 0.3 est faux | Chapitre 2 |
| Débordement d'entier | Un int de C# dépasse 2 147 483 647 et devient négatif | Chapitre 2 |
| Transtypage | int(3.9) vaut 3 ; "3" + "4" vaut "34" | Chapitre 2 |
| Évaluation partielle | nb != 0 and total / nb > 10 est sûr, l'ordre inverse plante | Chapitre 2 |
| Indice hors domaine | t[len(t)] lève IndexError | Chapitre 7 |
| Effet de bord | Une fonction qui modifie la liste reçue en paramètre modifie celle de l'appelant | Chapitre 6 |
Déboguer avec méthode
Un programme qui donne un mauvais résultat sans message d'erreur est le cas le plus difficile. Modifier le code au hasard aggrave souvent les choses. Suis plutôt ces étapes :
- Reproduire : trouve une entrée précise qui donne un résultat faux, et calcule à la main le résultat attendu.
- Localiser : fais la trace (à la main, avec des
printou le débogueur) et repère la première ligne où une variable prend une valeur différente de celle que tu attendais. - Expliquer : formule la cause en une phrase (« le compteur est initialisé dans la boucle »). Si tu ne sais pas l'expliquer, tu n'as pas encore trouvé.
- Corriger cette cause, et seulement elle.
- Retester tout le jeu d'essai, pas seulement le cas qui échouait : une correction peut en casser un autre.
Le programme demande justement de travailler sur des « algorithmes délibérément erronés dont les défauts sont repérés puis débogués ». C'est une question classique d'entretien : « votre programme renvoie 0 pour cette liste, comment le corrigez-vous ? ». La réponse attendue suit ces cinq étapes.
Exercices
Exercice 1 sur 6 Trace d'une boucle Pour Réussi À revoir
Fais la trace de cet algorithme et dis ce qu'il affiche. Que calcule-t-il en général, pour un entier n ≥ 1 ?
n ← 5
s ← 0
Pour i de 1 à n Faire
s ← s + 2 × i - 1
FinPour
Afficher(s)
Voir la réponseMasquer la réponse de l'exercice 1
| i | 2 × i − 1 | s |
|---|---|---|
| avant | 0 | |
| 1 | 1 | 1 |
| 2 | 3 | 4 |
| 3 | 5 | 9 |
| 4 | 7 | 16 |
| 5 | 9 | 25 |
L'algorithme affiche 25. Il calcule la somme des n premiers nombres impairs (1 + 3 + 5 + 7 + 9), qui vaut n² : on le vérifie sur la colonne s, qui contient 1, 4, 9, 16, 25.
Tu avais trouvé ?
Exercice 2 sur 6 Retrouver le rôle Réussi À revoir
Fais la trace pour t = [4, 9, 2, 9, 5], puis pour t = [3, 1, 2]. Que fait cet algorithme ?
r ← 0
Pour i de 1 à longueur(t) - 1 Faire
Si t[i] < t[i - 1] Alors
r ← r + 1
FinSi
FinPour
Afficher(r)
Voir la réponseMasquer la réponse de l'exercice 2
Pour [4, 9, 2, 9, 5] :
| i | t[i − 1] | t[i] | t[i] < t[i − 1] ? | r |
|---|---|---|---|---|
| avant | 0 | |||
| 1 | 4 | 9 | non | 0 |
| 2 | 9 | 2 | oui | 1 |
| 3 | 2 | 9 | non | 1 |
| 4 | 9 | 5 | oui | 2 |
Affichage : 2. Pour [3, 1, 2], on compare 1 à 3 (oui), puis 2 à 1 (non) : affichage 1. L'algorithme compte le nombre de fois où un élément est plus petit que celui qui le précède, c'est-à-dire le nombre de « descentes » du tableau. Il affiche 0 si et seulement si le tableau est trié dans l'ordre croissant. La boucle commence à 1 pour que t[i - 1] existe.
Tu avais trouvé ?
Exercice 3 sur 6 Jeu d'essai de l'année bissextile Réussi À revoir
Propose un jeu d'essai d'au moins cinq années pour tester le programme de l'année bissextile. Pour chaque année, dis pourquoi tu la choisis et le résultat attendu.
Voir la réponseMasquer la réponse de l'exercice 3
| Année | Pourquoi | Attendu |
|---|---|---|
| 2026 | Non divisible par 4 | non bissextile |
| 2024 | Divisible par 4, pas par 100 | bissextile |
| 2100 | Divisible par 100, pas par 400 | non bissextile |
| 2000 | Divisible par 400 | bissextile |
| 1900 | Deuxième cas « divisible par 100, pas par 400 », pour confirmer | non bissextile |
Chaque partie de la condition (a MOD 4 = 0 ET a MOD 100 ≠ 0) OU a MOD 400 = 0 est rendue vraie ou fausse au moins une fois. Un jeu d'essai limité à 2024 et 2026 ne détecterait pas l'oubli de la règle des siècles.
Tu avais trouvé ?
Exercice 4 sur 6 Corriger un algorithme erroné Réussi À revoir
Ce programme doit afficher la moyenne des notes supérieures ou égales à 10. Pour [8, 12, 15, 10], il affiche 6.75 au lieu de 12.33. Trouve les deux erreurs avec une trace, puis corrige.
notes = [8, 12, 15, 10]
somme = 0
nb = 0
for note in notes:
if note > 10:
somme = somme + note
nb = nb + 1
print(round(somme / nb, 2))
Voir la réponseMasquer la réponse de l'exercice 4
| note | note > 10 ? | somme | nb |
|---|---|---|---|
| avant | 0 | 0 | |
| 8 | non | 0 | 1 |
| 12 | oui | 12 | 2 |
| 15 | oui | 27 | 3 |
| 10 | non | 27 | 4 |
La trace donne 27 / 4 = 6.75. Elle montre deux écarts avec ce qu'on attendait : la note 10 n'est pas ajoutée à somme, et nb augmente aussi pour la note 8. D'où les deux erreurs :
note > 10exclut le 10, alors que l'énoncé dit « supérieures ou égales » : il faut>=.nb = nb + 1est hors duif(mauvaise indentation) : on compte toutes les notes au lieu des seules notes retenues.
notes = [8, 12, 15, 10]
somme = 0
nb = 0
for note in notes:
if note >= 10:
somme = somme + note
nb = nb + 1
if nb > 0:
print(round(somme / nb, 2))
else:
print("Aucune note supérieure ou égale à 10")
Résultat : (12 + 15 + 10) / 3 = 12.33. Le test nb > 0 traite le cas où aucune note ne convient.
Tu avais trouvé ?
Exercice 5 sur 6 Lire un traceback Réussi À revoir
Ce programme plante. Quel est le type d'erreur, à quelle ligne, et comment la corriger ?
serveurs = ["web01", "web02", "bdd01"]
for i in range(1, len(serveurs) + 1):
print(i, serveurs[i])
Voir la réponseMasquer la réponse de l'exercice 5
Le programme affiche 1 web02 et 2 bdd01, puis lève IndexError: list index out of range à la ligne 3, quand i vaut 3 : la liste a 3 éléments, d'indices 0, 1 et 2. Deux erreurs de bornes se combinent : on saute web01 (indice 0) et on déborde à la fin. Correction, si l'on veut numéroter à partir de 1 :
serveurs = ["web01", "web02", "bdd01"]
for i in range(len(serveurs)):
print(i + 1, serveurs[i])
Tu avais trouvé ?
Exercice 6 sur 6 Des tests avec assert Réussi À revoir
Écris la fonction est_bissextile(annee) qui renvoie True ou False, puis transforme le jeu d'essai de l'exercice précédent en cinq assert.
Voir la réponseMasquer la réponse de l'exercice 6
def est_bissextile(annee):
return (annee % 4 == 0 and annee % 100 != 0) or annee % 400 == 0
assert est_bissextile(2026) == False
assert est_bissextile(2024) == True
assert est_bissextile(2100) == False
assert est_bissextile(2000) == True
assert est_bissextile(1900) == False
print("Tests réussis")
La fonction renvoie directement la valeur de la condition, qui est déjà un booléen : pas besoin d'un if … return True else return False. On peut aussi écrire assert not est_bissextile(2026).
Tu avais trouvé ?