{
  "chapter": {
    "id": "specification",
    "level": "premiere",
    "theme": "Programmation",
    "title": "Spécification",
    "description": "Spécifier une fonction : signature (nom, paramètres,\ntype de retour), préconditions, postconditions,\ndocumentation par docstrings, exemples d'usage,\nrelation avec les tests.",
    "parties": [
      {
        "id": "contrat",
        "title": "Spécifier : signature et contrat"
      },
      {
        "id": "conditions",
        "title": "Préconditions et postconditions"
      },
      {
        "id": "tests",
        "title": "Tests et robustesse"
      }
    ],
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Spécification",
      "partie": "contrat",
      "statement": "Qu'est-ce que la **spécification** d'une\nfonction ?",
      "options": [
        {
          "text": "Son code source complet, recopié intégralement ligne par ligne",
          "correct": false,
          "feedback": "Le code source décrit\n**comment** la fonction\nréalise sa tâche, alors\nque la spécification\ndécrit **ce qu'elle doit faire**. Les deux\nnotions sont\ndistinctes.\n"
        },
        {
          "text": "Son nombre total de lignes de code",
          "correct": false,
          "feedback": "Le nombre de lignes est\nune mesure du code, sans\nrapport avec ce que la\nfonction est censée\naccomplir.\n"
        },
        {
          "text": "Sa vitesse d'exécution sur la machine",
          "correct": false,
          "feedback": "La vitesse d'exécution est\nune mesure de performance,\nobservable à l'usage. Ce\nn'est pas la\nspécification, qui décrit\nle comportement attendu\nde la fonction.\n"
        },
        {
          "text": "Ce que la fonction doit faire, indépendamment du code",
          "correct": true,
          "feedback": "Bonne réponse : la spécification est un\n**contrat** entre l'auteur et\nl'utilisateur. Elle précède\nl'implémentation.\n"
        }
      ],
      "explanation": "Adage : « bien spécifier = à moitié\nimplémenté ». Bien spécifier permet de\ntester, de documenter, et de coder\ncorrectement."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "signature"
      ],
      "title": "Signature",
      "partie": "contrat",
      "statement": "Qu'appelle-t-on la **signature** d'une fonction ?",
      "options": [
        {
          "text": "Le nom de l'auteur de la fonction",
          "correct": false,
          "feedback": "Cette interprétation\nlittérale n'a aucun\nrapport avec le sens\ntechnique du mot\n« signature » en\nprogrammation.\n"
        },
        {
          "text": "Son nom, ses paramètres typés et son type de retour",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'« en-tête »\nidentifiant uniquement la fonction. En\nPython avec annotations :\n`def carre(x: int) -> int:`.\n"
        },
        {
          "text": "Le numéro de ligne où la fonction est définie dans le fichier",
          "correct": false,
          "feedback": "La position dans le\nfichier source ne fait\npas partie de la\nsignature : seuls le\nnom, les paramètres et\nle type de retour la\ncomposent.\n"
        },
        {
          "text": "La date de création",
          "correct": false,
          "feedback": "La date de création n'a\naucun lien avec la\nsignature, qui désigne\nuniquement l'en-tête\nidentifiant la\nfonction.\n"
        }
      ],
      "explanation": "Deux fonctions de **même signature** sont\ninterchangeables du point de vue de\nl'appelant. C'est la base de la modularité."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "precondition"
      ],
      "title": "Précondition",
      "partie": "conditions",
      "statement": "Qu'est-ce qu'une **précondition** ?",
      "options": [
        {
          "text": "Une condition d'arrêt d'une boucle du programme",
          "correct": false,
          "feedback": "La condition d'arrêt\nd'une boucle est une\nnotion liée au contrôle\nde flux d'un programme,\net non à la\nspécification d'une\nfonction.\n"
        },
        {
          "text": "Une condition vraie avant l'appel, sur les paramètres",
          "correct": true,
          "feedback": "Bonne réponse : par exemple, pour une\nfonction `racine(x)`, la précondition\npeut être « x ≥ 0 ». Si la précondition\nn'est pas respectée, la fonction n'a\npas à fonctionner correctement.\n"
        },
        {
          "text": "Une condition vérifiée juste après l'appel de la fonction",
          "correct": false,
          "feedback": "Erreur : ce serait une **postcondition**.\n"
        },
        {
          "text": "Une condition tirée au hasard",
          "correct": false,
          "feedback": "Une précondition n'a rien\nd'aléatoire. C'est une\ncontrainte explicite,\ndocumentée par le\nconcepteur de la\nfonction.\n"
        }
      ],
      "explanation": "C'est l'utilisateur qui doit garantir la\nprécondition. La fonction peut faire\nconfiance à ses paramètres (sauf si elle\nest défensive et vérifie elle-même)."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "postcondition"
      ],
      "title": "Postcondition",
      "partie": "conditions",
      "statement": "Qu'est-ce qu'une **postcondition** ?",
      "options": [
        {
          "text": "Une condition tirée au hasard",
          "correct": false,
          "feedback": "Une postcondition n'a\nrien d'aléatoire. C'est\nune garantie explicite\nfournie par la fonction\nsur son résultat.\n"
        },
        {
          "text": "Le type de retour déclaré dans la signature de la fonction",
          "correct": false,
          "feedback": "Le type de retour fait\npartie de la signature.\nLa postcondition est, elle,\nune propriété logique\nque la valeur renvoyée\ndoit vérifier (par\nexemple, « le résultat\nest positif »).\n"
        },
        {
          "text": "Ce que la fonction garantit sur son résultat en sortie",
          "correct": true,
          "feedback": "Bonne réponse : par exemple, pour\n`racine(x)`, la postcondition est\n« la valeur renvoyée r vérifie\nr * r == x » (à epsilon près pour les\nflottants).\n"
        },
        {
          "text": "Une condition avant l'appel",
          "correct": false,
          "feedback": "Erreur : ce serait une **précondition**.\n"
        }
      ],
      "explanation": "Précondition + postcondition = **contrat**\nde la fonction. Cette approche est dite\n« **programmation par contrat** »."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "docstring"
      ],
      "title": "Docstring",
      "partie": "contrat",
      "statement": "Comment **documenter** une fonction Python selon la convention du langage ?",
      "options": [
        {
          "text": "Avec un commentaire placé tout à la fin de la fonction",
          "correct": false,
          "feedback": "Erreur : la convention est de placer la documentation en tout début de corps de fonction, sous forme de docstring, juste après la ligne `def`.\n"
        },
        {
          "text": "Avec une docstring en première instruction du corps",
          "correct": true,
          "feedback": "Bonne réponse : `def f(x):` puis, sur la ligne suivante, `\"\"\"description...\"\"\"`. C'est ce texte qu'affiche `help(f)`.\n"
        },
        {
          "text": "Avec un commentaire `#` au-dessus",
          "correct": false,
          "feedback": "Erreur : un commentaire `#` se lit dans le code, mais la convention de Python pour documenter une fonction est la docstring, placée dans le corps de la fonction.\n"
        },
        {
          "text": "Dans un fichier README placé à part",
          "correct": false,
          "feedback": "La documentation\ngénérale d'un projet\npeut être placée dans un\nfichier README, mais la\ndocumentation d'une\nfonction se met\ndirectement dans son\ncorps, à l'aide d'une\ndocstring.\n"
        }
      ],
      "explanation": "Docstring de qualité : description courte,\nligne vide, description détaillée,\nparamètres, retour, exemples (`>>> ...`)."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "exemple-spec"
      ],
      "title": "Exemple complet",
      "partie": "contrat",
      "statement": "Quelle est la **bonne pratique** pour spécifier\nune fonction Python `valeur_absolue(x)` ?",
      "options": [
        {
          "text": "```python\n# x est un nombre\n# renvoie sa valeur absolue\ndef valeur_absolue(x):\n    return abs(x)\n```\n",
          "correct": false,
          "feedback": "Erreur : la spécification est écrite en commentaires placés au-dessus de la fonction. Elle est lisible, mais la convention de Python est une docstring, chaîne entre triples guillemets placée sous la signature.\n"
        },
        {
          "text": "```python\ndef valeur_absolue(x: float) -> float:\n    # Renvoie la valeur absolue de x.\n    # Précondition : x est un nombre.\n    return abs(x)\n```\n",
          "correct": false,
          "feedback": "Erreur : la spécification est écrite en commentaires dans le corps. La forme attendue est une docstring, chaîne entre triples guillemets placée sous la signature.\n"
        },
        {
          "text": "```python\ndef valeur_absolue(x: float) -> float:\n    \"\"\"Renvoie la valeur absolue de x.\n\n    Précondition : x est un nombre.\n    Postcondition : le résultat est >= 0.\n    \"\"\"\n    return abs(x)\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : signature avec types,\ndocstring décrivant le but, pré- et\npostconditions. Le code se documente\nlui-même.\n"
        },
        {
          "text": "```python\ndef valeur_absolue(x):\n    \"\"\"valeur_absolue(x)\"\"\"\n    return abs(x)\n```\n",
          "correct": false,
          "feedback": "Erreur : la docstring ne fait que répéter\nle nom de la fonction. Elle ne dit ni ce\nque fait la fonction, ni ce qu'elle attend,\nni ce qu'elle garantit.\n"
        }
      ],
      "explanation": "Bonnes pratiques : signature typée,\ndocstring claire, contrat\npré-/post-conditions documenté."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "pourquoi"
      ],
      "title": "Pourquoi spécifier ?",
      "partie": "contrat",
      "statement": "Pourquoi spécifier une fonction **avant** de\nl'écrire ?",
      "options": [
        {
          "text": "Parce que c'est strictement obligatoire dans le langage Python",
          "correct": false,
          "feedback": "Erreur : ce n'est pas une obligation\nsyntaxique. C'est une bonne pratique.\n"
        },
        {
          "text": "Pour ralentir l'exécution du code",
          "correct": false,
          "feedback": "La spécification n'a\naucune incidence sur la\nperformance ; elle\nprécède l'écriture du\ncode et ne s'exécute\npas.\n"
        },
        {
          "text": "Pour clarifier le but, faciliter les tests et la documentation",
          "correct": true,
          "feedback": "Bonne réponse : la spécification\nstructure la pensée. On évite de coder\ndans le brouillard. La fonction est\nalors testable.\n"
        },
        {
          "text": "Pour utiliser davantage de mémoire",
          "correct": false,
          "feedback": "La spécification est un\ndocument conceptuel\nrédigé avant le code.\nElle n'a aucun rapport\navec la consommation\nmémoire à l'exécution.\n"
        }
      ],
      "explanation": "Sans spécification : code parfois\n« qui marche », mais buggé sur des cas\nlimites non envisagés. Avec spécification :\ncas limites pensés en amont."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "test",
        "assert"
      ],
      "title": "Écrire un test avec assert",
      "partie": "tests",
      "statement": "La fonction `carre(x)` doit renvoyer le carré de `x`. Quelle ligne constitue un test qui arrête le programme si `carre(3)` ne renvoie pas `9` ?",
      "options": [
        {
          "text": "`carre(3) == 9`",
          "correct": false,
          "feedback": "Erreur : cette ligne calcule un booléen, mais personne ne l'exploite. Si `carre(3)` renvoyait une autre valeur, le programme continuerait sans rien signaler.\n"
        },
        {
          "text": "`assert carre(3) = 9`",
          "correct": false,
          "feedback": "Erreur : le signe `=` sert à l'affectation. Cette ligne provoque une erreur de syntaxe ; une comparaison s'écrit avec `==`.\n"
        },
        {
          "text": "`assert carre(9) == 3`",
          "correct": false,
          "feedback": "Erreur : les rôles sont inversés. Pour une fonction correcte, `carre(9)` vaut $81$ : ce test arrêterait le programme alors que la fonction est juste.\n"
        },
        {
          "text": "`assert carre(3) == 9`",
          "correct": true,
          "feedback": "Bonne réponse : si la comparaison est fausse, `assert` arrête le programme avec une `AssertionError` ; si elle est vraie, l'exécution continue sans rien afficher.\n"
        }
      ],
      "explanation": "Un test compare le résultat d'un appel à la valeur attendue par la spécification. Écrit avec `assert`, il ne dit rien quand tout va bien et arrête le programme dès qu'un résultat est faux."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "exhaustivite"
      ],
      "title": "Cas limites",
      "partie": "tests",
      "statement": "Quels sont les **cas limites** typiques à\nconsidérer en spécifiant une fonction sur une\nliste ?",
      "options": [
        {
          "text": "Une liste constituée uniquement d'éléments imaginaires",
          "correct": false,
          "feedback": "Cette interprétation\nn'a pas de sens en\ninformatique : on\ntravaille toujours avec\ndes données concrètes.\n"
        },
        {
          "text": "Liste vide, à un élément, ou très longue",
          "correct": true,
          "feedback": "Bonne réponse : ces cas révèlent\nsouvent des bugs. Toujours les inclure\ndans les tests.\n"
        },
        {
          "text": "Une liste de couleurs",
          "correct": false,
          "feedback": "Cette description est\ntrop spécifique : on\ncherche ici les cas\nlimites génériques d'une\nfonction sur une\nliste, indépendamment\ndu type de ses\néléments.\n"
        },
        {
          "text": "Aucun, le cas général suffit",
          "correct": false,
          "feedback": "Erreur : les bugs sont souvent dans les\ncas limites.\n"
        }
      ],
      "explanation": "Pour les nombres : 0, négatifs, très\ngrands, fractionnaires. Pour les chaînes :\nvide, un caractère, accents, emojis. Pour\nles listes : vide, un élément, doublons,\ntriée/non triée."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "exemple-pre"
      ],
      "title": "Identifier la précondition",
      "partie": "conditions",
      "statement": "Pour la fonction\n```python\ndef diviser(a, b):\n    return a / b\n```\nQuelle est une **précondition** essentielle ?",
      "options": [
        {
          "text": "Aucune précondition n'est nécessaire",
          "correct": false,
          "feedback": "Une précondition est\nbien nécessaire :\n`b != 0`, faute de quoi\nla division provoque\nune exception.\n"
        },
        {
          "text": "`a > 0`",
          "correct": false,
          "feedback": "La division est\nparfaitement définie\npour toute valeur de\n`a` (positive, nulle ou\nnégative). Cette\ncontrainte n'est donc\npas une précondition\nnécessaire.\n"
        },
        {
          "text": "`a == b`",
          "correct": false,
          "feedback": "L'égalité entre `a` et\n`b` ne joue aucun rôle\ndans la division. Cette\ncontrainte n'est pas\nune précondition\npertinente.\n"
        },
        {
          "text": "`b != 0`",
          "correct": true,
          "feedback": "Bonne réponse : sans cette\nprécondition, on lève\n`ZeroDivisionError`. À documenter\nclairement.\n"
        }
      ],
      "explanation": "Bonnes pratiques : (1) noter la\nprécondition dans la docstring ; (2) la\nvérifier en début de fonction avec\n`assert` ; (3) la tester explicitement."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "post-doc"
      ],
      "title": "Bonne docstring",
      "partie": "conditions",
      "statement": "Que devrait contenir une **bonne docstring** ?",
      "options": [
        {
          "text": "Le but, les paramètres, la valeur renvoyée et des exemples",
          "correct": true,
          "feedback": "Bonne réponse : ce sont les informations utiles à l'utilisateur, qui peut ainsi appeler la fonction sans avoir besoin de lire son code.\n"
        },
        {
          "text": "Uniquement le nom de l'auteur ayant rédigé la fonction et sa date",
          "correct": false,
          "feedback": "Le nom de l'auteur peut\ny figurer en complément,\nmais il ne suffit pas :\nla docstring sert avant\ntout à décrire le\ncomportement de la\nfonction.\n"
        },
        {
          "text": "Le code source intégral de la fonction",
          "correct": false,
          "feedback": "La docstring décrit le\n**comportement** de la\nfonction. Reproduire\nson code source serait\nredondant : ce dernier\nest déjà visible juste\naprès.\n"
        },
        {
          "text": "Un poème ou un texte décoratif",
          "correct": false,
          "feedback": "Cette interprétation est\nfantaisiste. La\ndocstring doit être\nclaire, factuelle et\ninformative.\n"
        }
      ],
      "explanation": "Une docstring minimale décrit le but, les paramètres et la valeur renvoyée ; une docstring complète y ajoute des exemples d'appel et des remarques sur les cas particuliers."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "help"
      ],
      "title": "help()",
      "partie": "contrat",
      "statement": "Que fait `help(ma_fonction)` en Python ?",
      "options": [
        {
          "text": "Elle lance la documentation officielle en ligne sur Internet",
          "correct": false,
          "feedback": "Erreur : `help` ne consulte aucune ressource en ligne. Elle affiche la documentation disponible sur place, fournie par la docstring de la fonction.\n"
        },
        {
          "text": "Elle exécute la fonction donnée en argument",
          "correct": false,
          "feedback": "La fonction `help` se\ncontente d'afficher la\ndocumentation associée\nà son argument ; elle\nn'exécute pas la\nfonction.\n"
        },
        {
          "text": "Affiche la signature et la docstring de la fonction",
          "correct": true,
          "feedback": "Bonne réponse : c'est pourquoi documenter une fonction par une docstring est utile, la documentation s'affiche directement dans l'interpréteur.\n"
        },
        {
          "text": "Renvoie le code source",
          "correct": false,
          "feedback": "Erreur : `help` affiche la signature et la docstring de la fonction, pas les instructions de son corps.\n"
        }
      ],
      "explanation": "`help()` est un raccourci interactif pour consulter la documentation d'une fonction ou d'une bibliothèque. Toujours documenter ses fonctions, pour aider les futurs lecteurs, y compris soi-même."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "defense"
      ],
      "title": "Protéger une fonction par une assertion",
      "partie": "conditions",
      "statement": "La fonction `racine(x)` a pour précondition `x >= 0`. Quelle ligne, placée au début de son corps, arrête le programme dès qu'un appel ne respecte pas cette précondition ?",
      "options": [
        {
          "text": "`# précondition : x >= 0`",
          "correct": false,
          "feedback": "Erreur : un commentaire documente la précondition mais Python l'ignore. Un appel `racine(-4)` s'exécuterait sans aucun signalement.\n"
        },
        {
          "text": "`assert x < 0`",
          "correct": false,
          "feedback": "Erreur : la condition est inversée. Une assertion énonce ce qui doit être vrai ; celle-ci arrêterait le programme pour tous les appels corrects et laisserait passer les mauvais.\n"
        },
        {
          "text": "`assert x >= 0`",
          "correct": true,
          "feedback": "Bonne réponse : si `x` est négatif, l'assertion est fausse et le programme s'arrête avec une `AssertionError`, avant tout calcul absurde.\n"
        },
        {
          "text": "`return x >= 0`",
          "correct": false,
          "feedback": "Erreur : `return` termine la fonction et renvoie un booléen, à chaque appel. La racine ne serait jamais calculée, et aucun appel incorrect ne serait signalé.\n"
        }
      ],
      "explanation": "```python\ndef racine(x):\n    assert x >= 0, \"x doit être positif ou nul\"\n    return x ** 0.5\n```\nUne assertion en tête de fonction vérifie la précondition à chaque appel. Le message qui la suit aide à comprendre l'arrêt."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "type-retour"
      ],
      "title": "Type de retour",
      "partie": "contrat",
      "statement": "Pourquoi spécifier le **type de retour** d'une\nfonction ?",
      "options": [
        {
          "text": "Pour clarifier ce que reçoit l'appelant",
          "correct": true,
          "feedback": "Bonne réponse : par exemple, `def moyenne(notes: list) -> float` annonce à l'appelant qu'il recevra un flottant, sans qu'il ait à lire le corps de la fonction.\n"
        },
        {
          "text": "Pour économiser de la mémoire à l'exécution",
          "correct": false,
          "feedback": "Les annotations de type\nn'ont aucun effet sur\nla consommation mémoire\nd'un programme Python.\n"
        },
        {
          "text": "Cela rend la fonction nettement plus rapide à l'exécution",
          "correct": false,
          "feedback": "Les annotations de type\nn'ont aucun effet sur la\nperformance d'exécution\nen Python : elles ne\nservent qu'à\ndocumenter.\n"
        },
        {
          "text": "C'est obligatoire en Python",
          "correct": false,
          "feedback": "L'annotation du type de\nretour est facultative\nen Python ; le langage\naccepte parfaitement les\nfonctions sans\nannotation.\n"
        }
      ],
      "explanation": "Annotations de type courantes : `int`, `float`, `str`, `bool`, `list`, `dict`. Elles documentent le prototype de la fonction sans rien changer à son exécution."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "signature-correcte"
      ],
      "title": "Bonne signature",
      "partie": "contrat",
      "statement": "Quelle signature est la **plus claire** pour\n« calculer la moyenne d'une liste de notes » ?",
      "options": [
        {
          "text": "`def moyenne(notes: list[float]) -> str:`",
          "correct": false,
          "feedback": "Erreur : le nom et le paramètre sont clairs,\nmais l'annotation de retour `str` est\nfausse, une moyenne est un nombre. Une\nannotation erronée est pire qu'une absence\nd'annotation.\n"
        },
        {
          "text": "`def moyenne_liste_notes_float(x):`",
          "correct": false,
          "feedback": "Erreur : le nom entasse des informations\nqui devraient être portées par les\nannotations, et le paramètre `x` ne dit\nrien de ce qu'il reçoit.\n"
        },
        {
          "text": "`def moyenne(notes: list[float]) -> float:`",
          "correct": true,
          "feedback": "Bonne réponse : nom explicite, type du\nparamètre, type de retour. Le lecteur\ncomprend tout sans lire le code.\n"
        },
        {
          "text": "`def moyenne(notes: list[float], n: int) -> float:`",
          "correct": false,
          "feedback": "Erreur : le paramètre `n` est redondant, la\nlongueur de la liste s'obtient par\n`len(notes)`. Un paramètre superflu rend la\nsignature ambiguë : que faire si `n` ne vaut\npas `len(notes)` ?\n"
        }
      ],
      "explanation": "Bonnes pratiques de nommage : verbes pour\nles fonctions (« calculer », « afficher »,\n« valider »), noms explicites pour les\nparamètres (`notes` plutôt que `l`)."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "tests-cas-limites"
      ],
      "title": "Cas particuliers",
      "partie": "tests",
      "statement": "Pour la fonction `moyenne(notes)`, quel **cas limite** poserait problème ?",
      "options": [
        {
          "text": "Une liste de cent notes",
          "correct": false,
          "feedback": "Une liste contenant cent\nnotes constitue un cas\ntout à fait standard,\nsans difficulté\nparticulière.\n"
        },
        {
          "text": "La liste vide, qui divise par zéro",
          "correct": true,
          "feedback": "Bonne réponse : `sum([]) / len([])` =\n`0 / 0` → `ZeroDivisionError`. À\ndécider : précondition « liste non\nvide », ou retour spécial (`None`,\n`nan`).\n"
        },
        {
          "text": "Une note décimale, par exemple $12{,}5$ ou $14{,}25$",
          "correct": false,
          "feedback": "Les notes à virgule ne\nposent pas de problème\nparticulier au calcul\nde la moyenne.\n"
        },
        {
          "text": "Une note négative",
          "correct": false,
          "feedback": "Une note négative n'a\nrien d'un cas limite sur\nle plan mathématique :\nelle ne pose aucun\nproblème de calcul.\n"
        }
      ],
      "explanation": "Toujours penser : que se passe-t-il si\nl'**entrée est vide / unique / extrême** ?\nC'est typiquement là que se cachent les\nbugs."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "robustesse"
      ],
      "title": "Spec et robustesse",
      "partie": "tests",
      "statement": "Comment une bonne spécification améliore-t-elle\nla **robustesse** d'un programme ?",
      "options": [
        {
          "text": "En faisant réfléchir aux cas limites et aux entrées valides",
          "correct": true,
          "feedback": "Bonne réponse : la rigueur de la\nspécification se reflète dans la\nqualité du code. Et permet de **tester**\nsystématiquement.\n"
        },
        {
          "text": "En réduisant le nombre de lignes",
          "correct": false,
          "feedback": "Le nombre de lignes\ndépend du style et des\ncontraintes du langage,\npas de la spécification.\nUne bonne spec apporte\nde la rigueur, pas de\nla concision.\n"
        },
        {
          "text": "En écrivant le code plus rapidement",
          "correct": false,
          "feedback": "Pas garanti.\n"
        },
        {
          "text": "En obligeant à utiliser partout des types statiques très forts",
          "correct": false,
          "feedback": "Pas spécifiquement.\n"
        }
      ],
      "explanation": "Citation : « Si vous ne pouvez pas\nspécifier ce que doit faire votre code,\nvous ne savez pas ce qu'il doit faire. »\nConséquence directe : difficile à coder,\ntester, déboguer."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "jeu-de-tests",
        "cas-limites"
      ],
      "title": "Choisir un jeu de tests",
      "partie": "tests",
      "statement": "La spécification de `tarif(age)` indique : « renvoie $5$ si `age` est strictement inférieur à $18$, et $8$ sinon ». Quel jeu de tests vérifie le mieux cette spécification ?",
      "options": [
        {
          "text": "`tarif(10) == 5`, `tarif(12) == 5`, `tarif(15) == 5`",
          "correct": false,
          "feedback": "Erreur : les trois tests portent sur le même cas, celui des moins de $18$ ans. Une fonction qui renverrait toujours $5$ les réussirait tous.\n"
        },
        {
          "text": "`tarif(17) == 5`, `tarif(18) == 8`, `tarif(40) == 8`",
          "correct": true,
          "feedback": "Bonne réponse : les deux cas de la spécification sont testés, ainsi que la frontière entre eux, $17$ et $18$, où se glissent les erreurs du type `<=` au lieu de `<`.\n"
        },
        {
          "text": "`tarif(30) == 8`, `tarif(50) == 8`, `tarif(70) == 8`",
          "correct": false,
          "feedback": "Erreur : aucun test ne porte sur les moins de $18$ ans. Une fonction qui renverrait toujours $8$ passerait ce jeu de tests sans encombre.\n"
        },
        {
          "text": "`tarif(10) == 5`, `tarif(18) == 5`, `tarif(40) == 8`",
          "correct": false,
          "feedback": "Erreur : le test `tarif(18) == 5` contredit la spécification, puisque $18$ n'est pas strictement inférieur à $18$. Une fonction correcte échouerait à ce test.\n"
        }
      ],
      "explanation": "Un bon jeu de tests couvre chaque cas de la spécification et ses frontières, avec des valeurs attendues conformes au texte. Même ainsi, le succès d'un jeu de tests ne garantit pas la correction du programme."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "optimisation-spec"
      ],
      "title": "Spécification ambiguë",
      "partie": "contrat",
      "statement": "Quel problème a la spécification suivante ?\n« La fonction `chercher(liste, valeur)` cherche\nla valeur dans la liste. »",
      "options": [
        {
          "text": "Aucun problème, cette spécification est parfaitement claire et complète",
          "correct": false,
          "feedback": "Erreur : trop ambiguë.\n"
        },
        {
          "text": "Trop d'ambiguïtés sur ce qu'il faut renvoyer selon les cas",
          "correct": true,
          "feedback": "Bonne réponse : une bonne spec doit\nlever toutes les ambiguïtés. Sinon,\ndeux développeurs feront deux\nimplémentations différentes,\nincompatibles.\n"
        },
        {
          "text": "La fonction n'a pas assez de paramètres",
          "correct": false,
          "feedback": "La signature `chercher(liste,\nvaleur)` est cohérente :\ndeux paramètres suffisent\npour décrire la recherche.\nLe défaut de cette spec\ntient à l'imprécision de\nl'énoncé, pas au nombre\nde paramètres.\n"
        },
        {
          "text": "Le nom de la fonction est trop long",
          "correct": false,
          "feedback": "Pas le problème.\n"
        }
      ],
      "explanation": "Signe d'une bonne spec : un autre\ndéveloppeur peut implémenter la fonction\n**sans poser de question** et son code\npasse les tests fournis."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "post-erreur"
      ],
      "title": "Cas d'erreur",
      "partie": "conditions",
      "statement": "Comment doit-on **spécifier le comportement en cas d'erreur** d'une fonction ?",
      "options": [
        {
          "text": "Laisser le programme planter brutalement, sans rien prévoir de précis",
          "correct": false,
          "feedback": "Possible (exception non rattrapée),\nmais à documenter.\n"
        },
        {
          "text": "Renvoyer aléatoirement",
          "correct": false,
          "feedback": "Un comportement aléatoire\nen cas d'erreur rendrait\nla fonction imprévisible\net inutilisable. Une bonne\nspec définit toujours un\ncomportement déterministe :\nvaleur sentinelle, exception\nou précondition explicite.\n"
        },
        {
          "text": "Décider explicitement : valeur spéciale, exception ou précondition",
          "correct": true,
          "feedback": "Bonne réponse : trois stratégies\nprincipales. À documenter. Une fois\nchoisie, **être cohérent** dans tout\nle projet.\n"
        },
        {
          "text": "Ne pas y penser",
          "correct": false,
          "feedback": "Erreur classique : les bugs viennent\nsouvent de cas non spécifiés.\n"
        }
      ],
      "explanation": "Trois écoles : Python privilégie les\n**exceptions**, comme l'erreur `ValueError`\nque provoque `int(\"abc\")` ; C/Go préfèrent\nles **codes de retour** ; certains langages\nforcent des types `Result`/`Option` (Rust)."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "refactoring"
      ],
      "title": "Spec et évolution",
      "partie": "contrat",
      "statement": "Pourquoi une spécification claire facilite-t-elle\nla **maintenance** d'un programme ?",
      "options": [
        {
          "text": "On peut réécrire l'implémentation sans changer le contrat",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'**abstraction**.\nTant que la spec est respectée, le code\nappelant n'est pas affecté. Permet\nd'optimiser, refactoriser, corriger\nsans cascade de changements.\n"
        },
        {
          "text": "Parce qu'une spécification est jolie à lire",
          "correct": false,
          "feedback": "L'esthétique d'un\ndocument est un\ncritère subjectif. Ce\nn'est pas l'argument\nqui justifie l'intérêt\npratique d'une\nspécification.\n"
        },
        {
          "text": "Parce que le code obtenu est plus court",
          "correct": false,
          "feedback": "La longueur du code n'a\npas de lien direct avec\nla qualité de la\nspécification.\n"
        },
        {
          "text": "Parce qu'elle élimine totalement et définitivement les bugs",
          "correct": false,
          "feedback": "Cette affirmation est\ntrop optimiste. La\nspécification ne supprime\npas tous les bugs, mais\nelle **structure** le\ntravail et facilite\nleur détection.\n"
        }
      ],
      "explanation": "Principe de **boîte noire** : la fonction\nest opaque pour l'appelant. Sa spec est\nl'interface, son implémentation reste\ninterne. C'est un pilier de la\nprogrammation modulaire."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "partie": "tests",
      "statement": "Parmi les affirmations suivantes sur la\nspécification, laquelle est **fausse** ?",
      "options": [
        {
          "text": "La précondition est ce que la fonction doit garantir à l'appel",
          "correct": true,
          "feedback": "Faux (donc bonne réponse) : la\nprécondition est une condition que\nl'**appelant** doit garantir avant\nl'appel. La fonction garantit la\n**postcondition** en sortie.\n"
        },
        {
          "text": "La docstring permet d'accéder à la documentation via `help()`",
          "correct": false,
          "feedback": "Cette affirmation est correcte : la docstring placée en tête d'une fonction est affichée par `help()`. Ce n'est donc pas la mauvaise affirmation recherchée.\n"
        },
        {
          "text": "La signature comprend nom, paramètres et type de retour",
          "correct": false,
          "feedback": "Vrai : définition standard.\n"
        },
        {
          "text": "Une bonne spécification facilite la maintenance et la robustesse",
          "correct": false,
          "feedback": "Cette affirmation est\njuste : une spécification\nclaire réduit les\nambiguïtés, simplifie la\nmaintenance et limite\nles bogues. Ce n'est\ndonc pas la mauvaise\naffirmation recherchée.\n"
        }
      ],
      "explanation": "Mnémonique : **pré** comme « avant »\n(responsabilité de l'appelant), **post**\ncomme « après » (responsabilité de la\nfonction)."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "effet-de-bord"
      ],
      "title": "Effet de bord vs valeur de retour",
      "partie": "contrat",
      "statement": "Une fonction de tri peut être spécifiée de\ndeux façons différentes. Comment la\nspécification doit-elle distinguer\nexplicitement ces deux choix ?",
      "options": [
        {
          "text": "La spécification précise si la fonction modifie la liste ou en renvoie une copie triée",
          "correct": true,
          "feedback": "Bonne réponse : c'est exactement la\ndistinction faite par Python entre\n`liste.sort()` (en place, retourne `None`)\net `sorted(liste)` (renvoie une nouvelle\nliste). La spécification doit toujours\npréciser cela, sinon le comportement est\nambigu et bogué en pratique.\n"
        },
        {
          "text": "La distinction se fait au moment de l'exécution, selon la mémoire disponible sur la machine",
          "correct": false,
          "feedback": "Erreur : ce serait imprévisible et\nrendrait le comportement non\nreproductible. Une bonne spécification\nfixe le comportement à l'avance, pas en\nfonction de l'environnement.\n"
        },
        {
          "text": "La distinction est purement esthétique et n'a pas d'effet réel",
          "correct": false,
          "feedback": "Erreur : la différence est\nfondamentale. Modifier en place ou\nrenvoyer une copie n'a pas le même coût\nmémoire, et expose à des erreurs\nd'aliasage si plusieurs variables\nréférencent la même liste.\n"
        },
        {
          "text": "La distinction n'a pas d'importance, l'utilisateur s'adapte au comportement obtenu",
          "correct": false,
          "feedback": "Erreur : c'est exactement l'origine de\nnombreux bugs en pratique. Si la\nspécification ne dit pas si la liste est\nmodifiée ou si une nouvelle liste est\nrenvoyée, l'utilisateur peut écrire\n`liste = trier(liste)` alors que\n`trier` modifie déjà `liste` (et renvoie\n`None`), ce qui détruit la liste.\n"
        }
      ],
      "explanation": "Convention Python : un nom impératif comme\n`sort` désigne souvent une modification en\nplace ; un nom au participe passé comme\n`sorted` désigne une fonction pure qui\nrenvoie un nouvel objet. Toute\nspécification doit préciser explicitement\nce point."
    },
    {
      "id": "q27",
      "difficulty": 3,
      "skills": [
        "contrat-detail"
      ],
      "title": "Précondition, postcondition, invariant",
      "partie": "contrat",
      "statement": "Dans une fonction qui parcourt une liste avec\nune boucle, comment se distinguent\n**précondition**, **postcondition** et\n**invariant de boucle** ?",
      "options": [
        {
          "text": "L'invariant de boucle remplace à lui seul la précondition et la postcondition de la fonction",
          "correct": false,
          "feedback": "Erreur : les trois notions coexistent.\nL'invariant de boucle est précisément\nl'outil qui fait le pont entre\nprécondition et postcondition à\ntravers la boucle.\n"
        },
        {
          "text": "La précondition porte sur l'entrée, la postcondition sur la sortie, l'invariant sur la boucle",
          "correct": true,
          "feedback": "Bonne réponse : trois échelles\ndifférentes. La précondition est le\ncontrat à l'entrée de la fonction, la\npostcondition le contrat à la sortie, et\nl'invariant de boucle un outil interne\nqui sert à prouver la postcondition à\npartir de la précondition.\n"
        },
        {
          "text": "La précondition est une postcondition imprécise",
          "correct": false,
          "feedback": "Erreur : ces deux notions ne sont pas\ncomparables, elles ont un statut\nsymétrique. La précondition est garantie\npar l'appelant, la postcondition par la\nfonction.\n"
        },
        {
          "text": "Ce sont trois noms différents pour la même notion",
          "correct": false,
          "feedback": "Erreur : ces trois notions ne portent\npas sur la même chose. Elles concernent\nrespectivement l'entrée de la fonction,\nsa sortie, et la boucle interne.\n"
        }
      ],
      "explanation": "Schéma général d'une preuve : précondition à\nl'entrée → invariant initialement vrai →\ninvariant conservé à chaque itération → en\nsortie de boucle, l'invariant combiné à la\ncondition d'arrêt entraîne la postcondition.\nLes trois notions s'articulent et ne se\nremplacent pas."
    },
    {
      "id": "q28",
      "difficulty": 1,
      "skills": [
        "tests",
        "utilite"
      ],
      "title": "Tester une fonction que l'on croit juste",
      "partie": "tests",
      "statement": "Lina a relu sa fonction trois fois et la trouve correcte. Pourquoi\nécrire quand même des tests ?",
      "options": [
        {
          "text": "Parce qu'une relecture ne remplace pas une exécution",
          "correct": true,
          "feedback": "Bonne réponse : les erreurs de frontière, d'indice ou de type\néchappent souvent à la lecture. Un test exécute réellement la\nfonction et compare le résultat à la spécification.\n"
        },
        {
          "text": "Parce que Python exige des tests pour exécuter une fonction",
          "correct": false,
          "feedback": "Erreur : Python n'impose rien ; les tests sont une discipline du\nprogrammeur.\n"
        },
        {
          "text": "Pour rendre la fonction plus rapide à l'exécution",
          "correct": false,
          "feedback": "Erreur : tester ne change pas la fonction, cela la vérifie.\n"
        },
        {
          "text": "Uniquement si la fonction fait plus de dix lignes",
          "correct": false,
          "feedback": "Erreur : une fonction courte peut cacher une erreur de\nfrontière.\n"
        }
      ],
      "explanation": "Un test confronte la fonction à sa spécification sur des cas\nchoisis : c'est le moyen le plus sûr de détecter une erreur avant\nles utilisateurs."
    },
    {
      "id": "q29",
      "difficulty": 2,
      "skills": [
        "tests",
        "limites",
        "preuve"
      ],
      "title": "Ce que prouve un test réussi",
      "partie": "tests",
      "statement": "Les dix tests de la fonction `tri` passent tous. Que peut-on en\nconclure ?",
      "options": [
        {
          "text": "Qu'aucun de ces dix cas ne révèle d'erreur, rien de plus",
          "correct": true,
          "feedback": "Bonne réponse : un test montre la présence d'une erreur quand il\néchoue, pas son absence quand il réussit. D'autres cas peuvent\nencore échouer.\n"
        },
        {
          "text": "Que la fonction est correcte pour toutes les listes",
          "correct": false,
          "feedback": "Erreur : dix cas ne couvrent pas l'infinité des listes\npossibles.\n"
        },
        {
          "text": "Que la fonction est rapide sur ces dix listes",
          "correct": false,
          "feedback": "Erreur : les tests vérifient le résultat, pas la vitesse.\n"
        },
        {
          "text": "Que la spécification est bien écrite et complète",
          "correct": false,
          "feedback": "Erreur : des tests peuvent réussir sur une fonction qui respecte\nune spécification fausse.\n"
        }
      ],
      "explanation": "Les tests augmentent la confiance ; seule une preuve, par invariant\npar exemple, établit la correction."
    },
    {
      "id": "q30",
      "difficulty": 2,
      "skills": [
        "tests",
        "frontiere",
        "cas-limite"
      ],
      "title": "La valeur à tester en priorité",
      "partie": "tests",
      "statement": "La fonction `est_majeur(age)` doit renvoyer `True` à partir de\n$18$ ans. Quelle valeur de `age` est la plus importante à tester ?",
      "options": [
        {
          "text": "$18$, la frontière entre les deux réponses",
          "correct": true,
          "feedback": "Bonne réponse : c'est là qu'une confusion entre `>` et `>=`\nse révèle. Les valeurs $17$ et $18$ testent les deux côtés de\nla frontière.\n"
        },
        {
          "text": "$40$, un adulte typique, loin de toute frontière",
          "correct": false,
          "feedback": "Erreur : une valeur loin de la frontière passe même avec une\ncomparaison mal écrite.\n"
        },
        {
          "text": "$5$, un enfant, loin de toute frontière",
          "correct": false,
          "feedback": "Erreur : même raison, $5$ ne distingue pas `>` de `>=`.\n"
        },
        {
          "text": "$100$, pour tester une grande valeur",
          "correct": false,
          "feedback": "Erreur : rien de particulier ne se passe à $100$ pour cette\nfonction.\n"
        }
      ],
      "explanation": "Les cas limites, aux frontières de la spécification, sont ceux qui\nrévèlent le plus d'erreurs."
    },
    {
      "id": "q31",
      "difficulty": 3,
      "skills": [
        "tests",
        "jeu-de-tests",
        "couverture"
      ],
      "title": "Un jeu de tests pour une fonction sur les listes",
      "partie": "tests",
      "statement": "La fonction `maximum(t)` renvoie le plus grand élément d'une liste\nnon vide. Quel jeu de tests couvre le mieux les cas à risque ?",
      "options": [
        {
          "text": "Un élément seul, le maximum en dernière position, des doublons du maximum",
          "correct": true,
          "feedback": "Bonne réponse : ces cas visent les erreurs classiques :\ninitialisation, parcours incomplet, comparaison stricte ou\nlarge.\n"
        },
        {
          "text": "Trois listes de cinq entiers tirés au hasard entre $0$ et $100$",
          "correct": false,
          "feedback": "Erreur : des cas aléatoires passent à côté des situations\nparticulières où les bugs se cachent.\n"
        },
        {
          "text": "La liste vide, testée trois fois de suite",
          "correct": false,
          "feedback": "Erreur : la liste vide viole la précondition ; la tester ne dit\nrien de la fonction, et un seul cas ne vaut pas trois.\n"
        },
        {
          "text": "Une seule liste longue, qui contient forcément tous les cas",
          "correct": false,
          "feedback": "Erreur : une longue liste n'a qu'un seul maximum et qu'une seule\nposition pour lui.\n"
        }
      ],
      "explanation": "Concevoir un jeu de tests, c'est imaginer comment la fonction\npourrait se tromper et écrire un cas pour chaque hypothèse."
    },
    {
      "id": "q32",
      "difficulty": 3,
      "skills": [
        "tests",
        "aleatoire",
        "propriete"
      ],
      "title": "Tester une fonction aléatoire",
      "partie": "tests",
      "statement": "La fonction `de()` renvoie un entier aléatoire entre $1$ et $6$.\nComment la tester, puisque son résultat change à chaque appel ?",
      "options": [
        {
          "text": "En vérifiant sur mille appels que le résultat est entre $1$ et $6$",
          "correct": true,
          "feedback": "Bonne réponse : faute de valeur attendue, on teste une\npropriété que tout résultat doit respecter, sur un grand nombre\nd'appels.\n"
        },
        {
          "text": "En vérifiant que `de()` vaut $3$, la valeur centrale",
          "correct": false,
          "feedback": "Erreur : le résultat n'est pas fixé, ce test échouerait\nsouvent.\n"
        },
        {
          "text": "En ne la testant pas, l'aléatoire étant intestable",
          "correct": false,
          "feedback": "Erreur : les propriétés d'une fonction aléatoire se testent.\n"
        },
        {
          "text": "En vérifiant qu'elle renvoie toujours la même valeur",
          "correct": false,
          "feedback": "Erreur : ce serait précisément un signe de défaillance.\n"
        }
      ],
      "explanation": "Quand la valeur exacte est imprévisible, on teste ce qui doit\ntoujours être vrai : bornes, type, répartition approximative."
    },
    {
      "id": "q33",
      "difficulty": 1,
      "skills": [
        "precondition",
        "responsabilite"
      ],
      "title": "Qui garantit la précondition",
      "partie": "conditions",
      "statement": "La spécification de `racine(x)` indique la précondition « `x` est\npositif ou nul ». À qui revient-il de la respecter ?",
      "options": [
        {
          "text": "À celui qui appelle la fonction",
          "correct": true,
          "feedback": "Bonne réponse : la précondition est une obligation de\nl'appelant ; en échange, la fonction s'engage sur sa\npostcondition.\n"
        },
        {
          "text": "À la fonction, qui doit corriger les arguments invalides",
          "correct": false,
          "feedback": "Erreur : la fonction peut vérifier, mais pas inventer un\nrésultat pour un argument interdit.\n"
        },
        {
          "text": "À Python, qui refuse les arguments invalides",
          "correct": false,
          "feedback": "Erreur : Python ne connaît pas la spécification.\n"
        },
        {
          "text": "À personne, une précondition n'étant qu'une suggestion",
          "correct": false,
          "feedback": "Erreur : hors de la précondition, le comportement n'est pas\ngaranti.\n"
        }
      ],
      "explanation": "Le contrat partage les responsabilités : l'appelant respecte la\nprécondition, la fonction assure la postcondition."
    },
    {
      "id": "q34",
      "difficulty": 2,
      "skills": [
        "postcondition",
        "maximum"
      ],
      "title": "La postcondition d'un maximum",
      "partie": "conditions",
      "statement": "Quelle postcondition décrit complètement le résultat `m` de\n`maximum(t)`, pour une liste `t` non vide ?",
      "options": [
        {
          "text": "`m` est dans `t` et aucun élément de `t` ne dépasse `m`",
          "correct": true,
          "feedback": "Bonne réponse : les deux conditions sont nécessaires. Sans la\npremière, renvoyer un nombre énorme conviendrait ; sans la\nseconde, n'importe quel élément conviendrait.\n"
        },
        {
          "text": "`m` est plus grand que le premier élément de `t`",
          "correct": false,
          "feedback": "Erreur : cette condition est insuffisante et même fausse quand\nle premier élément est le maximum.\n"
        },
        {
          "text": "`m` est un entier positif, comme toutes les notes",
          "correct": false,
          "feedback": "Erreur : le maximum d'une liste de négatifs est négatif, et\ncette condition ne dit rien du lien avec `t`.\n"
        },
        {
          "text": "`m` est le dernier élément de `t`, la liste étant triée",
          "correct": false,
          "feedback": "Erreur : le maximum peut être n'importe où.\n"
        }
      ],
      "explanation": "Une bonne postcondition est vérifiable et suffisante : elle\ncaractérise exactement le résultat attendu."
    },
    {
      "id": "q35",
      "difficulty": 2,
      "skills": [
        "precondition",
        "restriction",
        "utilite"
      ],
      "title": "Une précondition trop stricte",
      "partie": "conditions",
      "statement": "Pour `moyenne(notes)`, Sami écrit la précondition « `notes`\ncontient exactement cinq entiers entre $0$ et $20$ ». Quel est le\nproblème ?",
      "options": [
        {
          "text": "Elle interdit des usages légitimes, comme une liste de trois notes",
          "correct": true,
          "feedback": "Bonne réponse : une précondition doit être juste assez\nrestrictive pour que la fonction marche ; ici « liste non vide\nde nombres » suffit.\n"
        },
        {
          "text": "Une précondition ne peut pas mentionner la longueur d'une liste",
          "correct": false,
          "feedback": "Erreur : elle le peut ; la question est de savoir si c'est\nnécessaire.\n"
        },
        {
          "text": "La fonction ne peut pas calculer une moyenne de cinq valeurs",
          "correct": false,
          "feedback": "Erreur : elle le peut très bien, et bien d'autres tailles\naussi.\n"
        },
        {
          "text": "Les notes devraient être des flottants plutôt que des entiers",
          "correct": false,
          "feedback": "Erreur : le type des notes n'est pas le problème.\n"
        }
      ],
      "explanation": "Une précondition minimale rend la fonction réutilisable ; une\nprécondition excessive la condamne à un seul usage."
    },
    {
      "id": "q36",
      "difficulty": 3,
      "skills": [
        "effet-de-bord",
        "specification",
        "liste"
      ],
      "title": "Spécifier une fonction qui modifie son argument",
      "partie": "conditions",
      "statement": "La fonction `trier_sur_place(t)` réordonne la liste `t` reçue et ne\nrenvoie rien. Que doit préciser sa spécification, en plus de la\nprécondition ?",
      "options": [
        {
          "text": "Qu'elle modifie la liste passée en argument, son effet de bord",
          "correct": true,
          "feedback": "Bonne réponse : l'appelant doit savoir que sa liste sera\nchangée et qu'aucune valeur ne sera renvoyée, sous peine\nd'écrire `t = trier_sur_place(t)` et de perdre sa liste.\n"
        },
        {
          "text": "Le nombre de comparaisons effectuées",
          "correct": false,
          "feedback": "Erreur : le coût peut être documenté, mais ce n'est pas\nl'information essentielle pour utiliser la fonction.\n"
        },
        {
          "text": "Le nom de la variable à utiliser par l'appelant",
          "correct": false,
          "feedback": "Erreur : l'appelant nomme ses variables comme il veut.\n"
        },
        {
          "text": "Rien, une fonction sans `return` n'ayant pas besoin de spécification",
          "correct": false,
          "feedback": "Erreur : c'est justement une fonction à effet de bord qui doit\nle dire clairement.\n"
        }
      ],
      "explanation": "Un effet de bord fait partie du contrat : la docstring doit annoncer\nce que la fonction modifie en dehors d'elle."
    },
    {
      "id": "q37",
      "difficulty": 3,
      "skills": [
        "postcondition",
        "tri",
        "specification"
      ],
      "title": "La postcondition complète d'un tri",
      "partie": "conditions",
      "statement": "Pour une fonction `trier(t)` qui renvoie une nouvelle liste, la\npostcondition « le résultat est trié par ordre croissant » est-elle\nsuffisante ?",
      "options": [
        {
          "text": "Non, le résultat doit aussi contenir exactement les éléments de `t`",
          "correct": true,
          "feedback": "Bonne réponse : sans cette condition, renvoyer la liste vide\nrespecterait la postcondition. Un tri conserve les éléments et\nleurs répétitions.\n"
        },
        {
          "text": "Oui, être trié suffit à définir le résultat d'un tri",
          "correct": false,
          "feedback": "Erreur : une liste vide ou `[1, 2, 3]` sont triées sans avoir de\nrapport avec `t`.\n"
        },
        {
          "text": "Non, il faut préciser l'algorithme de tri utilisé",
          "correct": false,
          "feedback": "Erreur : la spécification décrit le résultat, pas la méthode.\n"
        },
        {
          "text": "Non, il faut imposer que `t` soit modifiée sur place",
          "correct": false,
          "feedback": "Erreur : la fonction renvoie une nouvelle liste ; `t` doit au\ncontraire rester intacte.\n"
        }
      ],
      "explanation": "Une postcondition se vérifie en cherchant les résultats absurdes\nqu'elle laisserait passer ; chaque faille appelle une condition\nsupplémentaire."
    },
    {
      "id": "q38",
      "difficulty": 1,
      "skills": [
        "docstring",
        "contenu"
      ],
      "title": "La première ligne d'une docstring",
      "partie": "contrat",
      "statement": "Que doit dire, en premier, la docstring d'une fonction ?",
      "options": [
        {
          "text": "Ce que fait la fonction, en une phrase",
          "correct": true,
          "feedback": "Bonne réponse : la première ligne résume le rôle de la\nfonction ; viennent ensuite les paramètres, la précondition et\nle résultat.\n"
        },
        {
          "text": "Le nom de son auteur et son adresse",
          "correct": false,
          "feedback": "Erreur : l'auteur peut figurer ailleurs, mais ce n'est pas ce\nque cherche l'utilisateur de la fonction.\n"
        },
        {
          "text": "Le détail de chaque ligne de code",
          "correct": false,
          "feedback": "Erreur : la docstring décrit l'usage, pas l'implémentation.\n"
        },
        {
          "text": "La date d'écriture et de dernière modification",
          "correct": false,
          "feedback": "Erreur : la date n'aide pas à utiliser la fonction.\n"
        }
      ],
      "explanation": "Une docstring répond à la question « que fait cette fonction et\ncomment l'appeler ? », sans entrer dans le code."
    },
    {
      "id": "q39",
      "difficulty": 3,
      "skills": [
        "specification",
        "valeur-de-retour",
        "absence"
      ],
      "title": "Que renvoyer quand il n'y a rien à renvoyer",
      "partie": "contrat",
      "statement": "La fonction `chercher(t, x)` renvoie l'indice de `x` dans `t`.\nQue doit préciser sa spécification pour le cas où `x` est absent ?",
      "options": [
        {
          "text": "La valeur renvoyée en cas d'absence, par exemple `-1` ou `None`",
          "correct": true,
          "feedback": "Bonne réponse : sans cette précision, l'appelant ne sait pas\ncomment reconnaître l'absence, et des choix différents selon les\nversions casseraient les programmes.\n"
        },
        {
          "text": "Rien, l'absence étant une erreur de l'appelant, hors contrat",
          "correct": false,
          "feedback": "Erreur : chercher une valeur absente est un usage normal, pas\nune violation de précondition.\n"
        },
        {
          "text": "Que la fonction affiche un message signalant l'absence",
          "correct": false,
          "feedback": "Erreur : afficher n'aide pas le programme appelant, qui a besoin\nd'une valeur exploitable.\n"
        },
        {
          "text": "Que la fonction renvoie `0`, premier indice valide",
          "correct": false,
          "feedback": "Erreur : `0` est un indice possible pour `x` présent ; il ne\npeut pas signaler l'absence.\n"
        }
      ],
      "explanation": "Les cas particuliers font partie de la spécification : le résultat\ndoit être défini pour toute entrée respectant la précondition."
    }
  ]
}