{
  "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",
          "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\ndoit faire**. Les deux\nnotions sont\ndistinctes.\n"
        },
        {
          "text": "Son nombre total de lignes",
          "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",
          "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": "La description précise de ce que la fonction doit faire (paramètres attendus, valeur renvoyée, conditions d'utilisation), indépendamment de l'implémentation",
          "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 (avec leurs types) 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",
          "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 de boucle",
          "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 qui doit être vraie avant l'appel de la fonction (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 après l'appel",
          "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 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 la valeur de retour (ou sur l'effet) après son exécution, si la précondition est respectée",
          "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é en fin de 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 (chaîne entre triples guillemets) 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):\n    return abs(x)\n```\n",
          "correct": false,
          "feedback": "Code correct mais non documenté. La\nspécification est implicite.\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. Code self-documenting.\n"
        },
        {
          "text": "```python\ndef valeur_absolue(x):\n    # implementation\n    pass\n```\n",
          "correct": false,
          "feedback": "Code non implémenté.\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 obligatoire",
          "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 de la fonction, faciliter la conception, permettre les tests, et servir de 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": "q08",
      "difficulty": 1,
      "skills": [
        "annotations-types"
      ],
      "title": "Annotations de type",
      "statement": "Que produisent les **annotations de type** en\nPython (`x: int`) ?",
      "options": [
        {
          "text": "Une erreur de syntaxe",
          "correct": false,
          "feedback": "Erreur : syntaxe valide depuis Python 3.5.\n"
        },
        {
          "text": "Une vérification automatique à l'exécution",
          "correct": false,
          "feedback": "Erreur : Python n'applique pas les\nannotations à l'exécution.\n"
        },
        {
          "text": "Une transformation du type",
          "correct": false,
          "feedback": "Erreur : pas de coercition.\n"
        },
        {
          "text": "Une documentation du type attendu, vérifiable par des outils externes (`mypy`)",
          "correct": true,
          "feedback": "Bonne réponse : annotations\ninformatives, sans effet à l'exécution\npar défaut. Mais des outils statiques\npeuvent les vérifier (`mypy`,\n`pyright`).\n"
        }
      ],
      "explanation": "Annotations utiles pour : lisibilité, IDE\n(auto-complétion, détection d'erreurs),\ntests statiques. À combiner avec une\ndocstring claire."
    },
    {
      "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": "À fournir des exemples d'utilisation qui peuvent être exécutés automatiquement comme tests par le module `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 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 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, liste à un élément, liste 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": "q11",
      "difficulty": 2,
      "skills": [
        "contrat"
      ],
      "title": "Programmation par contrat",
      "statement": "Que signifie « **programmation par contrat** » ?",
      "options": [
        {
          "text": "Rédiger un contrat juridique pour chaque programme",
          "correct": false,
          "feedback": "Le mot « contrat » est\nici employé au sens\ninformatique, sans\nrapport avec le droit.\n"
        },
        {
          "text": "Programmer à plusieurs au sein d'une même équipe",
          "correct": false,
          "feedback": "Cette interprétation\nlittérale est sans\nrapport avec le sens\ntechnique de\nl'expression\n« programmation par\ncontrat ».\n"
        },
        {
          "text": "Concevoir chaque fonction avec un contrat explicite (préconditions, postconditions, invariants), respecté par l'appelant et par la fonction",
          "correct": true,
          "feedback": "Bonne réponse : approche introduite par\nBertrand Meyer (langage Eiffel).\nChacune des deux parties (appelant,\nfonction) a des obligations.\n"
        },
        {
          "text": "Programmer en échange d'une rémunération financière",
          "correct": false,
          "feedback": "Cette interprétation\nlittérale n'a aucun\nrapport avec le sens\ntechnique du terme.\n"
        }
      ],
      "explanation": "Bénéfices : robustesse, débogage facile\n(violation de contrat = origine du bug).\nOutils : `assert` en Python pour vérifier\npré/post à l'exécution."
    },
    {
      "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": "Une description du but, des paramètres (avec types), de la valeur renvoyée, des conditions d'utilisation et idéalement 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 de la fonction",
          "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 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 de la fonction et sa docstring (si elle en a une)",
          "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 système de sécurité physique",
          "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 explicitement dans la fonction que les préconditions sont respectées (lever une exception si ce n'est pas le cas), au lieu de faire confiance à l'appelant",
          "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    if x < 0:\n        raise ValueError(\"x doit être ≥ 0\")\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 permettre les vérifications statiques (`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 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 m(l):`",
          "correct": false,
          "feedback": "Noms peu informatifs. Pas idéal.\n"
        },
        {
          "text": "`def moyenne(x):`",
          "correct": false,
          "feedback": "Acceptable mais sans typage. On peut\nmieux faire.\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 calc(*args, kwargs):`",
          "correct": false,
          "feedback": "Trop générique : on ne sait pas ce que\nla fonction fait.\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\nlimite** 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 provoque une division 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$",
          "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 forçant à réfléchir aux entrées valides, aux cas limites et aux comportements attendus, ce qui réduit les bugs et facilite les tests",
          "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 des types 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": "Annotations de type + `mypy` (vérification statique), `assert` (vérification à l'exécution), `doctest` (tests d'exemples), `unittest`/`pytest` (tests unitaires)",
          "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",
          "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",
          "correct": false,
          "feedback": "Erreur : trop ambiguë.\n"
        },
        {
          "text": "Plusieurs zones d'ombre : que renvoyer si trouvée (indice, booléen) ? Si plusieurs occurrences (la première, toutes) ? Si absente (None, -1, exception) ?",
          "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\ncas d'erreur** d'une fonction ?",
      "options": [
        {
          "text": "Faire planter le programme",
          "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 de retour spéciale (`None`, `-1`), exception levée, ou contrat (précondition que l'utilisateur doit garantir)",
          "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** (`raise ValueError(...)`) ;\nC/Go préfèrent les **codes de retour** ;\ncertains langages forcent des types\n`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": "Parce qu'on peut réécrire l'implémentation sans changer le contrat, donc sans casser le code qui l'utilise",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'**abstraction**.\nTant que la spec est respectée, l'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 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": "q24",
      "difficulty": 3,
      "skills": [
        "test-d-abord"
      ],
      "title": "Développement piloté par les tests",
      "statement": "En quoi consiste l'approche dite de\n**développement piloté par les tests** ?",
      "options": [
        {
          "text": "Tester manuellement le programme une fois qu'il est entièrement écrit",
          "correct": false,
          "feedback": "Cette description correspond à un test\nmanuel classique, et non à l'approche\npilotée par les tests, qui inverse\nprécisément la chronologie habituelle.\n"
        },
        {
          "text": "Faire générer automatiquement les tests par un programme d'intelligence artificielle",
          "correct": false,
          "feedback": "Cette approche est conduite par un\nhumain, et non par un système\nautomatique. Le programmeur écrit\nlui-même les tests à partir de la\nspécification.\n"
        },
        {
          "text": "Écrire d'abord les tests à partir de la spécification, puis le code juste suffisant pour les faire passer, et enfin retravailler la structure du code à tests inchangés",
          "correct": true,
          "feedback": "Cette démarche en trois étapes (test,\ncode, refactorisation) oblige à bien\nspécifier le comportement attendu avant\nde coder. Elle s'oppose à l'approche\nintuitive, qui consiste à coder\nd'abord et tester ensuite.\n"
        },
        {
          "text": "Exécuter les tests sur un seul ordinateur, plutôt que sur plusieurs",
          "correct": false,
          "feedback": "Le matériel utilisé pour exécuter les\ntests n'a aucun rapport avec cette\ndémarche.\n"
        }
      ],
      "explanation": "Les bénéfices de cette démarche sont un\ncode minimal et bien testé, ainsi qu'une\nconception guidée par le besoin réel.\nElle requiert toutefois un apprentissage et\nune discipline soutenue. Largement\nreconnue dans l'industrie, elle n'est pas\npour autant universelle."
    },
    {
      "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 une condition que la fonction doit garantir au moment de 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 indique clairement si la\nfonction modifie la liste reçue (effet\nde bord, exemple : `liste.sort()` qui\nrenvoie `None`), ou si elle construit\nune nouvelle liste triée sans toucher à\nla liste d'entrée (exemple : `sorted(liste)`)\n",
          "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, en fonction de la mémoire disponible",
          "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 adjectivé comme `sorted` ou\nparticipe `reversed` désigne une fonction\npure qui renvoie 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 la précondition et la postcondition",
          "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 les paramètres\nau moment de l'appel ; la postcondition\nporte sur la valeur renvoyée ou l'état\nfinal ; l'invariant de boucle porte sur\nune propriété conservée à chaque\nitération de la boucle interne\n",
          "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."
    }
  ]
}