Adloun

Une fonction corrige un questionnaire à choix unique

Exercice de TD · niveau 3 (difficile) · NSI (première), chapitre 11 — Conduire un projet · Tester pour de vrai

Énoncé

Une fonction corrige un questionnaire à choix unique.


def score(reponses, corrige):
    """Un point par bonne reponse."""
    n = 0
    for i in range(len(reponses)):
        if reponses[i] == corrige[i]:
            n = n + 1
    return n

Un élève signale que le programme plante quand il coche plus de réponses qu'il n'y a de questions. Un camarade corrige en remplaçant la ligne 4 par for i in range(len(corrige)):.

Corrigé

Posons corrige = ["a", "b", "c", "d"], soit quatre questions.

1. Avec la version d'origine, score(["a","b","c","d","e"], corrige) parcourt de à et évalue corrige[4] : IndexError. Avec range(len(corrige)), la boucle s'arrête à et la fonction renvoie . Le défaut signalé est bien réglé.

2. Mais le cas inverse fonctionnait, et ne fonctionne plus. Un élève qui ne répond qu'aux deux premières questions :

`score(2 réponses)``score(5 réponses)`
version d'origine`IndexError`
version corrigée`IndexError`
version correcte

On a déplacé l'erreur d'un cas à l'autre. C'est une régression : la correction d'un défaut a cassé un comportement qui marchait.

3. La boucle doit s'arrêter au plus court des deux :


def score(reponses, corrige):
    """Un point par bonne reponse.
    Les reponses en trop sont ignorees, les manquantes valent faux."""
    n = 0
    for i in range(min(len(reponses), len(corrige))):
        if reponses[i] == corrige[i]:
            n = n + 1
    return n

def test_non_regression():
    corrige = ["a", "b", "c", "d"]
    assert score(corrige, corrige) == 4              # tout juste
    assert score(["a", "x", "c", "x"], corrige) == 2 # cas ordinaire
    assert score(["a", "b"], corrige) == 2           # trop peu de reponses
    assert score(["a", "b", "c", "d", "e"], corrige) == 4  # trop de reponses
    assert score([], corrige) == 0                   # aucune reponse

test_non_regression()
print("ok test_non_regression")

Ce que ce jeu de tests garantit, et qu'aucun des deux groupes n'avait. Les deux derniers cas sont les deux défauts, l'ancien et le nouveau, transformés en assertions. Ils ne peuvent plus revenir sans être vus. C'est la troisième habitude du cours — écrire le test avant la correction — appliquée à la lettre : on aurait écrit assert score(["a","b","c","d","e"], corrige) == 4 d'abord, on l'aurait vu échouer, et le jeu de tests complet aurait alors immédiatement révélé que la correction cassait le cas court.

Une remarque de spécification. La docstring dit maintenant ce que la fonction fait des listes de longueurs différentes. C'est une décision, pas une évidence : on aurait pu choisir de refuser l'appel par un assert. Le défaut initial venait moins du code que de l'absence de cette décision — personne ne s'était demandé ce que devait faire le programme dans ce cas, donc chacun a corrigé selon l'exemple qu'il avait sous les yeux.

Les autres exercices de ce chapitre Le cours du chapitre

Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.