{
  "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.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Spécification",
      "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",
      "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",
      "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",
      "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",
      "statement": "Comment **documenter** une fonction Python\nconformément à la PEP 257 ?",
      "options": [
        {
          "text": "Avec un commentaire placé tout à la fin de la fonction",
          "correct": false,
          "feedback": "La convention est de\nplacer la docstring en\ntout début de corps de\nfonction, juste après\nla signature.\n"
        },
        {
          "text": "Avec une docstring en première instruction du corps",
          "correct": true,
          "feedback": "Bonne réponse : `def f(x):` puis sur\nla ligne suivante `\"\"\"description...\"\"\"`.\nAccessible via `help(f)` ou `f.__doc__`.\n"
        },
        {
          "text": "Avec un commentaire `#` au-dessus",
          "correct": false,
          "feedback": "Lisible par l'humain mais inaccessible\ndynamiquement.\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",
      "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": "Acceptable mais moins idiomatique\n(pas accessible via `help`).\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\ncommentaires, invisibles pour `help()` et\nles outils. La forme attendue est une\ndocstring, chaîne entre triples guillemets\nplacé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 ?",
      "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": [
        "exemple-doctest"
      ],
      "title": "Doctest",
      "statement": "À quoi servent les exemples `>>> ...` dans une\ndocstring ?",
      "options": [
        {
          "text": "À déclarer des constantes du programme",
          "correct": false,
          "feedback": "La déclaration de\nconstantes se fait par\naffectation classique,\nen dehors de toute\ndocstring.\n"
        },
        {
          "text": "À donner des exemples exécutables comme tests par `doctest`",
          "correct": true,
          "feedback": "Bonne réponse : double avantage,\ndocumentation et tests vérifiés. Le\nmodule `doctest` extrait ces exemples\net vérifie qu'ils fonctionnent.\n"
        },
        {
          "text": "À rendre la documentation nettement plus jolie visuellement",
          "correct": false,
          "feedback": "Cette interprétation est\ntrop superficielle. Ces\nexemples ont un rôle\nfonctionnel précis,\ncomme expliqué dans la\nbonne réponse.\n"
        },
        {
          "text": "À signaler une erreur dans le code",
          "correct": false,
          "feedback": "Aucun rapport. Les\nexemples `>>>` sont des\nextraits exécutables,\nconçus pour illustrer\nle comportement attendu\nde la fonction.\n"
        }
      ],
      "explanation": "Format doctest :\n```python\n\"\"\"\n>>> carre(3)\n9\n\"\"\"\n```\nLancement : `python -m doctest fichier.py`."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "exhaustivite"
      ],
      "title": "Cas limites",
      "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",
      "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",
      "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 : informations utiles à\nl'utilisateur sans qu'il ait besoin de\nlire le code. Plusieurs styles de\ndocstring : Google, NumPy, Sphinx.\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": "Docstring minimale = description courte +\nparamètres + retour. Docstring complète =\najouter exemples, exceptions levées,\nremarques."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "help"
      ],
      "title": "help()",
      "statement": "Que fait `help(ma_fonction)` en Python ?",
      "options": [
        {
          "text": "Elle lance la documentation officielle en ligne sur Internet",
          "correct": false,
          "feedback": "Cette description est\nimprécise. La fonction\n`help` consulte la\ndocumentation locale,\nfournie par les\ndocstrings du programme,\net non une ressource en\nligne.\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\ndocumenter via docstring est utile.\nVisible directement dans\nl'interpréteur ou les IDE.\n"
        },
        {
          "text": "Renvoie le code source",
          "correct": false,
          "feedback": "Erreur : `help` montre la docstring,\n`inspect.getsource` donne le code.\n"
        }
      ],
      "explanation": "`help()` est un raccourci interactif. Sur\nles IDE modernes, on a la même chose au\nsurvol. Toujours documenter pour aider\nles futurs lecteurs (y compris soi-même)."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "defense"
      ],
      "title": "Programmation défensive",
      "statement": "Que signifie « programmation défensive » ?",
      "options": [
        {
          "text": "Programmer un véritable système de sécurité physique anti-intrusion",
          "correct": false,
          "feedback": "Cette interprétation\nlittérale ne correspond\npas au sens technique du\nterme en programmation.\n"
        },
        {
          "text": "S'abstenir d'écrire du code",
          "correct": false,
          "feedback": "La programmation\ndéfensive consiste au\ncontraire à ajouter des\nvérifications dans le\ncode, et non à se\ndispenser d'en écrire.\n"
        },
        {
          "text": "Vérifier dans la fonction que les préconditions sont respectées",
          "correct": true,
          "feedback": "Bonne réponse : compromis entre\nrobustesse et performance. Souvent\nrecommandé pour les fonctions exposées à\nun usage extérieur.\n"
        },
        {
          "text": "Ajouter des virus de défense au programme",
          "correct": false,
          "feedback": "Cette description\nfantaisiste n'a aucun\nrapport avec le sens\ntechnique du terme.\n"
        }
      ],
      "explanation": "```python\ndef racine(x):\n    assert x >= 0, \"x doit être positif ou nul\"\n    return x ** 0.5\n```\nPlus robuste, mais plus verbeux. À doser\nselon le contexte."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "type-retour"
      ],
      "title": "Type de retour",
      "statement": "Pourquoi spécifier le **type de retour** d'une\nfonction ?",
      "options": [
        {
          "text": "Pour clarifier ce que reçoit l'appelant et aider `mypy`",
          "correct": true,
          "feedback": "Bonne réponse : par exemple,\n`def chercher(...) -> int | None`\nindique qu'on peut recevoir `None`,\nce qui doit être traité par\nl'appelant.\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 : `int`, `float`,\n`str`, `bool`, `list[int]`, `dict[str,\nint]`, `Optional[int]` (= `int | None`).\nTrès utiles dans les gros projets."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "signature-correcte"
      ],
      "title": "Bonne signature",
      "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",
      "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",
      "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": [
        "outils"
      ],
      "title": "Outils Python",
      "statement": "Quels **outils Python** aident à respecter une\nspécification ?",
      "options": [
        {
          "text": "Les annotations, `mypy`, `assert`, `doctest` et `pytest`",
          "correct": true,
          "feedback": "Bonne réponse : écosystème riche.\nCombinés, ils permettent de garantir\nla conformité aux spécifications avec\nun fort niveau de confiance.\n"
        },
        {
          "text": "Aucun outil dédié n'existe vraiment pour cela en Python",
          "correct": false,
          "feedback": "Erreur : il existe plusieurs outils.\n"
        },
        {
          "text": "Un compilateur",
          "correct": false,
          "feedback": "Python n'est pas compilé en standard.\n"
        },
        {
          "text": "Seulement `print`",
          "correct": false,
          "feedback": "`print` aide à déboguer, pas à\nspécifier.\n"
        }
      ],
      "explanation": "`assert preconditon, \"message\"` est très\nutilisé pour documenter et vérifier les\ncontrats. À utiliser en développement,\néventuellement désactivable en production\navec `python -O`."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "optimisation-spec"
      ],
      "title": "Spécification ambiguë",
      "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",
      "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",
      "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",
      "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\ncorrecte : la docstring\nplacée en tête d'une\nfonction est récupérée\npar `help()` et par\nl'attribut `__doc__`.\nCe n'est donc pas la\nmauvaise affirmation\nrecherché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",
      "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",
      "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."
    }
  ]
}