{
  "chapter": {
    "id": "programmation-fonctionnelle",
    "level": "terminale",
    "theme": "Programmation",
    "title": "Programmation fonctionnelle",
    "description": "Paradigme fonctionnel, fonctions pures, immuabilité, fonctions\nde première classe et d'ordre supérieur (map, filter, reduce),\nexpressions lambda, récursivité, comparaison avec le paradigme\nimpératif.",
    "parties": [
      {
        "id": "principes",
        "title": "Fonctions pures et immuabilité"
      },
      {
        "id": "ordre-superieur",
        "title": "Fonctions d'ordre supérieur"
      },
      {
        "id": "paradigmes",
        "title": "Comparaison des paradigmes"
      }
    ],
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "paradigme"
      ],
      "title": "Paradigme fonctionnel",
      "partie": "paradigmes",
      "statement": "Quelle est l'idée centrale du **paradigme fonctionnel** ?",
      "options": [
        {
          "text": "Composer des fonctions en évitant les effets de bord",
          "correct": true,
          "feedback": "Bonne réponse : la programmation fonctionnelle\nprivilégie les fonctions sans effet de bord\n(« fonctions pures »), l'immutabilité des données et\nla composition de fonctions. Cela rend le code plus\nprévisible et plus facile à raisonner.\n"
        },
        {
          "text": "Programmer uniquement avec des boucles `for`",
          "correct": false,
          "feedback": "Erreur : la programmation fonctionnelle remplace\nplutôt les boucles par de la récursivité ou des\nfonctions d'ordre supérieur.\n"
        },
        {
          "text": "Programmer en suivant l'ordre exact des instructions ligne par ligne",
          "correct": false,
          "feedback": "Erreur : c'est le paradigme **impératif**, dont la\nprogrammation fonctionnelle est précisément une\nalternative.\n"
        },
        {
          "text": "Programmer en utilisant uniquement des objets et des classes",
          "correct": false,
          "feedback": "Erreur : c'est le paradigme **orienté objet**, pas\nfonctionnel.\n"
        }
      ],
      "explanation": "Origines : le lambda-calcul de Church ($1936$) et le\nlangage Lisp ($1958$). Aujourd'hui, des langages\npurement fonctionnels (Haskell, OCaml) coexistent avec\ndes langages multi-paradigmes (Python, JavaScript)."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "fonction-pure"
      ],
      "title": "Fonction pure",
      "partie": "principes",
      "statement": "Qu'est-ce qu'une **fonction pure** ?",
      "options": [
        {
          "text": "Même résultat pour les mêmes arguments, sans effet de bord",
          "correct": true,
          "feedback": "Bonne réponse : la pureté implique la prévisibilité\ntotale et la facilité de test. C'est aussi ce qui\npermet la mémoïsation transparente.\n"
        },
        {
          "text": "Une fonction qui modifie directement ses propres arguments",
          "correct": false,
          "feedback": "Erreur : c'est l'inverse d'une fonction pure. Modifier\nses arguments est un effet de bord, ce qui rend la\nfonction impure.\n"
        },
        {
          "text": "Une fonction écrite uniquement en Python",
          "correct": false,
          "feedback": "Erreur : la pureté est indépendante du langage.\n"
        },
        {
          "text": "Une fonction qui ne contient aucun bug",
          "correct": false,
          "feedback": "Erreur : « pure » est un terme technique précis,\npas un jugement de qualité.\n"
        }
      ],
      "explanation": "Fonction pure typique : `def carre(x): return x * x`.\nFonction **impure** : `def ajoute_a_liste(L, x): L.append(x)`\nmodifie son argument, ce qui est un effet de bord."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "immuabilite"
      ],
      "title": "Immutabilité",
      "partie": "principes",
      "statement": "Que signifie qu'une donnée est **immuable** ?",
      "options": [
        {
          "text": "Elle est stockée en permanence sur le disque dur",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec le stockage.\n"
        },
        {
          "text": "Elle est protégée par un mot de passe",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec la sécurité. C'est une\npropriété structurelle de la donnée.\n"
        },
        {
          "text": "Elle ne peut pas être modifiée après sa création",
          "correct": true,
          "feedback": "Bonne réponse : pour modifier, on crée une nouvelle\ndonnée à partir de l'ancienne, sans toucher à\nl'originale. Exemple en Python : les chaînes, les\ntuples sont immuables.\n"
        },
        {
          "text": "Elle ne peut pas être lue",
          "correct": false,
          "feedback": "Erreur : une donnée immuable peut être lue\nlibrement, c'est seulement la modification qui est\ninterdite.\n"
        }
      ],
      "explanation": "L'immutabilité a plusieurs avantages : une donnée\npartagée entre plusieurs fonctions ne subit aucune\nmodification inattendue (pas d'effet de bord), et\non peut raisonner sur sa valeur sans suivre toute\nl'exécution du programme."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "fonction-premiere-classe"
      ],
      "title": "Fonctions de première classe",
      "partie": "ordre-superieur",
      "statement": "Que signifie « les fonctions sont des objets de première\nclasse » dans un langage ?",
      "options": [
        {
          "text": "Elles sont les plus rapides du programme",
          "correct": false,
          "feedback": "Erreur : pas de lien avec la performance.\n"
        },
        {
          "text": "On les passe, on les retourne et on les stocke comme des valeurs",
          "correct": true,
          "feedback": "Bonne réponse : c'est une caractéristique majeure\ndes langages fonctionnels. Python, JavaScript,\nOCaml, Haskell traitent les fonctions ainsi. C++,\nJava l'ont ajouté plus tardivement.\n"
        },
        {
          "text": "Elles sont écrites en majuscules",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec la casse.\n"
        },
        {
          "text": "Elles sont systématiquement les premières instructions du programme",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec l'ordre des instructions.\nLe terme est conceptuel.\n"
        }
      ],
      "explanation": "Cette propriété rend possibles les fonctions d'ordre\nsupérieur (`map`, `filter`, `sorted` avec `key`), qui\nprennent ou renvoient des fonctions, et qui sont au cœur\ndu paradigme fonctionnel."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "lambda"
      ],
      "title": "Expression lambda",
      "partie": "ordre-superieur",
      "statement": "En Python, qu'est-ce qu'une **expression lambda** ?",
      "options": [
        {
          "text": "Une variable globale",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec les variables globales.\n"
        },
        {
          "text": "Une fonction anonyme sur une ligne",
          "correct": true,
          "feedback": "Bonne réponse : `lambda x: x * x` est une fonction\nanonyme qui prend `x` et renvoie son carré.\nÉquivalent à `def f(x): return x * x` mais sans\nnom, utilisable directement comme expression.\n"
        },
        {
          "text": "Un type de boucle",
          "correct": false,
          "feedback": "Erreur : ce n'est pas une boucle. C'est une fonction\nanonyme.\n"
        },
        {
          "text": "Une instruction spéciale dédiée au débogage",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec le débogage.\n"
        }
      ],
      "explanation": "Le mot `lambda` vient du lambda-calcul de Church. En\nPython, les `lambda` sont limitées à une seule\nexpression. Pour une fonction plus complexe, on utilise\n`def`."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "map"
      ],
      "title": "Fonction map",
      "partie": "ordre-superieur",
      "statement": "Que fait la fonction `map(f, L)` en Python ?",
      "options": [
        {
          "text": "Elle filtre la liste selon un critère",
          "correct": false,
          "feedback": "Erreur : c'est le rôle de `filter`, pas `map`.\n"
        },
        {
          "text": "Elle réduit la liste à une seule valeur",
          "correct": false,
          "feedback": "Erreur : `map` produit une valeur pour chaque élément\nde `L`, elle ne les combine pas en une seule.\n"
        },
        {
          "text": "Elle applique `f` à chaque élément de `L`",
          "correct": true,
          "feedback": "Bonne réponse : pour obtenir une liste, on fait\n`list(map(f, L))`. Par exemple,\n`list(map(lambda x: x * 2, [1, 2, 3]))` renvoie\n`[2, 4, 6]`.\n"
        },
        {
          "text": "Elle modifie la liste `L` en y appliquant `f` directement",
          "correct": false,
          "feedback": "Erreur : `map` ne modifie pas la liste d'origine. Elle\nproduit de nouvelles valeurs et laisse `L` intacte.\n"
        }
      ],
      "explanation": "`map` et `filter` sont deux fonctions d'ordre supérieur\nclassiques : la première transforme chaque élément, la\nseconde ne garde que ceux qui vérifient un critère. Elles\ncorrespondent à des schémas d'usage très courants."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "filter"
      ],
      "title": "Fonction filter",
      "partie": "ordre-superieur",
      "statement": "Que fait `filter(p, L)` en Python ?",
      "options": [
        {
          "text": "Elle compte le nombre d'éléments dans `L`",
          "correct": false,
          "feedback": "Erreur : pour compter, on utilise `len` ou `sum`.\n"
        },
        {
          "text": "Elle garde les éléments de `L` où `p` est vrai",
          "correct": true,
          "feedback": "Bonne réponse : par exemple,\n`list(filter(lambda x: x > 0, [-1, 0, 1, 2]))`\nrenvoie `[1, 2]`. Les éléments pour lesquels `p`\nrenvoie `True` sont conservés.\n"
        },
        {
          "text": "Elle modifie directement chaque élément de `L`",
          "correct": false,
          "feedback": "Erreur : c'est plutôt le rôle de `map`.\n"
        },
        {
          "text": "Elle renvoie une liste vide",
          "correct": false,
          "feedback": "Erreur : `filter` renvoie un itérateur des éléments\nconservés, pas vide.\n"
        }
      ],
      "explanation": "Alternative idiomatique en Python avec\ncompréhension de liste :\n`[x for x in L if p(x)]`. Souvent plus lisible que\n`filter` en Python."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "map",
        "filter",
        "ordre-superieur"
      ],
      "title": "Map avec une fonction booléenne",
      "partie": "ordre-superieur",
      "statement": "On définit la fonction suivante :\n\n```python\ndef depasse_dix(x):\n    return x > 10\n```\n\nQue vaut `list(map(depasse_dix, [4, 15, 10, 23]))` ?",
      "options": [
        {
          "text": "`[15, 23]` : les éléments qui dépassent $10$",
          "correct": false,
          "feedback": "Erreur : c'est le résultat de `filter`, qui ne garde que les éléments\npour lesquels la fonction renvoie `True`. `map` renvoie, pour chaque\nélément, la valeur calculée par la fonction.\n"
        },
        {
          "text": "`[False, True, False, True]` : un booléen par élément",
          "correct": true,
          "feedback": "Bonne réponse : `map` applique `depasse_dix` à chaque élément et\nrassemble les résultats : `4 > 10` donne `False`, `15 > 10` donne\n`True`, `10 > 10` donne `False` et `23 > 10` donne `True`.\n"
        },
        {
          "text": "`[False, True, True, True]` : un booléen par élément",
          "correct": false,
          "feedback": "Erreur : `10 > 10` vaut `False`, car $10$ n'est pas strictement\nsupérieur à $10$. Seul `10 >= 10` vaudrait `True`.\n"
        },
        {
          "text": "`[4, 10]` : les éléments qui ne dépassent pas $10$",
          "correct": false,
          "feedback": "Erreur : ce sont les éléments écartés par le test. `map` ne retire\naucun élément : il renvoie autant de résultats que la liste contient\nd'éléments.\n"
        }
      ],
      "explanation": "`map(f, L)` renvoie un résultat `f(x)` pour chaque élément `x` de\n`L`, tandis que `filter(f, L)` ne garde que les éléments pour\nlesquels `f(x)` vaut `True`. Avec une fonction qui renvoie un\nbooléen, `map` donne donc des booléens."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "composition"
      ],
      "title": "Composition de fonctions",
      "partie": "ordre-superieur",
      "statement": "Que signifie « composer deux fonctions $f$ et $g$ » ?",
      "options": [
        {
          "text": "Les exécuter en parallèle",
          "correct": false,
          "feedback": "Erreur : la composition est séquentielle (l'une\naprès l'autre).\n"
        },
        {
          "text": "Créer une fonction qui applique $g$ puis $f$",
          "correct": true,
          "feedback": "Bonne réponse : $(f \\circ g)(x) = f(g(x))$. La\ncomposition de fonctions est la brique de base de la\nprogrammation fonctionnelle, qui voit les programmes\ncomme des chaînes de transformations.\n"
        },
        {
          "text": "Les écrire dans le même fichier",
          "correct": false,
          "feedback": "Erreur : aucun rapport. La composition est une\nopération mathématique.\n"
        },
        {
          "text": "Les fusionner définitivement en une seule grosse fonction",
          "correct": false,
          "feedback": "Erreur : la composition crée une nouvelle fonction\nmais ne modifie pas $f$ ni $g$ ; elles restent\nutilisables séparément.\n"
        }
      ],
      "explanation": "En programmation fonctionnelle, on construit des\nprogrammes complexes en composant des fonctions\nsimples. Ce style favorise la **réutilisation** et la\n**modularité**."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "recursion"
      ],
      "title": "Récursivité en programmation fonctionnelle",
      "partie": "principes",
      "statement": "Pourquoi la **récursivité** est-elle privilégiée à la\nboucle dans le paradigme fonctionnel ?",
      "options": [
        {
          "text": "Parce que les appels récursifs s'exécutent plus vite qu'une boucle",
          "correct": false,
          "feedback": "Erreur : la récursivité est généralement aussi\nrapide ou plus lente que les boucles, mais ce\nn'est pas la raison de son emploi.\n"
        },
        {
          "text": "Parce qu'une boucle repose sur un compteur que l'on modifie",
          "correct": true,
          "feedback": "Bonne réponse : `for i in range(n)` modifie\nimplicitement `i` à chaque tour, ce qui contredit\nl'immutabilité des données. Une approche purement\nfonctionnelle évite cela en utilisant des appels\nrécursifs avec de nouveaux paramètres.\n"
        },
        {
          "text": "Parce que les boucles n'existent tout simplement pas en Python",
          "correct": false,
          "feedback": "Erreur : Python a des boucles. C'est par\n**convention** ou par **style** qu'on les évite en\nprogrammation fonctionnelle.\n"
        },
        {
          "text": "Parce que c'est une habitude de style, sans justification de fond",
          "correct": false,
          "feedback": "Erreur : c'est une raison structurelle (éviter la\nmodification d'un état), pas un simple effet de\nmode.\n"
        }
      ],
      "explanation": "Les langages fonctionnels purs (Haskell) n'ont même pas\nde boucles : tout est exprimé par récursivité ou par\nfonctions d'ordre supérieur."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "pure-impure"
      ],
      "title": "Identifier une fonction pure",
      "partie": "principes",
      "statement": "Parmi les fonctions Python suivantes, laquelle est\n**pure** ?\n\n```python\ndef f1(x): return x + 1\ndef f2(x): print(x); return x\ndef f3(L, x): L.append(x); return L\ndef f4(): return random.random()\n```",
      "options": [
        {
          "text": "`f1` et `f4`",
          "correct": false,
          "feedback": "Erreur : `f4` n'est pas pure. Pour les mêmes\narguments (aucun ici), elle renvoie des résultats\ndifférents (génération aléatoire).\n"
        },
        {
          "text": "`f1` et `f2`",
          "correct": false,
          "feedback": "Erreur : `f2` appelle `print`, ce qui est un effet\nde bord (interaction avec le terminal).\n"
        },
        {
          "text": "`f1` seule",
          "correct": true,
          "feedback": "Bonne réponse : `f1` ne modifie rien et retourne\ntoujours la même valeur pour le même argument. `f2`\na un effet de bord (`print`), `f3` modifie son\nargument, `f4` retourne un résultat différent à\nchaque appel.\n"
        },
        {
          "text": "Les quatre fonctions",
          "correct": false,
          "feedback": "Erreur : seule `f1` respecte les deux conditions de\npureté.\n"
        }
      ],
      "explanation": "Les effets de bord typiques : modifier une variable\nglobale, écrire dans un fichier, appeler `print`,\nutiliser le hasard, lire l'horloge système, etc. Toutes\nces opérations rendent une fonction impure."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "map",
        "lambda"
      ],
      "title": "Lambda avec map",
      "partie": "ordre-superieur",
      "statement": "Que renvoie l'expression Python suivante ?\n```python\nlist(map(lambda x: x ** 2, [1, 2, 3, 4]))\n```",
      "options": [
        {
          "text": "`[1, 4, 9, 16]`",
          "correct": true,
          "feedback": "Bonne réponse : la lambda calcule le carré, et `map`\nl'applique à chaque élément. On obtient\n$[1^2, 2^2, 3^2, 4^2] = [1, 4, 9, 16]$.\n"
        },
        {
          "text": "`[1, 2, 3, 4]`",
          "correct": false,
          "feedback": "Erreur : `map` applique la fonction à chaque\nélément, donc transforme la liste.\n"
        },
        {
          "text": "`[1, 16]`",
          "correct": false,
          "feedback": "Erreur : `map` applique à **chaque** élément, on\nn'en perd pas en chemin.\n"
        },
        {
          "text": "`[2, 4, 6, 8]`",
          "correct": false,
          "feedback": "Erreur : ce serait `lambda x: x * 2`, pas `x ** 2`.\n`**` est l'opérateur **puissance** en Python.\n"
        }
      ],
      "explanation": "Cette construction est très idiomatique en\nprogrammation fonctionnelle. En Python, on peut aussi\nécrire `[x ** 2 for x in [1, 2, 3, 4]]`, qui est\nsouvent considéré plus lisible.",
      "variants": [
        {
          "statement": "Que renvoie l'expression Python suivante ?\n```python\nlist(map(lambda x: x ** 2, [1, 2, 3, 4]))\n```",
          "options": [
            {
              "text": "`[1, 4, 9, 16]`",
              "correct": true,
              "feedback": "Bonne réponse : la lambda calcule le carré, et `map`\nl'applique à chaque élément. On obtient\n$[1^2, 2^2, 3^2, 4^2] = [1, 4, 9, 16]$.\n"
            },
            {
              "text": "`[1, 2, 3, 4]`",
              "correct": false,
              "feedback": "Erreur : `map` applique la fonction à chaque\nélément, donc transforme la liste.\n"
            },
            {
              "text": "`[1, 16]`",
              "correct": false,
              "feedback": "Erreur : `map` applique à **chaque** élément, on\nn'en perd pas en chemin.\n"
            },
            {
              "text": "`[2, 4, 6, 8]`",
              "correct": false,
              "feedback": "Erreur : ce serait `lambda x: x * 2`, pas `x ** 2`.\n`**` est l'opérateur **puissance** en Python.\n"
            }
          ],
          "explanation": "Cette construction est très idiomatique en\nprogrammation fonctionnelle. En Python, on peut aussi\nécrire `[x ** 2 for x in [1, 2, 3, 4]]`, qui est\nsouvent considéré plus lisible."
        },
        {
          "statement": "Que renvoie l'expression Python suivante ?\n```python\nlist(map(lambda x: x ** 2, [2, 3, 5]))\n```",
          "options": [
            {
              "text": "`[4, 9, 25]`",
              "correct": true,
              "feedback": "Bonne réponse : la lambda calcule le carré, et `map`\nl'applique à chaque élément. On obtient\n$[2^2, 3^2, 5^2] = [4, 9, 25]$.\n"
            },
            {
              "text": "`[2, 3, 5]`",
              "correct": false,
              "feedback": "Erreur : `map` applique la fonction à chaque\nélément, donc transforme la liste.\n"
            },
            {
              "text": "`[4, 25]`",
              "correct": false,
              "feedback": "Erreur : `map` applique à **chaque** élément, on\nn'en perd pas en chemin.\n"
            },
            {
              "text": "`[4, 6, 10]`",
              "correct": false,
              "feedback": "Erreur : ce serait `lambda x: x * 2`, pas `x ** 2`.\n`**` est l'opérateur **puissance** en Python.\n"
            }
          ],
          "explanation": "Cette construction est très idiomatique en\nprogrammation fonctionnelle. En Python, on peut aussi\nécrire `[x ** 2 for x in [2, 3, 5]]`, qui est\nsouvent considéré plus lisible."
        },
        {
          "statement": "Que renvoie l'expression Python suivante ?\n```python\nlist(map(lambda x: x ** 2, [3, 6, 9, 10]))\n```",
          "options": [
            {
              "text": "`[9, 36, 81, 100]`",
              "correct": true,
              "feedback": "Bonne réponse : la lambda calcule le carré, et `map`\nl'applique à chaque élément. On obtient\n$[3^2, 6^2, 9^2, 10^2] = [9, 36, 81, 100]$.\n"
            },
            {
              "text": "`[3, 6, 9, 10]`",
              "correct": false,
              "feedback": "Erreur : `map` applique la fonction à chaque\nélément, donc transforme la liste.\n"
            },
            {
              "text": "`[9, 100]`",
              "correct": false,
              "feedback": "Erreur : `map` applique à **chaque** élément, on\nn'en perd pas en chemin.\n"
            },
            {
              "text": "`[6, 12, 18, 20]`",
              "correct": false,
              "feedback": "Erreur : ce serait `lambda x: x * 2`, pas `x ** 2`.\n`**` est l'opérateur **puissance** en Python.\n"
            }
          ],
          "explanation": "Cette construction est très idiomatique en\nprogrammation fonctionnelle. En Python, on peut aussi\nécrire `[x ** 2 for x in [3, 6, 9, 10]]`, qui est\nsouvent considéré plus lisible."
        }
      ]
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "filter",
        "lambda"
      ],
      "title": "Filter pour les nombres pairs",
      "partie": "ordre-superieur",
      "statement": "Que renvoie cette expression ?\n```python\nlist(filter(lambda x: x % 2 == 0, range(10)))\n```",
      "options": [
        {
          "text": "`[]`",
          "correct": false,
          "feedback": "Erreur : il y a bien des nombres pairs dans\n`range(10)`.\n"
        },
        {
          "text": "`[1, 3, 5, 7, 9]`",
          "correct": false,
          "feedback": "Erreur : `x % 2 == 0` teste si `x` est **pair**.\nDonc on garde les pairs, pas les impairs.\n"
        },
        {
          "text": "`[0, 1, 2, ..., 9]`",
          "correct": false,
          "feedback": "Erreur : ce serait `range(10)` complet, sans filtre.\n"
        },
        {
          "text": "`[0, 2, 4, 6, 8]`",
          "correct": true,
          "feedback": "Bonne réponse : `filter` conserve les éléments pour\nlesquels la condition est vraie. `range(10)` produit\n$0, 1, \\ldots, 9$ ; on garde les pairs $0, 2, 4, 6, 8$.\n"
        }
      ],
      "explanation": "Équivalent en compréhension de liste :\n`[x for x in range(10) if x % 2 == 0]`. Les deux\nformes sont fréquentes en Python.",
      "variants": [
        {
          "statement": "Que renvoie cette expression ?\n```python\nlist(filter(lambda x: x % 2 == 0, range(10)))\n```",
          "options": [
            {
              "text": "`[]`",
              "correct": false,
              "feedback": "Erreur : il y a bien des nombres pairs dans\n`range(10)`.\n"
            },
            {
              "text": "`[1, 3, 5, 7, 9]`",
              "correct": false,
              "feedback": "Erreur : `x % 2 == 0` teste si `x` est **pair**.\nDonc on garde les pairs, pas les impairs.\n"
            },
            {
              "text": "`[0, 1, 2, ..., 9]`",
              "correct": false,
              "feedback": "Erreur : ce serait `range(10)` complet, sans filtre.\n"
            },
            {
              "text": "`[0, 2, 4, 6, 8]`",
              "correct": true,
              "feedback": "Bonne réponse : `filter` conserve les éléments pour\nlesquels la condition est vraie. `range(10)` produit\n$0, 1, \\ldots, 9$ ; on garde les pairs $0, 2, 4, 6, 8$.\n"
            }
          ],
          "explanation": "Équivalent en compréhension de liste :\n`[x for x in range(10) if x % 2 == 0]`. Les deux\nformes sont fréquentes en Python."
        },
        {
          "statement": "Que renvoie cette expression ?\n```python\nlist(filter(lambda x: x % 2 == 0, range(13)))\n```",
          "options": [
            {
              "text": "`[]`",
              "correct": false,
              "feedback": "Erreur : il y a bien des nombres pairs dans\n`range(13)`.\n"
            },
            {
              "text": "`[1, 3, 5, 7, 9, 11]`",
              "correct": false,
              "feedback": "Erreur : `x % 2 == 0` teste si `x` est **pair**.\nDonc on garde les pairs, pas les impairs.\n"
            },
            {
              "text": "`[0, 1, 2, ..., 12]`",
              "correct": false,
              "feedback": "Erreur : ce serait `range(13)` complet, sans filtre.\n"
            },
            {
              "text": "`[0, 2, 4, 6, 8, 10, 12]`",
              "correct": true,
              "feedback": "Bonne réponse : `filter` conserve les éléments pour\nlesquels la condition est vraie. `range(13)` produit\n$0, 1, \\ldots, 12$ ; on garde les pairs $0, 2, 4, 6, 8, 10, 12$.\n"
            }
          ],
          "explanation": "Équivalent en compréhension de liste :\n`[x for x in range(13) if x % 2 == 0]`. Les deux\nformes sont fréquentes en Python."
        },
        {
          "statement": "Que renvoie cette expression ?\n```python\nlist(filter(lambda x: x % 2 == 0, range(7)))\n```",
          "options": [
            {
              "text": "`[]`",
              "correct": false,
              "feedback": "Erreur : il y a bien des nombres pairs dans\n`range(7)`.\n"
            },
            {
              "text": "`[1, 3, 5]`",
              "correct": false,
              "feedback": "Erreur : `x % 2 == 0` teste si `x` est **pair**.\nDonc on garde les pairs, pas les impairs.\n"
            },
            {
              "text": "`[0, 1, 2, ..., 6]`",
              "correct": false,
              "feedback": "Erreur : ce serait `range(7)` complet, sans filtre.\n"
            },
            {
              "text": "`[0, 2, 4, 6]`",
              "correct": true,
              "feedback": "Bonne réponse : `filter` conserve les éléments pour\nlesquels la condition est vraie. `range(7)` produit\n$0, 1, \\ldots, 6$ ; on garde les pairs $0, 2, 4, 6$.\n"
            }
          ],
          "explanation": "Équivalent en compréhension de liste :\n`[x for x in range(7) if x % 2 == 0]`. Les deux\nformes sont fréquentes en Python."
        }
      ]
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "comprehension"
      ],
      "title": "Compréhension de liste",
      "partie": "ordre-superieur",
      "statement": "Quelle est la **compréhension de liste** Python équivalente\nà `list(map(lambda x: x * 2, filter(lambda x: x > 0, L)))` ?",
      "options": [
        {
          "text": "`[x > 0 * 2 for x in L]`",
          "correct": false,
          "feedback": "Erreur : syntaxiquement valide mais sémantiquement\nfaux. La condition est ailleurs.\n"
        },
        {
          "text": "`[x for x in L * 2]`",
          "correct": false,
          "feedback": "Erreur : cela multiplie la liste par $2$ (deux\nrépétitions de la liste), pas chaque élément. De\nplus, le filtre est absent.\n"
        },
        {
          "text": "`[L if x > 0 for x in L]`",
          "correct": false,
          "feedback": "Erreur : syntaxe incorrecte de compréhension.\n"
        },
        {
          "text": "`[x * 2 for x in L if x > 0]`",
          "correct": true,
          "feedback": "Bonne réponse : on combine map et filter dans une\nseule compréhension. Cette forme est plus lisible\net idiomatique en Python.\n"
        }
      ],
      "explanation": "Les compréhensions de liste, inspirées des langages\nfonctionnels, s'intègrent naturellement à Python.\nElles combinent élégamment `map` et `filter` en\nune seule expression. Pour réduire une liste à une\nvaleur unique, on utilise `sum`, `max`, `min`, etc."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "haut-niveau"
      ],
      "title": "Fonction d'ordre supérieur",
      "partie": "ordre-superieur",
      "statement": "Quelle est la définition d'une **fonction d'ordre supérieur** ?",
      "options": [
        {
          "text": "Une fonction définie au niveau global du module",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec la portée.\n"
        },
        {
          "text": "Une fonction qui appelle d'autres fonctions",
          "correct": false,
          "feedback": "Erreur : la plupart des fonctions appellent d'autres\nfonctions. Le critère est plus précis.\n"
        },
        {
          "text": "Une fonction qui prend ou renvoie une fonction",
          "correct": true,
          "feedback": "Bonne réponse : `map`, `filter` et `sorted` (avec `key`)\nsont des fonctions d'ordre supérieur. Elles manipulent\nles fonctions comme n'importe quelle autre valeur.\n"
        },
        {
          "text": "Une fonction très complexe",
          "correct": false,
          "feedback": "Erreur : « ordre supérieur » est un terme technique,\npas un jugement de complexité.\n"
        }
      ],
      "explanation": "Les fonctions d'ordre supérieur sont la signature du\nparadigme fonctionnel. Elles permettent l'abstraction\ndu comportement : par exemple, `sorted(L, key=f)`\nlaisse l'utilisateur définir comment trier sans changer\nl'algorithme de tri."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "fonction-premiere-classe",
        "dictionnaire",
        "lecture-code"
      ],
      "title": "Des fonctions rangées dans un dictionnaire",
      "partie": "ordre-superieur",
      "statement": "Que vaut `resultat` après l'exécution de ce programme ?\n\n```python\ndef ajouter(a, b):\n    return a + b\n\ndef multiplier(a, b):\n    return a * b\n\noperations = {\"+\": ajouter, \"*\": multiplier}\nresultat = operations[\"*\"](4, 5)\n```",
      "options": [
        {
          "text": "`9`",
          "correct": false,
          "feedback": "Erreur : $9$ serait le résultat de `ajouter(4, 5)`, rangée sous la\nclé `\"+\"`. La clé `\"*\"` désigne `multiplier`.\n"
        },
        {
          "text": "`20`",
          "correct": true,
          "feedback": "Bonne réponse : `operations[\"*\"]` désigne la fonction `multiplier`,\net les parenthèses `(4, 5)` l'appellent aussitôt : `multiplier(4, 5)`\nrenvoie $20$.\n"
        },
        {
          "text": "La fonction `multiplier` elle-même, sans l'appeler",
          "correct": false,
          "feedback": "Erreur : `operations[\"*\"]` seul serait la fonction, mais il est\nsuivi de `(4, 5)`, qui provoque son appel. `resultat` reçoit donc la\nvaleur renvoyée, $20$.\n"
        },
        {
          "text": "Une erreur, car un dictionnaire ne peut pas contenir de fonctions",
          "correct": false,
          "feedback": "Erreur : en Python, une fonction est une valeur comme une autre. On\npeut la ranger dans un dictionnaire, la passer en argument ou la\nrenvoyer.\n"
        }
      ],
      "explanation": "Les fonctions sont des valeurs : on peut les stocker dans un\ndictionnaire et choisir laquelle appeler selon une clé.\n`operations[\"*\"](4, 5)` revient à `multiplier(4, 5)`, soit $20$."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "fonction-pure",
        "variable-globale"
      ],
      "title": "Une remise qui lit une variable globale",
      "partie": "principes",
      "statement": "On considère le programme suivant :\n```python\ntaux = 0.1\ndef remise(prix):\n    return prix - prix * taux\na = remise(200)\ntaux = 0.5\nb = remise(200)\n```\nQue valent `a` et `b`, et que peut-on en conclure ?",
      "options": [
        {
          "text": "`180.0` et `100.0` : `remise` n'est pas une fonction pure",
          "correct": true,
          "feedback": "Bonne réponse : la valeur de `taux` est lue à chaque appel, $0.1$\npuis $0.5$. Le même argument $200$ donne deux résultats différents :\nle résultat ne dépend pas que des arguments, la fonction n'est donc\npas pure.\n"
        },
        {
          "text": "`180.0` et `180.0` : `taux` est lu une fois, à la définition",
          "correct": false,
          "feedback": "Erreur : Python lit `taux` au moment de chaque appel, pas lors de la\ndéfinition de `remise`. Le second appel utilise donc $0.5$ et\nrenvoie `100.0`.\n"
        },
        {
          "text": "`180.0` et `100.0` : `remise` est pure, elle ne modifie rien",
          "correct": false,
          "feedback": "Erreur : ne rien modifier ne suffit pas. Une fonction pure doit\naussi renvoyer la même valeur pour les mêmes arguments, or\n`remise(200)` vaut `180.0` puis `100.0`.\n"
        },
        {
          "text": "Une erreur : `taux` n'est pas défini à l'intérieur de `remise`",
          "correct": false,
          "feedback": "Erreur : une fonction peut lire une variable définie en dehors d'elle\nsans `global`. Le mot-clé `global` n'est nécessaire que pour la\nmodifier.\n"
        }
      ],
      "explanation": "Une fonction pure ne dépend que de ses arguments. En lisant la\nvariable globale `taux`, `remise` renvoie des valeurs différentes\npour un même prix dès que `taux` change. Pour la rendre pure, on\npasse le taux en paramètre : `remise(prix, taux)`.",
      "variants": [
        {
          "statement": "On considère le programme suivant :\n```python\ntaux = 0.1\ndef remise(prix):\n    return prix - prix * taux\na = remise(200)\ntaux = 0.5\nb = remise(200)\n```\nQue valent `a` et `b`, et que peut-on en conclure ?",
          "options": [
            {
              "text": "`180.0` et `100.0` : `remise` n'est pas une fonction pure",
              "correct": true,
              "feedback": "Bonne réponse : la valeur de `taux` est lue à chaque appel, $0.1$\npuis $0.5$. Le même argument $200$ donne deux résultats différents :\nle résultat ne dépend pas que des arguments, la fonction n'est donc\npas pure.\n"
            },
            {
              "text": "`180.0` et `180.0` : `taux` est lu une fois, à la définition",
              "correct": false,
              "feedback": "Erreur : Python lit `taux` au moment de chaque appel, pas lors de la\ndéfinition de `remise`. Le second appel utilise donc $0.5$ et\nrenvoie `100.0`.\n"
            },
            {
              "text": "`180.0` et `100.0` : `remise` est pure, elle ne modifie rien",
              "correct": false,
              "feedback": "Erreur : ne rien modifier ne suffit pas. Une fonction pure doit\naussi renvoyer la même valeur pour les mêmes arguments, or\n`remise(200)` vaut `180.0` puis `100.0`.\n"
            },
            {
              "text": "Une erreur : `taux` n'est pas défini à l'intérieur de `remise`",
              "correct": false,
              "feedback": "Erreur : une fonction peut lire une variable définie en dehors d'elle\nsans `global`. Le mot-clé `global` n'est nécessaire que pour la\nmodifier.\n"
            }
          ],
          "explanation": "Une fonction pure ne dépend que de ses arguments. En lisant la\nvariable globale `taux`, `remise` renvoie des valeurs différentes\npour un même prix dès que `taux` change. Pour la rendre pure, on\npasse le taux en paramètre : `remise(prix, taux)`."
        },
        {
          "statement": "On considère le programme suivant :\n```python\ntaux = 0.25\ndef remise(prix):\n    return prix - prix * taux\na = remise(120)\ntaux = 0.5\nb = remise(120)\n```\nQue valent `a` et `b`, et que peut-on en conclure ?",
          "options": [
            {
              "text": "`90.0` et `60.0` : `remise` n'est pas une fonction pure",
              "correct": true,
              "feedback": "Bonne réponse : la valeur de `taux` est lue à chaque appel, $0.25$\npuis $0.5$. Le même argument $120$ donne deux résultats différents :\nle résultat ne dépend pas que des arguments, la fonction n'est donc\npas pure.\n"
            },
            {
              "text": "`90.0` et `90.0` : `taux` est lu une fois, à la définition",
              "correct": false,
              "feedback": "Erreur : Python lit `taux` au moment de chaque appel, pas lors de la\ndéfinition de `remise`. Le second appel utilise donc $0.5$ et\nrenvoie `60.0`.\n"
            },
            {
              "text": "`90.0` et `60.0` : `remise` est pure, elle ne modifie rien",
              "correct": false,
              "feedback": "Erreur : ne rien modifier ne suffit pas. Une fonction pure doit\naussi renvoyer la même valeur pour les mêmes arguments, or\n`remise(120)` vaut `90.0` puis `60.0`.\n"
            },
            {
              "text": "Une erreur : `taux` n'est pas défini à l'intérieur de `remise`",
              "correct": false,
              "feedback": "Erreur : une fonction peut lire une variable définie en dehors d'elle\nsans `global`. Le mot-clé `global` n'est nécessaire que pour la\nmodifier.\n"
            }
          ],
          "explanation": "Une fonction pure ne dépend que de ses arguments. En lisant la\nvariable globale `taux`, `remise` renvoie des valeurs différentes\npour un même prix dès que `taux` change. Pour la rendre pure, on\npasse le taux en paramètre : `remise(prix, taux)`."
        },
        {
          "statement": "On considère le programme suivant :\n```python\ntaux = 0.5\ndef remise(prix):\n    return prix - prix * taux\na = remise(80)\ntaux = 0.75\nb = remise(80)\n```\nQue valent `a` et `b`, et que peut-on en conclure ?",
          "options": [
            {
              "text": "`40.0` et `20.0` : `remise` n'est pas une fonction pure",
              "correct": true,
              "feedback": "Bonne réponse : la valeur de `taux` est lue à chaque appel, $0.5$\npuis $0.75$. Le même argument $80$ donne deux résultats différents :\nle résultat ne dépend pas que des arguments, la fonction n'est donc\npas pure.\n"
            },
            {
              "text": "`40.0` et `40.0` : `taux` est lu une fois, à la définition",
              "correct": false,
              "feedback": "Erreur : Python lit `taux` au moment de chaque appel, pas lors de la\ndéfinition de `remise`. Le second appel utilise donc $0.75$ et\nrenvoie `20.0`.\n"
            },
            {
              "text": "`40.0` et `20.0` : `remise` est pure, elle ne modifie rien",
              "correct": false,
              "feedback": "Erreur : ne rien modifier ne suffit pas. Une fonction pure doit\naussi renvoyer la même valeur pour les mêmes arguments, or\n`remise(80)` vaut `40.0` puis `20.0`.\n"
            },
            {
              "text": "Une erreur : `taux` n'est pas défini à l'intérieur de `remise`",
              "correct": false,
              "feedback": "Erreur : une fonction peut lire une variable définie en dehors d'elle\nsans `global`. Le mot-clé `global` n'est nécessaire que pour la\nmodifier.\n"
            }
          ],
          "explanation": "Une fonction pure ne dépend que de ses arguments. En lisant la\nvariable globale `taux`, `remise` renvoie des valeurs différentes\npour un même prix dès que `taux` change. Pour la rendre pure, on\npasse le taux en paramètre : `remise(prix, taux)`."
        }
      ]
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "imperatif-vs-fonctionnel"
      ],
      "title": "Impératif vs fonctionnel",
      "partie": "paradigmes",
      "statement": "Quel est l'équivalent **fonctionnel** du code impératif\nsuivant ?\n\n```python\ntotal = 0\nfor x in L:\n    if x > 0:\n        total += x ** 2\n```",
      "options": [
        {
          "text": "`sum(x ** 2 if x > 0 for x in L)`",
          "correct": false,
          "feedback": "Erreur : syntaxe invalide. `x ** 2 if x > 0` sans `else`\nn'est pas une expression correcte : le filtre se place\naprès le `for`, sous la forme `for x in L if x > 0`.\n"
        },
        {
          "text": "`sum(x ** 2 for x in L if x > 0)`",
          "correct": true,
          "feedback": "Bonne réponse : le filtre `if x > 0` écarte les valeurs\nnégatives ou nulles, `x ** 2` transforme chaque valeur\nretenue et `sum` additionne le tout. Pas de variable\nmodifiée, pas de boucle explicite.\n"
        },
        {
          "text": "`list(map(lambda x: x ** 2, filter(lambda x: x > 0, L)))`",
          "correct": false,
          "feedback": "Erreur : le filtrage et la mise au carré sont corrects,\nmais cette expression renvoie la liste des carrés et non\nleur somme. Pour `L = [2, -1, 3]`, elle donne `[4, 9]`\nau lieu de $13$.\n"
        },
        {
          "text": "`for x in L: total += x ** 2`",
          "correct": false,
          "feedback": "Erreur : c'est une boucle impérative qui modifie\n`total`. On veut s'en abstraire.\n"
        }
      ],
      "explanation": "Le code fonctionnel est plus court, sans variable\nmodifiable, et exprime directement l'**intention**\n(« la somme des carrés des positifs ») plutôt que la\n**mécanique** d'accumulation."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "recursion-fonctionnelle",
        "cas-de-base"
      ],
      "title": "Récursivité sans boucle",
      "partie": "principes",
      "statement": "Quelle fonction calcule **récursivement**, sans boucle, la somme des\néléments d'une liste `L`, l'appel `somme(L, 0)` devant renvoyer la\nsomme de toute la liste ?",
      "options": [
        {
          "text": "```python\ndef somme(L, i):\n    return L[i] + somme(L, i + 1)\n```",
          "correct": false,
          "feedback": "Erreur : il manque le cas de base. Quand `i` atteint `len(L)`,\n`L[i]` n'existe pas : `somme([1, 2, 3], 0)` lève `IndexError`.\n"
        },
        {
          "text": "```python\ndef somme(L, i):\n    total = 0\n    for j in range(i, len(L)):\n        total = total + L[j]\n    return total\n```",
          "correct": false,
          "feedback": "Erreur : le résultat est juste, mais la fonction utilise une boucle\net une variable `total` modifiée à chaque tour. C'est l'écriture\nimpérative, pas récursive.\n"
        },
        {
          "text": "```python\ndef somme(L, i):\n    if i == len(L):\n        return 0\n    return L[i] + somme(L, i + 1)\n```",
          "correct": true,
          "feedback": "Bonne réponse : le cas de base traite l'indice qui dépasse la liste\nen renvoyant $0$, et chaque appel ajoute `L[i]` à la somme des\néléments suivants. Aucune boucle, aucune variable modifiée.\n"
        },
        {
          "text": "```python\ndef somme(L, i):\n    if i == len(L) - 1:\n        return 0\n    return L[i] + somme(L, i + 1)\n```",
          "correct": false,
          "feedback": "Erreur : le cas de base renvoie $0$ sur le dernier indice au lieu\nd'ajouter `L[i]`, si bien que le dernier élément est oublié :\n`somme([1, 2, 3], 0)` renvoie $3$ au lieu de $6$.\n"
        }
      ],
      "explanation": "Une fonction récursive a besoin d'un cas de base, qui arrête les\nappels, et d'un appel sur un problème plus petit, ici la fin de la\nliste à partir de `i + 1`. En Python, le nombre d'appels imbriqués\nest limité : pour de très longues listes, on préfère `sum`."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "paradigme-mixte"
      ],
      "title": "Mélange de paradigmes",
      "partie": "paradigmes",
      "statement": "Un programme Python utilise une boucle `for` pour\nparcourir une liste, puis appelle `sorted` avec une\nfonction lambda comme critère. Combien de paradigmes\nde programmation différents ce code combine-t-il ?",
      "options": [
        {
          "text": "Aucun, ce n'est pas vraiment un programme correct et valide",
          "correct": false,
          "feedback": "Erreur : c'est bien un programme valide, et il\nutilise plusieurs paradigmes.\n"
        },
        {
          "text": "Deux : impératif (la boucle) et fonctionnel (la lambda)",
          "correct": true,
          "feedback": "Bonne réponse : c'est exactement ce que dit le\nprogramme officiel : « Dans un même programme,\non peut utiliser des paradigmes différents. »\n"
        },
        {
          "text": "Trois : impératif, fonctionnel et objet",
          "correct": false,
          "feedback": "Erreur : aucune classe ni objet n'est défini\ndans cet exemple, donc le paradigme objet n'est\npas mobilisé.\n"
        },
        {
          "text": "Un seul, l'impératif",
          "correct": false,
          "feedback": "Erreur : la boucle `for` est bien impérative,\nmais l'usage de `sorted` avec une lambda relève\ndu style fonctionnel.\n"
        }
      ],
      "explanation": "Python est un langage **multi-paradigme** : il\npermet d'écrire dans le style impératif (boucles,\naffectations), fonctionnel (lambda, `map`,\ncompréhensions) ou objet (classes), et de\ncombiner librement les trois selon les besoins."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "comparaison-imperatif-fonctionnel"
      ],
      "title": "Calculer la somme des carrés",
      "partie": "paradigmes",
      "statement": "On veut calculer la somme des carrés des entiers\nd'une liste `nombres`. Quel code suit le **style fonctionnel** ?",
      "options": [
        {
          "text": "```python\ntotal = 0\nfor n in nombres:\n    total = total + n * n\n```\n",
          "correct": false,
          "feedback": "Erreur : ce code est **impératif** : il modifie\nune variable `total` à chaque itération.\n"
        },
        {
          "text": "```python\nclass Sommateur:\n    def __init__(self):\n        self.total = 0\n    def ajouter(self, n):\n        self.total += n * n\n```\n",
          "correct": false,
          "feedback": "Erreur : ce code définit une classe ; c'est le\nstyle **objet**, pas fonctionnel.\n"
        },
        {
          "text": "```python\ntotal = nombres\n```\n",
          "correct": false,
          "feedback": "Erreur : ce code ne calcule pas du tout la\nsomme des carrés.\n"
        },
        {
          "text": "```python\ntotal = sum(n * n for n in nombres)\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : ce code est **fonctionnel** : il\nconstruit une expression sans variable d'état\nintermédiaire et passe le résultat à la fonction\n`sum`. Pas d'effet de bord.\n"
        }
      ],
      "explanation": "Le style fonctionnel privilégie les expressions\nsans état modifiable. La compréhension de liste et\nles fonctions comme `sum`, `map`, `filter`\npermettent souvent d'écrire des traitements de\ndonnées très concis dans ce style."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "choix-paradigme"
      ],
      "title": "Choisir un paradigme",
      "partie": "paradigmes",
      "statement": "Pour écrire un script court qui transforme une\nliste de prix en y appliquant une remise de $10\\%$,\nquel **paradigme** est le plus naturel en Python ?",
      "options": [
        {
          "text": "Une approche fonctionnelle avec une compréhension de liste ou un `map` sur une lambda",
          "correct": true,
          "feedback": "Bonne réponse : la transformation d'une liste en\nune autre par une fonction simple est typique du\nstyle fonctionnel. Une compréhension de liste\ncomme `[p * 0.9 for p in prix]` est concise et\nclaire.\n"
        },
        {
          "text": "Un paradigme logique type Prolog",
          "correct": false,
          "feedback": "Possible théoriquement mais inadapté ici, et\nProlog n'est pas Python. Le paradigme logique\nn'est pas étudié au lycée.\n"
        },
        {
          "text": "Une approche objet avec une classe `Remiseur` et une méthode `appliquer`",
          "correct": false,
          "feedback": "Possible mais nettement excessif pour une\ntransformation de liste de quelques lignes. La\nPOO se justifie quand on modélise des entités\navec un état et plusieurs comportements.\n"
        },
        {
          "text": "Aucun paradigme : il faut tout écrire à la main, avec des `goto`",
          "correct": false,
          "feedback": "Erreur : il n'existe pas de `goto` en Python, et\naucun paradigme moderne ne le promeut.\n"
        }
      ],
      "explanation": "Une même tâche peut souvent s'écrire dans plusieurs\nparadigmes. Le bon choix dépend de la nature du\nproblème : transformations de données pour le\nfonctionnel, modélisation d'entités pour l'objet,\nséquences d'instructions pour l'impératif. Python\npermet de mélanger les trois."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "parallelisme"
      ],
      "title": "Programmation fonctionnelle et parallélisme",
      "partie": "paradigmes",
      "statement": "Pourquoi la programmation fonctionnelle est-elle\nparticulièrement adaptée au calcul **parallèle** ?",
      "options": [
        {
          "text": "Elle utilise automatiquement davantage de cœurs du processeur",
          "correct": false,
          "feedback": "Erreur : ce n'est pas la cause, c'est l'effet\nrecherché. La cause est plus profonde.\n"
        },
        {
          "text": "Elle utilise moins de mémoire",
          "correct": false,
          "feedback": "Erreur : au contraire, l'immutabilité peut consommer\nplus de mémoire (création de nouveaux objets plutôt\nque modification en place).\n"
        },
        {
          "text": "Elle est plus simple à apprendre",
          "correct": false,
          "feedback": "Erreur : c'est subjectif, et de toute façon pas la\nraison de l'aptitude au parallélisme.\n"
        },
        {
          "text": "Sans effets de bord ni mutation, pas de conflit d'accès",
          "correct": true,
          "feedback": "Bonne réponse : en programmation impérative, deux calculs\nmenés en parallèle qui modifient la même variable doivent\nse coordonner, ce qui est complexe et lent. Avec des\ndonnées qui ne changent pas, chaque calcul avance seul,\nsans risque de conflit.\n"
        }
      ],
      "explanation": "C'est un argument fort à l'heure des processeurs à\nplusieurs cœurs : des calculs sans effet de bord peuvent\nêtre répartis entre plusieurs processus sans qu'ils se\ngênent."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese",
        "paradigmes"
      ],
      "title": "Comparaison des paradigmes",
      "partie": "paradigmes",
      "statement": "Parmi les affirmations suivantes, laquelle est **vraie** ?",
      "options": [
        {
          "text": "La programmation fonctionnelle remplace définitivement l'orientée objet",
          "correct": false,
          "feedback": "Erreur : les deux paradigmes coexistent et se\ncomplètent. La plupart des langages modernes sont\nmulti-paradigmes (Python, JavaScript, Scala).\n"
        },
        {
          "text": "La programmation fonctionnelle ne fonctionne que sur les ordinateurs récents",
          "correct": false,
          "feedback": "Erreur : Lisp date de $1958$ et tournait sur les\npremiers ordinateurs. La programmation fonctionnelle\nest antérieure à l'orientée objet.\n"
        },
        {
          "text": "La programmation fonctionnelle interdit l'utilisation des classes",
          "correct": false,
          "feedback": "Erreur : on peut utiliser des classes en\nprogrammation fonctionnelle, surtout dans des\nlangages multi-paradigmes. Les puristes les évitent,\nmais ce n'est pas une interdiction du paradigme\nlui-même.\n"
        },
        {
          "text": "Fonctionnelle et objet sont complémentaires, selon les situations",
          "correct": true,
          "feedback": "Bonne réponse : OO modélise des entités avec état\net comportement (interfaces graphiques, simulation,\njeux). Fonctionnelle modélise des transformations\nde données (calcul, science des données). Beaucoup\nde programmes mélangent les deux.\n"
        }
      ],
      "explanation": "L'idéal pragmatique : utiliser le bon paradigme pour\nle bon problème. Une bonne formation NSI inclut donc\nles deux pour développer la flexibilité de pensée\nalgorithmique."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "principes",
        "purete",
        "effet-de-bord",
        "affichage"
      ],
      "title": "Une fonction qui affiche",
      "partie": "principes",
      "statement": "La fonction `double` est-elle pure ?\n```python\ndef double(x):\n    print(x)\n    return 2 * x\n```",
      "options": [
        {
          "text": "Non, l'affichage est un effet de bord",
          "correct": true,
          "feedback": "Bonne réponse : le résultat ne dépend que de `x`, mais la\nfonction agit sur l'extérieur en écrivant à l'écran. Deux appels\nidentiques n'ont pas le même effet sur le monde.\n"
        },
        {
          "text": "Oui, le résultat ne dépendant que de `x`",
          "correct": false,
          "feedback": "Erreur : la pureté exige aussi l'absence de tout effet\nextérieur, et `print` en est un.\n"
        },
        {
          "text": "Oui, car `print` ne modifie aucune variable",
          "correct": false,
          "feedback": "Erreur : écrire à l'écran est un effet de bord même sans\nvariable modifiée.\n"
        },
        {
          "text": "Non, car elle ne contient pas de boucle",
          "correct": false,
          "feedback": "Erreur : la présence ou l'absence de boucle ne définit pas la\npureté.\n"
        }
      ],
      "explanation": "Une fonction pure ne fait que calculer sa valeur de retour :\nafficher, écrire dans un fichier ou modifier une variable globale\nsont des effets de bord."
    },
    {
      "id": "q27",
      "difficulty": 2,
      "skills": [
        "principes",
        "immuabilite",
        "tuple"
      ],
      "title": "Ajouter un élément sans rien modifier",
      "partie": "principes",
      "statement": "Quelle version respecte l'immuabilité, en laissant intacte la\nstructure reçue ?",
      "options": [
        {
          "text": "`def ajoute(t, x): return t + (x,)`, avec `t` un tuple",
          "correct": true,
          "feedback": "Bonne réponse : `t + (x,)` construit un nouveau tuple ; `t`\nreste inchangé pour tous ceux qui le partagent.\n"
        },
        {
          "text": "`def ajoute(l, x): l.append(x)`, avec `l` une liste",
          "correct": false,
          "feedback": "Erreur : `append` modifie la liste reçue, visible depuis\nl'appelant.\n"
        },
        {
          "text": "`def ajoute(l, x): l += [x]`, avec `l` une liste",
          "correct": false,
          "feedback": "Erreur : sur une liste, `+=` modifie l'objet existant, comme\n`append`.\n"
        },
        {
          "text": "`def ajoute(l, x): l.insert(len(l), x)`, avec `l` une liste",
          "correct": false,
          "feedback": "Erreur : `insert` modifie la liste en place.\n"
        }
      ],
      "explanation": "L'immuabilité se pratique en renvoyant de nouvelles valeurs : les\ntuples s'y prêtent, les listes exigent une copie."
    },
    {
      "id": "q28",
      "difficulty": 3,
      "skills": [
        "principes",
        "purete",
        "memoisation"
      ],
      "title": "Pureté et mémorisation des résultats",
      "partie": "principes",
      "statement": "Pourquoi peut-on mémoriser les résultats d'une fonction pure dans\nun dictionnaire pour éviter de les recalculer, alors que c'est\nrisqué pour une fonction quelconque ?",
      "options": [
        {
          "text": "Une fonction pure renvoie toujours la même valeur pour les mêmes arguments",
          "correct": true,
          "feedback": "Bonne réponse : le résultat mémorisé reste valable à tout\nmoment. Une fonction qui lit une variable globale ou l'heure\npourrait rendre une valeur différente au prochain appel.\n"
        },
        {
          "text": "Une fonction pure s'exécute plus vite, le dictionnaire restant petit",
          "correct": false,
          "feedback": "Erreur : la vitesse n'a rien à voir ; c'est la stabilité du\nrésultat qui compte.\n"
        },
        {
          "text": "Une fonction pure n'accepte qu'un seul argument, facile à utiliser comme clé",
          "correct": false,
          "feedback": "Erreur : une fonction pure peut avoir plusieurs arguments, un\ntuple sert alors de clé.\n"
        },
        {
          "text": "Une fonction pure ne peut pas lever d'exception, le dictionnaire reste cohérent",
          "correct": false,
          "feedback": "Erreur : une fonction pure peut lever une exception, par\nexemple une division par zéro.\n"
        }
      ],
      "explanation": "La mémoïsation suppose qu'un résultat ne change pas d'un appel à\nl'autre : c'est exactement ce que garantit la pureté."
    },
    {
      "id": "q29",
      "difficulty": 1,
      "skills": [
        "principes",
        "immuabilite",
        "somme"
      ],
      "title": "Calculer une somme sans réaffecter",
      "partie": "principes",
      "statement": "Parmi ces façons de calculer la somme d'une liste `l`, laquelle ne\nmodifie aucune variable ni aucune structure ?",
      "options": [
        {
          "text": "Un appel `sum(l)`, qui renvoie la somme sans rien changer",
          "correct": true,
          "feedback": "Bonne réponse : `sum` reçoit la liste et renvoie une valeur, sans\nrien changer ni réaffecter.\n"
        },
        {
          "text": "Une boucle `for` accumulant dans `total`, réaffecté à chaque tour",
          "correct": false,
          "feedback": "Erreur : `total` est réaffecté à chaque tour, c'est le style\nimpératif.\n"
        },
        {
          "text": "Une boucle remplaçant `l[0]` par `l[0] + l[1]` et retirant `l[1]`",
          "correct": false,
          "feedback": "Erreur : la liste est détruite au passage.\n"
        },
        {
          "text": "Un `l.append(sum(l))` suivi de la lecture de `l[-1]`",
          "correct": false,
          "feedback": "Erreur : la liste reçoit un élément de plus.\n"
        }
      ],
      "explanation": "Le style fonctionnel compose des fonctions qui renvoient des\nvaleurs au lieu de mettre à jour des variables."
    },
    {
      "id": "q30",
      "difficulty": 2,
      "skills": [
        "principes",
        "effet-de-bord",
        "liste",
        "trace"
      ],
      "title": "Un argument modifié par la fonction",
      "partie": "principes",
      "statement": "Qu'affiche ce programme, et la fonction `sans_tete` est-elle pure ?\n```python\ndef sans_tete(l):\n    l.pop(0)\n    return l\n\na = [1, 2, 3]\nb = sans_tete(a)\nprint(a)\n```",
      "options": [
        {
          "text": "`[2, 3]`, et la fonction n'est pas pure, elle modifie `a`",
          "correct": true,
          "feedback": "Bonne réponse : `pop(0)` retire le premier élément de la liste\nreçue, qui est `a` elle-même. Un appel a changé une valeur\nextérieure, c'est un effet de bord.\n"
        },
        {
          "text": "`[1, 2, 3]`, et la fonction est pure, elle renvoie une liste",
          "correct": false,
          "feedback": "Erreur : `a` et `l` désignent la même liste, modifiée par\n`pop`.\n"
        },
        {
          "text": "`[2, 3]`, et la fonction est pure, le résultat étant renvoyé",
          "correct": false,
          "feedback": "Erreur : renvoyer un résultat n'efface pas l'effet de bord sur\n`a`.\n"
        },
        {
          "text": "`[1, 2, 3]`, et la fonction n'est pas pure, elle utilise `pop`",
          "correct": false,
          "feedback": "Erreur : la fonction n'est effectivement pas pure, mais `a` a\nbien été modifiée.\n"
        }
      ],
      "explanation": "Une version pure construirait une nouvelle liste, `l[1:]`, sans\ntoucher à celle reçue.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme, et la fonction `sans_tete` est-elle pure ?\n```python\ndef sans_tete(l):\n    l.pop(0)\n    return l\n\na = [1, 2, 3]\nb = sans_tete(a)\nprint(a)\n```",
          "options": [
            {
              "text": "`[2, 3]`, et la fonction n'est pas pure, elle modifie `a`",
              "correct": true,
              "feedback": "Bonne réponse : `pop(0)` retire le premier élément de la liste\nreçue, qui est `a` elle-même. Un appel a changé une valeur\nextérieure, c'est un effet de bord.\n"
            },
            {
              "text": "`[1, 2, 3]`, et la fonction est pure, elle renvoie une liste",
              "correct": false,
              "feedback": "Erreur : `a` et `l` désignent la même liste, modifiée par\n`pop`.\n"
            },
            {
              "text": "`[2, 3]`, et la fonction est pure, le résultat étant renvoyé",
              "correct": false,
              "feedback": "Erreur : renvoyer un résultat n'efface pas l'effet de bord sur\n`a`.\n"
            },
            {
              "text": "`[1, 2, 3]`, et la fonction n'est pas pure, elle utilise `pop`",
              "correct": false,
              "feedback": "Erreur : la fonction n'est effectivement pas pure, mais `a` a\nbien été modifiée.\n"
            }
          ],
          "explanation": "Une version pure construirait une nouvelle liste, `l[1:]`, sans\ntoucher à celle reçue."
        },
        {
          "statement": "Qu'affiche ce programme, et la fonction `sans_tete` est-elle pure ?\n```python\ndef sans_tete(l):\n    l.pop(0)\n    return l\n\na = [5, 6]\nb = sans_tete(a)\nprint(a)\n```",
          "options": [
            {
              "text": "`[6]`, et la fonction n'est pas pure, elle modifie `a`",
              "correct": true,
              "feedback": "Bonne réponse : `pop(0)` retire le premier élément de la liste\nreçue, qui est `a` elle-même. Un appel a changé une valeur\nextérieure, c'est un effet de bord.\n"
            },
            {
              "text": "`[5, 6]`, et la fonction est pure, elle renvoie une liste",
              "correct": false,
              "feedback": "Erreur : `a` et `l` désignent la même liste, modifiée par\n`pop`.\n"
            },
            {
              "text": "`[6]`, et la fonction est pure, le résultat étant renvoyé",
              "correct": false,
              "feedback": "Erreur : renvoyer un résultat n'efface pas l'effet de bord sur\n`a`.\n"
            },
            {
              "text": "`[5, 6]`, et la fonction n'est pas pure, elle utilise `pop`",
              "correct": false,
              "feedback": "Erreur : la fonction n'est effectivement pas pure, mais `a` a\nbien été modifiée.\n"
            }
          ],
          "explanation": "Une version pure construirait une nouvelle liste, `l[1:]`, sans\ntoucher à celle reçue."
        },
        {
          "statement": "Qu'affiche ce programme, et la fonction `sans_tete` est-elle pure ?\n```python\ndef sans_tete(l):\n    l.pop(0)\n    return l\n\na = [7, 8, 9, 10]\nb = sans_tete(a)\nprint(a)\n```",
          "options": [
            {
              "text": "`[8, 9, 10]`, et la fonction n'est pas pure, elle modifie `a`",
              "correct": true,
              "feedback": "Bonne réponse : `pop(0)` retire le premier élément de la liste\nreçue, qui est `a` elle-même. Un appel a changé une valeur\nextérieure, c'est un effet de bord.\n"
            },
            {
              "text": "`[7, 8, 9, 10]`, et la fonction est pure, elle renvoie une liste",
              "correct": false,
              "feedback": "Erreur : `a` et `l` désignent la même liste, modifiée par\n`pop`.\n"
            },
            {
              "text": "`[8, 9, 10]`, et la fonction est pure, le résultat étant renvoyé",
              "correct": false,
              "feedback": "Erreur : renvoyer un résultat n'efface pas l'effet de bord sur\n`a`.\n"
            },
            {
              "text": "`[7, 8, 9, 10]`, et la fonction n'est pas pure, elle utilise `pop`",
              "correct": false,
              "feedback": "Erreur : la fonction n'est effectivement pas pure, mais `a` a\nbien été modifiée.\n"
            }
          ],
          "explanation": "Une version pure construirait une nouvelle liste, `l[1:]`, sans\ntoucher à celle reçue."
        }
      ]
    },
    {
      "id": "q31",
      "difficulty": 2,
      "skills": [
        "paradigmes",
        "tests",
        "purete"
      ],
      "title": "Tester une fonction pure",
      "partie": "paradigmes",
      "statement": "Pourquoi une fonction pure est-elle plus simple à tester qu'une\nfonction qui lit et modifie des variables globales ?",
      "options": [
        {
          "text": "Il suffit de choisir des arguments et de vérifier la valeur renvoyée",
          "correct": true,
          "feedback": "Bonne réponse : rien à préparer avant l'appel, rien à inspecter\naprès, sinon le résultat. Chaque test est indépendant des\nautres.\n"
        },
        {
          "text": "Une fonction pure n'a jamais besoin d'être testée, étant correcte par nature",
          "correct": false,
          "feedback": "Erreur : la pureté ne garantit pas que le calcul est juste.\n"
        },
        {
          "text": "Une fonction pure s'exécute plus vite, les tests durent donc moins",
          "correct": false,
          "feedback": "Erreur : la vitesse n'est pas en cause, c'est l'absence d'état à\npréparer.\n"
        },
        {
          "text": "Les tests d'une fonction pure peuvent s'écrire sans connaître ses arguments",
          "correct": false,
          "feedback": "Erreur : un test appelle la fonction avec des arguments précis.\n"
        }
      ],
      "explanation": "Une fonction qui dépend d'un état global oblige à reconstruire cet\nétat avant chaque test et à le vérifier après."
    },
    {
      "id": "q32",
      "difficulty": 2,
      "skills": [
        "paradigmes",
        "etat",
        "parametres"
      ],
      "title": "Où se trouve l'état d'un programme fonctionnel",
      "partie": "paradigmes",
      "statement": "Un programme impératif garde l'état du jeu dans des variables qu'il\nmet à jour. Comment un programme fonctionnel représente-t-il\nl'évolution de cet état ?",
      "options": [
        {
          "text": "Chaque fonction reçoit l'état et renvoie un nouvel état",
          "correct": true,
          "feedback": "Bonne réponse : l'état circule dans les paramètres et les\nvaleurs de retour ; l'ancien état n'est jamais modifié, un\nnouveau est construit à chaque étape.\n"
        },
        {
          "text": "Il stocke l'état dans une variable globale lue par toutes les fonctions",
          "correct": false,
          "feedback": "Erreur : c'est précisément ce que le style fonctionnel évite.\n"
        },
        {
          "text": "Il n'a pas d'état, un programme fonctionnel ne pouvant rien faire évoluer",
          "correct": false,
          "feedback": "Erreur : l'état évolue, mais par création de nouvelles valeurs.\n"
        },
        {
          "text": "Chaque fonction modifie l'état reçu avant de le renvoyer",
          "correct": false,
          "feedback": "Erreur : modifier l'état reçu serait un effet de bord.\n"
        }
      ],
      "explanation": "Sans variable mutable, le temps qui passe se traduit par une suite\nde valeurs, chacune calculée à partir de la précédente."
    },
    {
      "id": "q33",
      "difficulty": 3,
      "skills": [
        "paradigmes",
        "objet",
        "fonctionnel",
        "comparaison"
      ],
      "title": "Déplacer un point en objet et en fonctionnel",
      "partie": "paradigmes",
      "statement": "Deux programmes déplacent un point. Lequel relève du style\nfonctionnel ?\n```python\n# Version A\np.deplace(2, 3)\n\n# Version B\nq = deplace(p, 2, 3)\n```",
      "options": [
        {
          "text": "La version B, qui renvoie un nouveau point sans toucher à `p`",
          "correct": true,
          "feedback": "Bonne réponse : `deplace` reçoit `p` et renvoie un point\ndistinct ; `p` reste disponible tel quel. La version A modifie\nl'état interne de l'objet `p`.\n"
        },
        {
          "text": "La version A, qui regroupe la donnée et son comportement",
          "correct": false,
          "feedback": "Erreur : regrouper données et méthodes et modifier l'état est le\npropre du style objet.\n"
        },
        {
          "text": "Les deux, puisque toutes deux appellent une fonction `deplace`",
          "correct": false,
          "feedback": "Erreur : appeler une fonction ne suffit pas ; c'est l'absence de\nmodification qui compte.\n"
        },
        {
          "text": "Aucune, le style fonctionnel interdisant les objets comme `p`",
          "correct": false,
          "feedback": "Erreur : toute donnée peut être manipulée en style fonctionnel,\npourvu qu'on ne la modifie pas.\n"
        }
      ],
      "explanation": "En objet, l'opération change l'objet ; en fonctionnel, elle produit\nune nouvelle valeur et laisse l'ancienne intacte."
    },
    {
      "id": "q34",
      "difficulty": 2,
      "skills": [
        "paradigmes",
        "effets-de-bord",
        "organisation"
      ],
      "title": "Les effets de bord inévitables",
      "partie": "paradigmes",
      "statement": "Un programme doit bien, à un moment, afficher un résultat ou écrire\nun fichier. Comment le style fonctionnel compose-t-il avec ces\neffets de bord nécessaires ?",
      "options": [
        {
          "text": "Il les isole dans quelques fonctions, le cœur du calcul restant pur",
          "correct": true,
          "feedback": "Bonne réponse : les fonctions pures calculent, et une mince\ncouche de fonctions impures affiche ou enregistre leurs\nrésultats. Le programme est pur presque partout.\n"
        },
        {
          "text": "Il les interdit totalement, un programme fonctionnel n'affichant rien",
          "correct": false,
          "feedback": "Erreur : un programme qui ne produit aucun effet ne sert à\nrien.\n"
        },
        {
          "text": "Il les répartit dans toutes les fonctions, pour qu'aucune ne domine",
          "correct": false,
          "feedback": "Erreur : disperser les effets rend chaque fonction impure, c'est\nl'inverse du but.\n"
        },
        {
          "text": "Il les remplace par des valeurs de retour, sans jamais rien afficher",
          "correct": false,
          "feedback": "Erreur : renvoyer une valeur ne l'affiche pas ; un effet reste\nnécessaire au bout de la chaîne.\n"
        }
      ],
      "explanation": "Le style fonctionnel ne supprime pas les effets de bord, il les\ncantonne aux frontières du programme."
    },
    {
      "id": "q35",
      "difficulty": 2,
      "skills": [
        "ordre-superieur",
        "fermeture",
        "lambda",
        "trace"
      ],
      "title": "Une fonction fabriquée par une autre",
      "partie": "ordre-superieur",
      "statement": "Qu'affiche ce programme ?\n```python\ndef multiplieur(k):\n    return lambda x: k * x\n\nf = multiplieur(3)\nprint(f(5))\n```",
      "options": [
        {
          "text": "`15`",
          "correct": true,
          "feedback": "Bonne réponse : `multiplieur(3)` renvoie une fonction qui\nmultiplie son argument par $3$ ; `f(5)` vaut\n$3 \\times 5 = 15$.\n"
        },
        {
          "text": "`8`",
          "correct": false,
          "feedback": "Erreur : la fonction renvoyée multiplie, elle n'additionne pas.\n"
        },
        {
          "text": "`3`",
          "correct": false,
          "feedback": "Erreur : `f` n'est pas la valeur `3`, c'est une fonction qui\nse souvient de `3`.\n"
        },
        {
          "text": "Une erreur, `k` ayant disparu",
          "correct": false,
          "feedback": "Erreur : la fonction renvoyée conserve la valeur de `k`, c'est\nune fermeture.\n"
        }
      ],
      "explanation": "Une fonction peut en renvoyer une autre, qui garde en mémoire les\nvariables de l'appel qui l'a créée.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef multiplieur(k):\n    return lambda x: k * x\n\nf = multiplieur(3)\nprint(f(5))\n```",
          "options": [
            {
              "text": "`15`",
              "correct": true,
              "feedback": "Bonne réponse : `multiplieur(3)` renvoie une fonction qui\nmultiplie son argument par $3$ ; `f(5)` vaut\n$3 \\times 5 = 15$.\n"
            },
            {
              "text": "`8`",
              "correct": false,
              "feedback": "Erreur : la fonction renvoyée multiplie, elle n'additionne pas.\n"
            },
            {
              "text": "`3`",
              "correct": false,
              "feedback": "Erreur : `f` n'est pas la valeur `3`, c'est une fonction qui\nse souvient de `3`.\n"
            },
            {
              "text": "Une erreur, `k` ayant disparu",
              "correct": false,
              "feedback": "Erreur : la fonction renvoyée conserve la valeur de `k`, c'est\nune fermeture.\n"
            }
          ],
          "explanation": "Une fonction peut en renvoyer une autre, qui garde en mémoire les\nvariables de l'appel qui l'a créée."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef multiplieur(k):\n    return lambda x: k * x\n\nf = multiplieur(4)\nprint(f(6))\n```",
          "options": [
            {
              "text": "`24`",
              "correct": true,
              "feedback": "Bonne réponse : `multiplieur(4)` renvoie une fonction qui\nmultiplie son argument par $4$ ; `f(6)` vaut\n$4 \\times 6 = 24$.\n"
            },
            {
              "text": "`10`",
              "correct": false,
              "feedback": "Erreur : la fonction renvoyée multiplie, elle n'additionne pas.\n"
            },
            {
              "text": "`4`",
              "correct": false,
              "feedback": "Erreur : `f` n'est pas la valeur `4`, c'est une fonction qui\nse souvient de `4`.\n"
            },
            {
              "text": "Une erreur, `k` ayant disparu",
              "correct": false,
              "feedback": "Erreur : la fonction renvoyée conserve la valeur de `k`, c'est\nune fermeture.\n"
            }
          ],
          "explanation": "Une fonction peut en renvoyer une autre, qui garde en mémoire les\nvariables de l'appel qui l'a créée."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef multiplieur(k):\n    return lambda x: k * x\n\nf = multiplieur(2)\nprint(f(9))\n```",
          "options": [
            {
              "text": "`18`",
              "correct": true,
              "feedback": "Bonne réponse : `multiplieur(2)` renvoie une fonction qui\nmultiplie son argument par $2$ ; `f(9)` vaut\n$2 \\times 9 = 18$.\n"
            },
            {
              "text": "`11`",
              "correct": false,
              "feedback": "Erreur : la fonction renvoyée multiplie, elle n'additionne pas.\n"
            },
            {
              "text": "`2`",
              "correct": false,
              "feedback": "Erreur : `f` n'est pas la valeur `2`, c'est une fonction qui\nse souvient de `2`.\n"
            },
            {
              "text": "Une erreur, `k` ayant disparu",
              "correct": false,
              "feedback": "Erreur : la fonction renvoyée conserve la valeur de `k`, c'est\nune fermeture.\n"
            }
          ],
          "explanation": "Une fonction peut en renvoyer une autre, qui garde en mémoire les\nvariables de l'appel qui l'a créée."
        }
      ]
    },
    {
      "id": "q36",
      "difficulty": 2,
      "skills": [
        "ordre-superieur",
        "sorted",
        "key",
        "trace"
      ],
      "title": "Trier avec une fonction de clé",
      "partie": "ordre-superieur",
      "statement": "Que renvoie `sorted(['pomme', 'kiwi', 'banane'], key=len)` ?",
      "options": [
        {
          "text": "`['kiwi', 'pomme', 'banane']`",
          "correct": true,
          "feedback": "Bonne réponse : `key=len` indique de comparer les mots par leur\nlongueur ; la fonction `len` est passée en argument, sans être\nappelée.\n"
        },
        {
          "text": "`['banane', 'kiwi', 'pomme']`",
          "correct": false,
          "feedback": "Erreur : ce serait l'ordre alphabétique, sans `key`.\n"
        },
        {
          "text": "`['banane', 'pomme', 'kiwi']`",
          "correct": false,
          "feedback": "Erreur : l'ordre est croissant, du plus court au plus long.\n"
        },
        {
          "text": "Une erreur, `len` devant être appelée",
          "correct": false,
          "feedback": "Erreur : c'est la fonction elle-même qui est attendue, pas son\nrésultat.\n"
        }
      ],
      "explanation": "Le paramètre `key` de `sorted` reçoit une fonction : c'est un\nexemple de fonction d'ordre supérieur dans la bibliothèque\nstandard.",
      "variants": [
        {
          "statement": "Que renvoie `sorted(['pomme', 'kiwi', 'banane'], key=len)` ?",
          "options": [
            {
              "text": "`['kiwi', 'pomme', 'banane']`",
              "correct": true,
              "feedback": "Bonne réponse : `key=len` indique de comparer les mots par leur\nlongueur ; la fonction `len` est passée en argument, sans être\nappelée.\n"
            },
            {
              "text": "`['banane', 'kiwi', 'pomme']`",
              "correct": false,
              "feedback": "Erreur : ce serait l'ordre alphabétique, sans `key`.\n"
            },
            {
              "text": "`['banane', 'pomme', 'kiwi']`",
              "correct": false,
              "feedback": "Erreur : l'ordre est croissant, du plus court au plus long.\n"
            },
            {
              "text": "Une erreur, `len` devant être appelée",
              "correct": false,
              "feedback": "Erreur : c'est la fonction elle-même qui est attendue, pas son\nrésultat.\n"
            }
          ],
          "explanation": "Le paramètre `key` de `sorted` reçoit une fonction : c'est un\nexemple de fonction d'ordre supérieur dans la bibliothèque\nstandard."
        },
        {
          "statement": "Que renvoie `sorted(['cerise', 'ail', 'pomme'], key=len)` ?",
          "options": [
            {
              "text": "`['ail', 'pomme', 'cerise']`",
              "correct": true,
              "feedback": "Bonne réponse : `key=len` indique de comparer les mots par leur\nlongueur ; la fonction `len` est passée en argument, sans être\nappelée.\n"
            },
            {
              "text": "`['ail', 'cerise', 'pomme']`",
              "correct": false,
              "feedback": "Erreur : ce serait l'ordre alphabétique, sans `key`.\n"
            },
            {
              "text": "`['cerise', 'pomme', 'ail']`",
              "correct": false,
              "feedback": "Erreur : l'ordre est croissant, du plus court au plus long.\n"
            },
            {
              "text": "Une erreur, `len` devant être appelée",
              "correct": false,
              "feedback": "Erreur : c'est la fonction elle-même qui est attendue, pas son\nrésultat.\n"
            }
          ],
          "explanation": "Le paramètre `key` de `sorted` reçoit une fonction : c'est un\nexemple de fonction d'ordre supérieur dans la bibliothèque\nstandard."
        },
        {
          "statement": "Que renvoie `sorted(['sel', 'poivre', 'thym'], key=len)` ?",
          "options": [
            {
              "text": "`['sel', 'thym', 'poivre']`",
              "correct": true,
              "feedback": "Bonne réponse : `key=len` indique de comparer les mots par leur\nlongueur ; la fonction `len` est passée en argument, sans être\nappelée.\n"
            },
            {
              "text": "`['poivre', 'sel', 'thym']`",
              "correct": false,
              "feedback": "Erreur : ce serait l'ordre alphabétique, sans `key`.\n"
            },
            {
              "text": "`['poivre', 'thym', 'sel']`",
              "correct": false,
              "feedback": "Erreur : l'ordre est croissant, du plus court au plus long.\n"
            },
            {
              "text": "Une erreur, `len` devant être appelée",
              "correct": false,
              "feedback": "Erreur : c'est la fonction elle-même qui est attendue, pas son\nrésultat.\n"
            }
          ],
          "explanation": "Le paramètre `key` de `sorted` reçoit une fonction : c'est un\nexemple de fonction d'ordre supérieur dans la bibliothèque\nstandard."
        }
      ]
    },
    {
      "id": "q37",
      "difficulty": 3,
      "skills": [
        "ordre-superieur",
        "liste-de-fonctions",
        "composition",
        "trace"
      ],
      "title": "Enchaîner des fonctions rangées dans une liste",
      "partie": "ordre-superieur",
      "statement": "Qu'affiche ce programme ?\n```python\ndef enchaine(fonctions, x):\n    for f in fonctions:\n        x = f(x)\n    return x\n\nprint(enchaine([lambda x: x + 1, lambda x: x * 2], 3))\n```",
      "options": [
        {
          "text": "`8`",
          "correct": true,
          "feedback": "Bonne réponse : les fonctions s'appliquent dans l'ordre de la\nliste, d'abord $+1$ puis $\\times 2$ :\n$(3 + 1) \\times 2 = 8$.\n"
        },
        {
          "text": "`7`",
          "correct": false,
          "feedback": "Erreur : ce serait l'ordre inverse, $\\times 2$ puis $+1$.\n"
        },
        {
          "text": "`4`",
          "correct": false,
          "feedback": "Erreur : seule la première fonction aurait été appliquée.\n"
        },
        {
          "text": "Une erreur, une liste ne pouvant contenir des fonctions",
          "correct": false,
          "feedback": "Erreur : les fonctions sont des valeurs comme les autres, une\nliste peut en contenir.\n"
        }
      ],
      "explanation": "Une liste de fonctions parcourue par une boucle réalise une\ncomposition : l'ordre des éléments est l'ordre d'application.",
      "variants": [
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef enchaine(fonctions, x):\n    for f in fonctions:\n        x = f(x)\n    return x\n\nprint(enchaine([lambda x: x + 1, lambda x: x * 2], 3))\n```",
          "options": [
            {
              "text": "`8`",
              "correct": true,
              "feedback": "Bonne réponse : les fonctions s'appliquent dans l'ordre de la\nliste, d'abord $+1$ puis $\\times 2$ :\n$(3 + 1) \\times 2 = 8$.\n"
            },
            {
              "text": "`7`",
              "correct": false,
              "feedback": "Erreur : ce serait l'ordre inverse, $\\times 2$ puis $+1$.\n"
            },
            {
              "text": "`4`",
              "correct": false,
              "feedback": "Erreur : seule la première fonction aurait été appliquée.\n"
            },
            {
              "text": "Une erreur, une liste ne pouvant contenir des fonctions",
              "correct": false,
              "feedback": "Erreur : les fonctions sont des valeurs comme les autres, une\nliste peut en contenir.\n"
            }
          ],
          "explanation": "Une liste de fonctions parcourue par une boucle réalise une\ncomposition : l'ordre des éléments est l'ordre d'application."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef enchaine(fonctions, x):\n    for f in fonctions:\n        x = f(x)\n    return x\n\nprint(enchaine([lambda x: x + 1, lambda x: x * 2], 5))\n```",
          "options": [
            {
              "text": "`12`",
              "correct": true,
              "feedback": "Bonne réponse : les fonctions s'appliquent dans l'ordre de la\nliste, d'abord $+1$ puis $\\times 2$ :\n$(5 + 1) \\times 2 = 12$.\n"
            },
            {
              "text": "`11`",
              "correct": false,
              "feedback": "Erreur : ce serait l'ordre inverse, $\\times 2$ puis $+1$.\n"
            },
            {
              "text": "`6`",
              "correct": false,
              "feedback": "Erreur : seule la première fonction aurait été appliquée.\n"
            },
            {
              "text": "Une erreur, une liste ne pouvant contenir des fonctions",
              "correct": false,
              "feedback": "Erreur : les fonctions sont des valeurs comme les autres, une\nliste peut en contenir.\n"
            }
          ],
          "explanation": "Une liste de fonctions parcourue par une boucle réalise une\ncomposition : l'ordre des éléments est l'ordre d'application."
        },
        {
          "statement": "Qu'affiche ce programme ?\n```python\ndef enchaine(fonctions, x):\n    for f in fonctions:\n        x = f(x)\n    return x\n\nprint(enchaine([lambda x: x + 1, lambda x: x * 2], 10))\n```",
          "options": [
            {
              "text": "`22`",
              "correct": true,
              "feedback": "Bonne réponse : les fonctions s'appliquent dans l'ordre de la\nliste, d'abord $+1$ puis $\\times 2$ :\n$(10 + 1) \\times 2 = 22$.\n"
            },
            {
              "text": "`21`",
              "correct": false,
              "feedback": "Erreur : ce serait l'ordre inverse, $\\times 2$ puis $+1$.\n"
            },
            {
              "text": "`11`",
              "correct": false,
              "feedback": "Erreur : seule la première fonction aurait été appliquée.\n"
            },
            {
              "text": "Une erreur, une liste ne pouvant contenir des fonctions",
              "correct": false,
              "feedback": "Erreur : les fonctions sont des valeurs comme les autres, une\nliste peut en contenir.\n"
            }
          ],
          "explanation": "Une liste de fonctions parcourue par une boucle réalise une\ncomposition : l'ordre des éléments est l'ordre d'application."
        }
      ]
    }
  ]
}