Créer exos aléatoires vers $\LaTeX$

Bonjour à tous,

Comme déjà évoqué par des intervenants, le but est de créer un énoncé sous format $\LaTeX$ avec des données aléatoires (noms de points, mesures de côtés, d’angles, etc.).

Ma première idée :
Utiliser excel comme base puis faire écrire le code $LaTeX$ avec VBA dans un fichier *.txt par exemple (ou *.tex ?).

Vient une première question : dans VBA, je vais sûrement trouver des tutos mais si c’est simple, quelqu’un pourrait m’indiquer un code VBA qui permet d’écrire le contenu d’une cellule par exemple, dans un fichier ?
Je le répète, si c’est un peu de travail chronophage, j’irai chercher les tutos.

Deuxième question :
Une autre idée pour générer ce texte ?

Cordialement

Dom

Réponses

  • Bonjour,
    On peut écrire un programme Python qui génère le fichier .tex et écrit dessus.
  • Ok.
    Je trouve un avantage à éditer du Excel (voire du Word) car ce n’est pas du code que l’on lit.
    On voit bien le contenu de la feuille de calculs par exemple. Ensuite on code en VBA un code Latex.

    Mais je peux m’adapter, bien sûr.

    Merci pour la réponse.
  • Bonjour,

    Voici une façon de tout générer depuis le fichier .tex. J'ai donné un autre exemple plus complexe ici (calcul sur les fractions).
    \documentclass{article}
    % \usepackage{xparse} % seulement nécessaire si LaTeX plus vieux que 2020-10-01
    \usepackage{xfp}
    \usepackage{siunitx}
    \sisetup{
      output-decimal-marker = {,},  % virgule comme séparateur décimal
    }
    
    \ExplSyntaxOn
    \tl_new:N \l__dom_pointA_tl     % peut être autre que A
    \tl_new:N \l__dom_pointB_tl     % peut être autre que B
    \tl_new:N \l__dom_pointC_tl     % peut être autre que C
    
    \cs_new_protected:Npn \dom_choisir_noms_points_aleatoires:n #1
      {
        \seq_set_from_clist:Nn \l_tmpa_seq {#1}
        \seq_shuffle:N \l_tmpa_seq
        \seq_pop_left:NN \l_tmpa_seq \l__dom_pointA_tl
        \seq_pop_left:NN \l_tmpa_seq \l__dom_pointB_tl
        \seq_pop_left:NN \l_tmpa_seq \l__dom_pointC_tl
      }
    
    % Alias avec nom LaTeX2e
    \cs_new_eq:NN \choixPoints \dom_choisir_noms_points_aleatoires:n
    
    \cs_new:Npn \__dom_triangle_choisi:
      {
        \l__dom_pointA_tl \l__dom_pointB_tl \l__dom_pointC_tl
      }
    
    % Alias avec nom LaTeX2e
    \cs_new_eq:NN \triangleChoisi \__dom_triangle_choisi:
    
    \cs_new:Npn \pointA { \l__dom_pointA_tl }
    \cs_new:Npn \pointB { \l__dom_pointB_tl }
    \cs_new:Npn \pointC { \l__dom_pointC_tl }
    \cs_new:Npn \point #1 { \use:c { l__dom_point#1_tl } } % macro générique
    
    \ExplSyntaxOff
    
    \NewDocumentCommand \enonce { }
      {%
        Soit $\triangleChoisi$ un triangle rectangle en $\pointB$ tel que
        $\pointA \pointB = \SI{\fpeval{round(rand()*5, 2)}}{\centi\meter}$ et
        $\pointB \pointC = \SI{\fpeval{round(rand()*12, 2)}}{\centi\meter}$.
        Calculer $\pointA \pointC$.
    
        \smallskip
        Utilisation de la macro générique : le triangle est rectangle en
        $\point{B}$.%
      }
    
    \begin{document}
    % La « randomisation » se fait là.
    \choixPoints{ A, B, C, D, P, Q, R, S, T }%
    \enonce
    
    \bigskip
    \choixPoints{X,Y,Z}%
    \enonce
    
    \end{document}
    
    125398
  • Ha !
    Merci pour la réponse.

    Je me demandais s’il existait des « random » directement dans le code LateX.
    Très intéressant.

    Je n’ai pas encore essayé : le « point{B} » m’étonne au lieu de « pointB », vers la fin, juste avant le « begin{document} ».
  • \point est ce que j'ai appelé une macro générique.

    \choixPoints, qui est un alias pour \dom_choisir_noms_points_aleatoires:n, initialise un certain nombre de points (ici, 3 mais ça pourrait être ce que l'on veut) en permutant aléatoirement la liste passée en argument puis en stockant le premier dans \l__dom_pointA_tl, le deuxième dans \l__dom_pointB_tl et le troisième dans \l__dom_pointC_tl.

    Ces trois noms de macros utilisent les conventions de nommage expl3 (préfixe 'l' = variable locale, suivi de deux '_' = interface privée, espace de nommage 'dom' pour éviter les clashes, suffixe '_tl' = type token list). Tu ne peux pas les utiliser directement en dehors de \ExplSyntaxOn... \ExplSyntaxOff car en temps normal, le _ déclenche une erreur s'il n'est pas utilisé en mode maths (et même en mode maths, il ne peut faire partie d'un nom de macro, sauf à utiliser \csname... \endcsname pour fabriquer le token). Par contre, en régime \ExplSyntaxOn, _ et : sont considérés comme des lettres du point de vue de la table des catcodes de TeX ; ils peuvent donc être utilisés directement dans des noms de macros.

    Tu pourrais écrire ton énoncé en syntaxe \ExplSyntaxOn comme ça :
    \NewDocumentCommand \enonce { }
      {
        Soit~$\__dom_triangle_choisi:$~un~triangle~rectangle~en~$\l__dom_pointB_tl$~tel~
        ...
      }
    
    ou encore comme ça, parce que les espaces sont (presque partout) ignorés en régime \ExplSyntaxOn :
    \NewDocumentCommand \enonce { }
      {
        Soit~ $\__dom_triangle_choisi:$~ un~ triangle~ rectangle~ en~ $\l__dom_pointB_tl$~ tel~
        ...
      }
    
    On voit que ce “catcode régime” n'est pas très agréable pour écrire des phrases — il est plus prévu pour programmer. Pour cette raison, mon code crée trois macros \pointA, \pointB et \pointC qui, après une étape de développement, donnent chacune respectivement \l__dom_pointA_tl, \l__dom_pointB_tl et \l__dom_pointC_tl. Ainsi, on peut utiliser \pointA, \pointB et \pointC à la place de \l__dom_pointA_tl, \l__dom_pointB_tl et \l__dom_pointC_tl, et cela ne nécessite aucune acrobatie, y compris en régime \ExplSyntaxOff (« syntaxe LaTeX2e »).

    Parenthèse. C'est presque la même chose pour \__dom_triangle_choisi: et \triangleChoisi. La seule différence est que \triangleChoisi est figé égal à la définition de \__dom_triangle_choisi: au moment où ceci est exécuté :
    \cs_new_eq:NN \triangleChoisi \__dom_triangle_choisi:  # comme \let mais mieux
    
    (mieux car cela génère une erreur si \triangleChoisi est déjà défini). C'est un chouia plus efficace que ceci :
    \cs_new:Npn \triangleChoisi { \l__dom_pointA_tl \l__dom_pointB_tl \l__dom_pointC_tl }
    
    qui nécessiterait une étape de développement pour passer de \triangleChoisi à :
    \l__dom_pointA_tl \l__dom_pointB_tl \l__dom_pointC_tl
    
    Par contre, si l'on s'amuse à redéfinir \__dom_triangle_choisi: après le
    \cs_new_eq:NN \triangleChoisi \__dom_triangle_choisi:
    
    cela n'affecte pas \triangleChoisi (c'est le principe de \let). Fin de la parenthèse.

    Maintenant, revenons à ma « macro générique » \point. C'est tout simple : pour tout $x$, trois étapes de développement sur \point{x} donnent le control sequence token (“macro”) \l__dom_point$x$_tl (même si $x$ contient des caractères bizarres comme des espaces ou des '\'). Cette macro permet donc d'obtenir \l__dom_pointZZZ_tl avec \point{ZZZ}, sans qu'il soit nécessaire d'avoir préalablement exécuté :
    \cs_new:Npn \pointZZZ { \l__dom_pointZZZ_tl }
    
  • Merci pour le temps passé brian.
    Comme d’habitude, il y a du contenu !

    Je vais bosser ça :-)
  • Avec plaisir. Le code suivant fait la même chose que le précédent, mais sans noms de macros intermédiaires en syntaxe expl3. C'est suffisant si l'aspect programmation se résume à la portion congrue dans \choixPoints. Tu le trouveras sans doute plus facile à comprendre, au moins pour commencer :
    \documentclass{article}
    % \usepackage{xparse} % seulement nécessaire si LaTeX plus vieux que 2020-10-01
    \usepackage{xfp}
    \usepackage{siunitx}
    \sisetup{
      output-decimal-marker = {,},  % virgule comme séparateur décimal
    }
    
    \ExplSyntaxOn
    \NewDocumentCommand \choixPoints { m }
      {
        \seq_set_from_clist:Nn \l_tmpa_seq {#1}
        \seq_shuffle:N \l_tmpa_seq
        \seq_pop_left:NN \l_tmpa_seq \pointA
        \seq_pop_left:NN \l_tmpa_seq \pointB
        \seq_pop_left:NN \l_tmpa_seq \pointC
      }
    
    \cs_new:Npn \triangleChoisi { \pointA \pointB \pointC }
    \cs_new:Npn \point #1 { \use:c { point#1 } } % macro générique
    \ExplSyntaxOff
    
    \NewDocumentCommand \enonce { }
      {%
        Soit $\triangleChoisi$ un triangle rectangle en $\pointB$ tel que
        $\pointA \pointB = \SI{\fpeval{round(rand()*5, 2)}}{\centi\meter}$ et
        $\pointB \pointC = \SI{\fpeval{round(rand()*12, 2)}}{\centi\meter}$.
        Calculer $\pointA \pointC$.
    
        \smallskip
        Utilisation de la macro générique : le triangle est rectangle en
        $\point{B}$.%
      }
    
    \begin{document}
    
    % La « randomisation » se fait là.
    \choixPoints{ A, B, C, D, P, Q, R, S, T }%
    \enonce
    
    \bigskip
    \choixPoints{X,Y,Z}%
    \enonce
    
    \end{document}
    
  • Je préfère un script en Python qui sort automagiquement le document en $\LaTeX$, ça me semble plus flexible et plus… scriptable, par exemple, sortir trente fiches d’exercices différentes chacune avec un nom dessus et avec le corrigé nominatif. Je ne doute pas qu’on puisse faire ça avec $\LaTeX$ mais avec Python, on peut avoir accès à des bibliothèques mathématiques plus poussées. En fouinant dans ma machine (Debian), je trouve le paquet python3-plastex qui semble faire le boulot.
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Python est puissant et pratique, on le sait ; mais du code qui génère du code, ce n'est pas toujours très lisible. Tout est possible ; je ne prends pas parti.

    Je prolonge un peu mon message précédent. Reprenons la définition ci-dessus :
    \NewDocumentCommand \enonce { }
      {%
        Soit $\triangleChoisi$ un triangle rectangle en $\pointB$ tel que
        $\pointA \pointB = \SI{\fpeval{round(rand()*5, 2)}}{\centi\meter}$ et
        $\pointB \pointC = \SI{\fpeval{round(rand()*12, 2)}}{\centi\meter}$.
        Calculer $\pointA \pointC$.
    
        \smallskip
        Utilisation de la macro générique : le triangle est rectangle en
        $\point{B}$.%
      }
    
    Avec le code donné dans mon message précédent, lorsque \pointA est développée, on obtient un lexème caractère explicite (explicit character token), par exemple X, Y ou Z après \choixPoints{X,Y,Z} ; autrement dit, pour TeX, après une étape de développement — qui se fait en général automatiquement —, c'est exactement comme si on avait tapé la lettre choisie aléatoirement à la place de \pointA (idem pour \pointB et \pointC). Pas de problème en vue. Mais si on a initialisé un peu différemment :
    \choixPoints{A_1, A_2, \mathscr{B}}
    
    on s'approche d'une zone dangereuse. Par exemple, après développement, ceci :
    Calculer $\pointA \pointC$.
    
    peut devenir cela (n'oublions pas la permutation pseudo-aléatoire appliquée dans \choixPoints) :
    Calculer $A_1A_2$.    % kif kif $A_1 A_2$ car mode maths
    
    Ça passe encore, mais on sent les problèmes arriver. Si mon énoncé contient par exemple :
    $f_\pointA$
    
    là, c'est cuit, parce que « f_A_1 » en mode maths n'est pas syntaxiquement correct (c'est ambigu). Il fallait écrire « f_{\pointA} ». Pour éviter ce genre de problème, il vaut donc mieux mettre des accolades autour des paramètres (à supposer qu'on veuille bien en faire des \mathord : c'est effectivement le cas ici, car les paramètres représentent tous des noms de points : on ne veut pour aucun d'eux un espacement spécial autour comme pour les opérateurs ou relations binaires, les \mathop, les délimiteurs ouvrants ou fermants, etc.). On arrive donc à ceci, qui est un peu plus propre :
    \NewDocumentCommand \enonce { }
      {%
        % Les accolades sont totalement inutiles dans la ligne suivante, c'est
        % juste pour la cohérence de style :
        Soit ${\triangleChoisi}$ un triangle rectangle en ${\pointB}$ tel que
        ${\pointA} {\pointB} = \SI{\fpeval{round(rand()*5, 2)}}{\centi\meter}$ et
        ${\pointB} {\pointC} = \SI{\fpeval{round(rand()*12, 2)}}{\centi\meter}$.
        Calculer ${\pointA} {\pointC}$.
      }
    
    (j'enlève exprès le deuxième paragaphe). On peut aussi décider d'expliciter les paramètres de la macro \enonce :
    \NewDocumentCommand \enonce { m m m }
      {%
        Soit ${#1}{#2}{#3}$ un triangle rectangle en $#2$ tel que
        ${#1} {#2} = \SI{\fpeval{round(rand()*5, 2)}}{\centi\meter}$ et
        ${#2} {#3} = \SI{\fpeval{round(rand()*12, 2)}}{\centi\meter}$.
        Calculer ${#1} {#3}$.
      }
    
    Dans ce cas, il faudra faire \enonce{\pointA}{\pointB}{\pointC} ou \enonce{P}{Q}{R} lors de l'appel. Mais attention :
    • avec cette façon de faire, on ne peut dépasser 9 paramètres ;
    • la lisibilité du code décroît sensiblemement avec le nombre de paramètres.
    Pour éviter ces écueils, on peut soit garder la première méthode (avec toutes les accolades pour être bien propre), soit utiliser une syntaxe d'entrée de type keyval list qui permet de passer un nombre arbitraire de « paramètres » nommés :
    \enonce{pointA=B, pointB=M_n, pointC=\mathscr{U}}
    
    Cette approche n'empêche pas d'utiliser en argument des macros quelconques contenant des noms choisis aléatoirement, telles que nos macros \pointA, \pointB et \pointC définies ci-dessus :
    \enonce{pointA=\pointA, pointB=\pointB, pointC=\pointC}
    
    Si cette façon de faire intéresse, je peux proposer une implémentation avec pgfkeys ou l3keys.
  • Je complète un dernier coup (enfin, j'espère !). La raison principale est que l'espacement entre les lettres pour un triangle $ABC$ ou une longeur $YZ$ avec les codes ci-dessus ne me plaît pas. Comme on le sait, l'espacement entre des lettres (ou symboles apparentés) en mode maths est plutôt fait pour représenter des produits de variables ou de constantes. Cela donne des espaces bizarres lorsqu'on nomme un triangle avec ses trois sommets, ou encore la longueur d'un segment en listant les deux points l'un après l'autre (curieusement, je n'ai jamais vu ce problème traité dans ce que j'ai lu sur (La)TeX). Pour y remédier, je propose d'utiliser \mathit, ou plutôt un alias que j'appelle \pts — comme ça, si un jour j'ai une meilleure idée, je change juste la définition de \pts. J'en profite pour ajouter un peu de balisage sémantique avec des macros très simples \dist et \triang s'appuyant sur \pts.

    Deuxième chose : je bricole un petit peu pour que mon code stocke dans \pointA, \pointB, \pointC non pas le contenu directement sorti du tirage aléatoire, mais celui-ci entouré d'accolades (vu comment sont écrites \dist et \triang, ça fait « ceinture et bretelles » avec elles ; mais ainsi, c'est robuste même si on n'utilise pas des macros aussi bien écrites :-D).

    Troisième chose : idem mais en explicitant les paramètres de l'éononcé et en les passant sous la forme d'une keyval list (clé1=valeur1, clé2=valeur2...) comme proposé dans mon message précédent, ceci implémenté avec le package pgfkeys. La partie de ce fichier située entre $\backslash$begin{document} et $\backslash$end{document} est comme suit :
    % La « randomisation » se fait là : affectation de \pointA, \pointB et \pointC.
    \choixPoints{ A, B, C, D, P, Q, R, S, T }%
    \enoncePyth[point 1=\pointA, point 2=\pointB, point 3=\pointC]
    
    \medskip
    \choixPoints{X,Y,Z}%
    \enoncePyth[point 1=\pointA, point 2=\pointB, point 3=\pointC]
    
    \medskip
    \enoncePyth[point 1=P, point 2=Q, point 3=R] % pas d'aléatoire ici
    
    \medskip
    \enoncePyth % ni là (utilisation des valeurs par défaut A, B, C)
    
    125428
    125430
  • Je vais regarder ça tantôt. J’aime bien ça fait du boulot qu’on a envie de faire ;-)
    Ha oui, j’observe moins d’espace entre les lettres dans ces nouvelles versions que dans la première.
    Apparemment j’ai trouvé quelque de plus pointilleux que moi sur la mise forme des documents (:D

    Merci encore.
Connectez-vous ou Inscrivez-vous pour répondre.