2. Tutoriel : Créer un nouvel exercice¶
Cette article décrit la procédure pour créer un nouvel exercice pour Pyromaths. Nous prendrons comme exemple la création d’un exercice de résolution d’équations du premier degré du type \(ax+b=cx+d\) où les nombres \(a\), \(b\), \(c\), \(d\) sont des entiers relatifs.
Note
Certains termes techniques anglais (comme template, merge, etc.) n’ont volontairement pas été traduits. Cette documentation renvoit à d’autres documentation en anglais, et pour que la personne lisant ces lignes s’y retrouve d’une documentation à l’autre, nous avons fait le choix de conserver les termes anglais plutôt que d’utiliser leur équivalent français.
Note
Cette documentation a été écrite en se mettant à la place d’une personne utilisant GNU/Linux. Il est également possible d’écrire un nouvel exercice depusi Windows ou MacOS, mais certaines commandes décrites dans ce document changent un peu. À vous de les adapter.
2.1. Préambule¶
Les personnes pressées peuvent jeter un œil à deux exercices implémentés dans Pyromaths :
un exemple simple (avec très peu de cas particuliers) : la recherche d’état stable.
- classe
EtatStableSysteme2
du modulematrices.py
template de l'énoncé
template de la solution
- classe
un exemple plus complexe (avec cas particuliers) : bilan sur les polynômes du second degré en seconde.
- classe
BilanTrinomeSansDiscriminant
du moduleSecondDegre.py
; template de l'énoncé
;template de la solution
.
- classe
2.2. Prérequis¶
2.2.1. Loi¶
Pyromaths est publié sous licence publique GNU version 3 (GPLv3). Si vous souhaitez contribuer à Pyromaths en partageant votre exercice, celui-ci devra impérativement être également publié sous cette même licence.
2.2.2. Connaissances¶
Créer un exercice pour Pyromaths nécessite de savoir utiliser un minimum :
- \(LaTeX\) ;
- Python (version 3) ;
- git.
Une connaissance de la bibliothèque Python jinja2 est un plus, mais les bases s’apprennent rapidement et sont décrites plus loins dans ce document.
2.2.3. Outils¶
À part git, tous les outils nécessaires pour créer un exercice sont des dépendances de Pyromaths. Installez la version de développement, et faites fonctionner Pyromath : les outils nécessaires pour ce tutoriel seront alors disponibles.
2.3. Environnement de travail¶
Commençons par télécharger les sources de Pyromaths, en utilisant le logiciel git. Si vous avez un compte Framagit, utilisez :
$ git clone git@framagit.org/pyromaths/pyromaths.git
Si vous n’avez pas de tel compte, utilisez :
$ git clone https://framagit.org/pyromaths/pyromaths.git
Puis déplacez vous dans le répertoire pyromaths ainsi créé. À partir de maintenant, sauf mention contraire, toutes les commandes sont à exécuter depuis ce répertoire.
La version de développement de Pyromaths se trouve dans la branche develop :
$ git checkout develop
Il sera plus confortable, pour vous comme pour nous, que vous travailliez dans une branche séparée, par exemple EquationPremierDegre :
$ git branch EquationPremierDegre
$ git checkout EquationPremierDegre
2.4. Brouillon¶
La première étape est d’écrire un exercice en \(LaTeX\), sans passer par Python, sans aléa : juste pour observer le rendu final. Utilisez l’outil en ligne de commandes pyromaths.
$ pyromaths dummy
Note
Si pyromaths n’est pas (encore) installé chez vous, ou une version trop ancienne, vous pouvez télécharger le dépôt et utiliser le binaire utils/pyromaths
à la place de pyromaths
pour utiliser la version de développement.
Cette commande a pour effet de créer un modèle d’exercice, sous la forme d’un PDF qui est affiché à l’écran, et d’un fichier \(LaTeX\) exercices.tex
.
Déplacez ce fichier dans un répertoire temporaire, et modifiez-le pour écrire le sujet de votre énoncé, à la place de ÉNONCÉ DE L'EXERCICE
et CORRIGÉ DE L'EXERCICE
. Ne vous souciez pas de la manière dont cela sera intégré à Pyromaths ; ne vous souciez pas de la manière dont l’aléa sera intégré : nous verrons cela plus tard. C’est l’occasion de travailler la formulation de l’énoncé et de la solution pour qu’ils soient le plus clair possible.
Ne modifiez que les lignes qui correspondent à l’énoncé ou au corrigé. En particulier, ne modifiez pas le préambule.
Ce fichier doit être compilé avec latex
, puis converti en pdf avec dvipdf
. À la fin de cette étape, nous obtenons l’énoncé suivant (tex
, pdf
).
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% DÉBUT DE L'ÉNONCÉ
\exercice
Déterminer les solutions de l'équation $3x+2=-x-1$. Si nécessaire, les solutions seront arrondies au centième.
% FIN DE L'ÉNONCÉ
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\label{LastPage}
\newpage
\currentpdfbookmark{Le corrigé des exercices}{Corrigé}
\lhead{\textsl{{\footnotesize Page \thepage/ \pageref{LastCorPage}}}}
\setcounter{page}{1} \setcounter{exo}{0}
\
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% DÉBUT DU CORRIGÉ
\exercice*
\begin{align*}
3x+2 &= -x-1 \\
3x+x &= -1-2 \\
4x &= -3 \\
x &= -\frac{3}{4} \\
x &= -0,75
\end{align*}
L'unique solution est $x=-0,75$.
% FIN DU CORRIGÉ
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
2.5. Première version (sans aléa)¶
Nous allons maintenant intégrer cet exercice à Pyromaths, sans aléa pour le moment.
Choisissez un identifiant pour votre exercice : un nom composé uniquement de lettres sans accents et de chiffres, sans espaces, comme ConversionDegresRadians
, TheoremeDePythagore
, CoordonneesDuMilieu
, etc. Pour notre exemple, nous choissons EquationPremierDegre
(qui sera décliné en EquationPremierDegre2
, EquationPremierDegre3
, etc. au fil de ce tutoriel).
Notez que si le nom commence par un tiret bas (par exemple _TheoremeDePythagore
), l’exercice sera ignoré (cela peut-être utile pour « désactiver » un exercice en cours de création).
2.5.1. Code Python¶
Le code Python de l’exercice doit être placé dans un des sous-dossiers de pyromaths/ex/
. Dans notre cas, ce sera pyromaths/ex/troisiemes
. Ensuite, modifiez un des fichiers .py déjà existant, ou créez-en un nouveau. Gardez une certaine logique : un exercice sur Pythagore a sa place dans le même fichier qu’un autre exercice sur Pythagore ; un exercice de trigonométrie n’a pas sa place dans un fichier matrices.py
. Dans notre cas, nous crréons un nouveau fichier contenant le code suivant.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # Pyromaths
#
# Un programme en Python qui permet de créer des fiches d'exercices types de
# mathématiques niveau collège ainsi que leur corrigé en LaTeX.
#
# Copyright (C) 2018 -- Louis Paternault (spalax@gresille.org)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
"""Équations du premier degré"""
from pyromaths.ex import Jinja2Exercise
class EquationPremierDegre2(Jinja2Exercise):
"""Résolution d'équations du premier degré à coefficients entiers."""
tags = ['Troisième', 'Équation', 'Premier degré']
|
Modifiez les parties suivantes :
- ligne 8 : votre nom, et l’année courante ;
- ligne 27 : l’identifiant de l’exercice ;
- ligne 28 : la description de l’exercice ;
- ligne 30 : les tags (étiquettes) de l’exercice (ceux-ci doivent contenir le niveau, plus d’autres à votre guise ; pour connaître la liste des tags déjà utilisés, utilisez
pyromaths tags
).
2.5.2. Code \(LaTeX\)¶
Le code \(LaTeX\), quant à lui, doit être placé dans le répertoire pyromaths/data/exercices/templates
, dans deux fichiers au nom de votre exercices. Reprenez votre fichier exercices.tex
, et extrayez les lignes correspondant à l’énoncé, que vous écrivez dans le fichier EquationPremierDegre2-statement.tex
, et celles correspondant au corrigé dans le fichier EquationPremierDegre2-answer.tex
.
L’énoncé est alors dans le fichier EquationPremierDegre2-statement.tex
.
1 2 3 | \exercice
Déterminer les solutions de l'équation $3x+2=-x-1$. Si nécessaire, les solutions seront arrondies au centième.
|
Le corrigé est dans le fichier EquationPremierDegre2-answer.tex
1 2 3 4 5 6 7 8 9 10 11 | \exercice*
\begin{align*}
3x+2 &= -x-1 \\
3x+x &= -1-2 \\
4x &= -3 \\
x &= -\frac{3}{4} \\
x &= -0,75
\end{align*}
L'unique solution est $x=-0,75$.
|
2.5.3. Génération de l’exercice¶
Vous pouvez maintenant tester la génération de votre exercice, en exécutant la commande suivante.
$ pyromaths generate EquationPremierDegre2
Vous obtenez alors le fichier exercice.pdf
.
2.5.4. Bilan¶
Nous avons écrit notre premier exercice, qui est intégré à Pyromaths. Par contre, il n’y a pas d’aléa : les valeurs numériques sont toujours les mêmes. Cela sera résolu dans la partie suivante.
2.6. Ajout du hasard¶
Dans cette partie, pour générer l’exercice et suivre votre travail, la commande à utiliser est la suivante.
$ pyromaths generate EquationPremierDegre3:2
Remarquez que par rapport à la commande utilisée dans la partie précédente, un :2
a été ajouté à la fin de la ligne. Il correspond à la graine (seed) du générateur pseudo-aléatoire.
Note
Un ordinateur ne sait pas générer du hasard. Il faut ruser.
Dans notre exercice, nous avons besoin de nombres entiers entre 0 et 9. Pour avoir des nombres aléatoires, à chaque fois que nous utilisons un nombre aléatoire, nous prenons une décimale de π : d’abord 1, puis 4, puis 1, puis 5, et ainsi de suite. Cela a l’air aléatoire à première vue, mais deux exécutions successives donneront exactement le même exercice. Améliorons cela.
Nous gardons le même système, mais au lieu de commencer à la première décimale de π, nous utilisons désormais sur l’heure courante : si le programme est lancé à 13h37, nous utilisons alors les décimales de π à partir de la 1337e. Ainsi, deux exécutions successives donneront deux exercices différents.
C’est mieux. Mais quand nous créerons notre exercices, nous allons générer encore et encore un exercice, et nous aimerions toujours générer le même (cela facilitera le développement, pour ne pas être perturbé par des valeurs numériques qui changent ; pour qu’un bug introduit par une valeur numérique spécifique n’apparaisse et ne disparaisse pas aléatoirement). Du coup, nous imposons le début de la séquence aléatoire : c’est la signification du :2
ajouté à la fin de la ligne de commande.
C’est un peu plus compliqué en réalité, mais dans les grande lignes, c’est ainsi qu’un ordinateur génère du hasard. Plus d’informations, par exemple, dans l’article de Wikipédia Pseudorandom generator.
Si nous voulons générer un autre exercice, il suffit de transformer le EquationPremierDegre3:2
en EquationPremierDegre3:1729
, EquationPremierDegre3:0123456789
, ou n’importe quel nombre de votre choix.
2.6.1. Code Python¶
Du côté de Python, il faut tirer au hasard quatre nombres entiers entre -10 et 10 (sauf 0 et 1, qui sont des cas particuliers), et les rendre disponible depuis le code \(LaTeX\). Cela se fait avec le contexte. Toutes les variables présentes dans ce dictionnaire seront accessibles depuis le template jinja2.
1 2 3 4 5 6 7 8 9 | def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.context = {
"a": random.choice([1, -1]) * random.randint(2, 9),
"b": random.choice([1, -1]) * random.randint(2, 9),
"c": random.choice([1, -1]) * random.randint(2, 9),
"d": random.choice([1, -1]) * random.randint(2, 9),
}
|
2.6.2. Code \(LaTeX\)¶
Du côté de \(LaTeX\), nous allons profiter de la bibliothèque jinja2 pour utiliser les variables rendues disponibles dans le contexte.
Note
Cette note se veut une courte introduction à Jinja2. Pour aller plus loins, rendez-vous sur le site du projet.
Un template jinja2 est du code \(LaTeX\) qui sera reproduit tel quel dans le document final, sauf que :
- les variables peuvent être évaluées avec des doubles parenthèses. Pour insérer la valeur de la variable
a
du contexte, il faut utiliser(( a ))
; - des structures de contrôle (condition, boucle) peuvent être utilisées entourées par
(*
et*)
.
Notons que les chaînes définissant ces blocs ont été modifiées par rapport aux chaînes initiales, car trop proches de la syntaxe \(LaTeX\). Ceci est documenté sur le site officiel, et mis en œuvre dans la classe pyromaths.outils.jinja2tex.LatexEnvironment
.
1 2 3 4 5 6 7 8 9 10 11 12 13 | class LatexEnvironment(Environment):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.block_start_string = '(*'
self.block_end_string = '*)'
self.variable_start_string = '(('
self.variable_end_string = '))'
self.comment_start_string = '(% '
self.comment_end_string = ' %)'
self.trim_blocks = True
self.lstrip_blocks = True
|
L’énoncé est assez simple : il suffit de faire appel aux variables du contexte.
1 2 3 4 | \exercice
Déterminer les solutions de l'équation $(( a )) x (( "%+d"|format(b) ))= (( c )) x (( "%+d"|format(d) ))$.
Si nécessaire, les solutions seront arrondies au centième.
|
Dans ce code, (( a ))
et (( c ))
sont remplacés par les valeurs des variables a
et c
du contexte, et (( "%+d"|format(b) ))
est remplacé par le résultat du code Python "%+d" % b
, ce qui a pour effet d’écrire l’entier b
avec son signe (qu’il soit positif ou négatif).
La rédaction du corrigé se fait de la même manière, en remarquant que le code (( d - b ))
, par exemple, est remplacé par le résultat du calcul d - b
. Notons également l’utilisation de (( ((d-b)/(a-c)) | round(2) ))
, qui permet d’arrondir le résultat du calcul (d-b)/(a-c)
à deux chiffres après la virgule. L’ensemble de ces fonctions (format
, round
, etc.), que jinja2 appelle filters, est décrit dans la documentation officielle.
1 2 3 4 5 6 7 8 9 10 11 | \exercice*
\begin{align*}
(( a )) x (( "%+d"|format(b) )) &= (( c )) x (( "%+d"|format(d) )) \\
(( a )) x (( "%+d"|format(-c) ))x &= (( d )) (( "%+d"|format(-b) )) \\
(( a - c )) x &= (( d - b )) \\
x &= \frac{(( d - b ))}{(( a - c ))} \\
x &\simeq \numprint{(( ((d-b)/(a-c)) | round(2) ))}
\end{align*}
L'unique solution est $x\simeq\numprint{(( ((d-b)/(a-c)) | round(2) ))}$.
|
2.6.3. Débuggage¶
Durant cette phase, il est probable que le code \(LaTeX\) produit soit un peu compliqué, et contienne des erreurs. Il serait alors pratique de pouvoir observer (si ce n’est plus) ce code avant compilation. C’est possible avec l’option --pipe
de cli.
Cette option permet de définir des commandes (du shell) qui seront executées sur le fichier \(LaTeX\), avant sa compilation. Par exemple :
--pipe cat
exécutecat FICHIER.tex
, et permet d’observer le fichier avant compilation ;--pipe vim
exécutevim FICHIER.tex
, et permet de modifier le fichier avant compilation ;--pipe "cp {} draft.tex"
exécutecp FICHIER.tex draft.tex
, et permet d’obtenir une copie du fichier \(LaTeX\), si le problème est trop complexe pour pouvoir être résolu avec les options ci-dessous ;- et n’importe quelle commande du shell peut-être exécutée, au gré de votre imagination.
2.6.4. Bilan¶
Nous avons produit l’exercice exercice.pdf
. Il fonctionne, mais il y a trois problèmes dans le corrigé : premièrement, alors que la solution est exacte, le signe \(\simeq\) est utilisé ; ensuite, bien que la solution soit entière, le code a produit 3,0
plutôt que 3
; enfin, un troisième problème n’apparaît pas ici, mais sera expliqué et résolu plus loin dans ce document.

2.7. Structures de contrôle¶
Dans la correction de l’exercice, le signe utilisé pour donner la solution est \(\simeq\), que la solution soit exacte ou non. Cela peut être corrigé avec une structure de contrôle.
2.7.1. Structures de contrôles¶
Pour corriger la relation d’équivalence (égalité ou approximation), il suffit de tester si la solution est exacte ou non. Pour cela, nous testons si la solution (multipliée par 100) est égale à la partie entière de la solution, multipliée par 100 elle aussi.
9 10 11 12 13 | (* if 100*(d-b)/(a-c) == (100*(d-b)/(a-c))|int *)
=
(* else *)
\simeq
(* endif *)
|
Pour tester si la solution est exacte, nous aurions aussi pu définir un test personnalisé.
D’autres structures de contrôle sont disponibles ; elles sont détaillées dans la documentation officielle.
2.7.2. Bilan¶
La source de la correction est maintenant celle-ci.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | \exercice*
\begin{align*}
(( a )) x (( b|facteur("so") )) &= (( c )) x (( d|facteur("so") )) \\
(( a )) x (( -c|facteur("so") ))x &= (( d )) (( -b|facteur("so") )) \\
(( a - c )) x &= (( d - b )) \\
x &= \frac{(( d - b ))}{(( a - c ))} \\
x &
(* if 100*(d-b)/(a-c) == (100*(d-b)/(a-c))|int *)
=
(* else *)
\simeq
(* endif *)
\numprint{(( ((d-b)/(a-c)) | round(2) ))}
\end{align*}
L'unique solution est
$x
(* if 100*(d-b)/(a-c) == (100*(d-b)/(a-c))|int *)
=
(* else *)
\simeq
(* endif *)
\numprint{(( ((d-b)/(a-c)) | round(2) ))}$.
|
Elle produit ce résultat
. Reste à traiter le problème de l’affichage des nombres entiers (3,0
au lieu de 3
).
2.8. Affichage des nombres, et filters personnalisés¶
2.8.1. Problème¶
Afficher un nombre n’est pas aussi simple qu’il n’y paraît. Dans notre exemple, le code produit 3,0
plutôt que 3
, à cause de Python qui manipule des flottants, et écrit donc la première version pour insister sur le type flottant plutôt qu’entier. Mais il y a bien pire.
Supposons par exemple que nous voulons afficher l’équation de la droite d’équation \(y=ax+b\), où \(a\) et \(b\) sont des nombres entiers. A priori, utiliser y=\numprint{(( a ))}x+\numprint{(( b ))}
dans notre template devrait faire l’affaire, non ? Non. Plusieurs problèmes peuvent se poser.
- Si \(a=0\) et \(b=2\), nous obtenons
y=0x+2
au lieu dey=2
. - Si \(a=1\) et \(b=2\), nous obtenons
y=1x+2
au lieu dey=x+2
. - Si \(a=-1\) et \(b=2\), nous obtenons
y=-1x+2
au lieu dey=-x+2
. - Si \(a=2\) et \(b=0\), nous obtenons
y=2x+0
au lieu dey=2x
. - Si \(a=2\) et \(b=-2\), nous obtenons
y=2x+-2
au lieu dey=2x-2
. - Si \(a=0\) et \(b=0\), nous obtenons
y=0x+0
au lieu dey=0
.
Cela fait beaucoup de cas à traiter. Tous (sauf le dernier) peuvent être résolu en utilisant le filter pyromaths.outils.jinja2utils.facteur()
.
2.8.2. Filters personnalisés¶
Un filter est une fonction qui peut être transmise au template afin d’être appelée depuis le template. Ils sont décrits sur la documentation officielle.
Une fonction du module pyromaths.outils.jinja2utils
existe pour corriger les problèmes cités plus haut : facteur()
permet de formatter correctement les facteurs dans une expression. Encore faut-il que cette fonction soit accessible depuis le template \(LaTeX\).
Ajoutons la méthode suivante à la classe EquationPremierDegre4
:
1 2 3 4 5 6 7 | @property
def environment(self):
environment = super().environment
environment.filters.update({
'facteur': facteur,
})
return environment
|
Celle-ci a pour effet d’ajouter à l’environnement jinja2 la fonction facteur()
comme un filter, qui est alors accessible depuis le template. Dans cette ligne, le nombre \(\frac{d-b}{a-c}\) est arrondi à deux décimales après la virgule, et affiché en respectant les règles françaises (notamment avec une virgule comme séparateur décimal).
14 | (( ((d-b)/(a-c))|facteur("2") ))
|
Un filter n’est rien d’autre qu’une fonction python. D’autres filters peuvent donc être définis et utilisés à votre convenance.
Note
Pour revenir au problème du début de cette partie, afficher l’équation d’une droite peut se faire en utilisant le code suivant, qui prend en charge tous les cas particuliers décrits plus haut.
(* if a == 0 and b == 0 *)
y = 0
(* else *)
y = (( a|facteur("*x") )) (( b|facteur("*so") ))
(* endif *)
- Si \(a=2\) et \(b=3\), le code \(LaTeX\) produit est
y=2x+3
. - Si \(a=0\) et \(b=0\), le test
if
permet d’afficher la bonne équation. - Si \(a=0\) l’option
"*x"
permet de ne rien afficher. - Si \(a=1\) l’option
"*x"
permet d’afficherx
plutôt que1x
. - Si \(a=-1\) l’option
"*x"
permet d’afficherx
plutôt que-1x
. - Si \(b=0\), l’option
"*so"
n’affiche pas l’ordonnée à l’origine. - Si \(b=-2\), le signe négatif est correctement affiché.
Des exemples d’utilisation de cette fonction sont fournis avec sa documentation
.
2.8.3. Arrondis étranges¶
Un autre problème pouvant survenir avec l’affichage des nombres est la présence d’arrondis pour le moins étranges. Par exemple, en Python, l’expression 1.1 + 2.2
produit 3.3000000000000003
. C’est normal, car les nombres étant stockés en binaire, les nombres 1,1 et 2,2, bien qu’étant des nombres « ronds » en décimal, ne le sont pas en binaire, et les approximations utilisées produisent ce résultat.
Pour éviter ce genre d’arrondi, il est possible d’utiliser le type decimal.Decimal
pour les nombres à virgule. Cette classe est conçue comme étant une représentation « pour les humains » d’un nombre à virgule (contrairement au type float
, qui est une représentation « pour les ordinateurs »), et arrondi correctement les calculs. Nous n’en avons a priori pas besoin ici.
2.8.4. Bilan¶
Nous obtenons cet exercice
. Il est quasiment terminé, non ? Non ! Car voici… (musique terrifiante) les cas particuliers…
2.9. Gestion des cas particuliers¶
Deux problèmes subsistent.
Dans certains cas (par exemple
pyromaths generate EquationPremierDegre4:15
, les deux coefficients \(a\) et \(c\) de l’équation \(ax+b=cx+d\) sont égaux, et notre programme, qui suppose qu’il existe une solution unique, essaye de la calculer, et divise par 0.Lorsque nous arrivons (par exemple) à l’équation
2x=6
, l’étape suivante est de diviser les deux membres par deux. Mais cette étape est inutile lorsque, par hasard,x
est multiplié par 1, comme dans l’exemple suivant.
Il y a trois manières de résoudre ces problèmes. Elles ne sont pas exclusives, et il en existe d’autres.
2.9.1. Prise en compte avec Python¶
Dans ce cas-là, la résolution se fait en Python. Le code \(LaTeX\) est donc réduit au minimum.
1 2 3 4 5 6 7 | \exercice*
\begin{align*}
(( calculs|join("\n") ))
\end{align*}
(( conclusion ))
|
C’est en Python, en revanche, que tous les cas particuliers sont traités. Nous avons donc ajouté deux variables au contexte : calculs
contenant la liste des étapes de calcul, et conclusion
contenant la phrase de conclusion.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | class EquationPremierDegre61(Jinja2Exercise):
"""Résolution d'équations du premier degré à coefficients entiers."""
tags = ['Troisième', 'Équation', 'Premier degré']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
a = random.choice([1, -1]) * random.randint(2, 9)
b = random.choice([1, -1]) * random.randint(2, 9)
c = random.choice([1, -1]) * random.randint(2, 9)
d = random.choice([1, -1]) * random.randint(2, 9)
calculs = [
r"{a}x{b:+d} &= {c}x{d:+d}\\".format(a=a, b=b, c=c, d=d),
r"{a}x{c:+d}x &= {d}{b:+d}\\".format(a=a, b=-b, c=-c, d=d),
]
if a-c == 0:
calculs.append(r"0 &= {}\\".format(d-b))
if d == b:
conclusion = u"Puisque $0=0$ est toujours vrai, alors l'équation a une infinité de solutions : tous les nombres réels sont des solutions."
else:
conclusion = u"Puisque $0={}$ est toujours faux, alors l'équation n'a aucune solution.".format(d-b)
elif a-c == -1:
calculs.append(r"-x &= {}\\".format(d-b))
calculs.append(r"(-1)\times -x&= (-1)\times {}\\".format(d-b))
calculs.append(r"x&={}\\".format(b-d))
conclusion = r"L'unique solution est $x = {}$.".format(b-d)
elif a-c == 1:
calculs.append(r"x &= {}\\".format(d-b))
conclusion = r"L'unique solution est $x = {}$.".format(d-b)
else:
calculs.append(r"{}x &= {}\\".format(a-c, d-b))
calculs.append(r"x&=\frac{{ {} }}{{ {} }}\\".format(d-b, a-c))
solution = facteur(float(d-b)/float(a-c), "2")
if 100*(float(d-b)/float(a-c)) == int(100 * float(d-b)/float(a-c)):
calculs.append(r"x&={}\\".format(solution))
conclusion = r"L'unique solution est $x = {}$.".format(solution)
else:
calculs.append(r"x&\simeq{}\\".format(solution))
conclusion = r"L'unique solution est $x \simeq {}$.".format(solution)
self.context = {
"a": a,
"b": b,
"c": c,
"d": d,
"calculs": calculs,
"conclusion": conclusion,
}
|
Le code est plus complet, mais plus difficile à lire.
2.9.2. Prise en compte avec Jinja2¶
Puisque les cas particuliers sont traités avec Jinja2, le code Python est réduit au minimum (il n’a pas été modifié depuis la version précédente).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class EquationPremierDegre62(Jinja2Exercise):
"""Résolution d'équations du premier degré à coefficients entiers."""
tags = ['Troisième', 'Équation', 'Premier degré']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.context = {
"a": random.choice([1, -1]) * random.randint(2, 9),
"b": random.choice([1, -1]) * random.randint(2, 9),
"c": random.choice([1, -1]) * random.randint(2, 9),
"d": random.choice([1, -1]) * random.randint(2, 9),
}
@property
def environment(self):
environment = super().environment
environment.filters.update({
'facteur': facteur,
})
return environment
|
Le code \(LaTeX\), en revanche, est plus fourni.
\exercice*
\begin{align*}
(( a )) x (( b|facteur("so") )) &= (( c )) x (( d|facteur("so") )) \\
(( a )) x (( -c|facteur("so") ))x &= (( d )) (( -b|facteur("so") )) \\
(* if a == c *)
0 &= (( d - b )) \\
(* else *)
(* if a - c == 1 *)
% Rien
(* elif a - c == -1 *)
-x &= (( d - b )) \\
-1 \times -x &= -1 \times (( d - b )) \\
(* else *)
(( a - c )) x &= (( d - b )) \\
x &= \frac{(( d - b ))}{(( a - c ))} \\
(* endif *)
x &
(* if 100*(d-b)/(a-c) == (100*(d-b)/(a-c))|int *)
=
(* else *)
\simeq
(* endif *)
(( ((d-b)/(a-c)) | facteur("2") ))
(* endif *)
\end{align*}
(* if a == c and b == d *)
Puisque $0=0$ est toujours vrai, l'équation a une infinité de solutions : tous les nombres réels.
(* elif a == c and b != d *)
Puisque $0=((d - b))$ est toujours faux, l'équation n'a pas de solutions.
(* else *)
L'unique solution est
$x
(* if 100*(d-b)/(a-c) == (100*(d-b)/(a-c))|int *)
=
(* else *)
\simeq
(* endif *)
(( ((d-b)/(a-c)) | facteur("2") ))$.
(* endif *)
Encore une fois, le code est plus complet, mais plus difficile à lire.
2.9.3. Suppression des cas particuliers¶
La méthode la plus confortable dans les cas simples est d’exclure les cas particuliers. Pour cela, au lieu d’accepter n’importe quel tirage de nos coefficients, s’ils ne nous conviennent pas, nous recommençons.
Avec cette méthode, pas besoin de toucher aux templates : nous modifions simplement le constructeur de la classe EquationPremierDegre63
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
while True:
a = random.choice([1, -1]) * random.randint(2, 9)
b = random.choice([1, -1]) * random.randint(2, 9)
c = random.choice([1, -1]) * random.randint(2, 9)
d = random.choice([1, -1]) * random.randint(2, 9)
if abs(a-c) == 1:
# 1x ou -1x sera affiché à un moment ou à un autre. Nous excluons ce cas.
continue
if a == c:
# Aucune solution, ou une infinité de solutions. Nous excluons ce cas.
continue
break
self.context = {
"a": a,
"b": b,
"c": c,
"d": d,
}
|
2.9.4. Bilan¶
Supprimer les cas particuliers est sans doute le plus confortable pour écrire un exercice. Mais c’est aussi moins riche pour les élèves.
Il n’y a pas de meilleure solution ici ; faites ce qui vous paraît le moins pire.
2.10. Finalisation¶
L’exercice est bientôt prêt à être publié.
2.10.1. Créer les vignettes¶
Si vous lancez la version graphique de Pyromaths, vous remarquez que l’aperçu de votre exercice n’est pas disponible ; il manquera aussi sur la version en ligne. C’est normal : il n’a pas encore été généré. Corrigez cela avec la commande suivante.
$ utils/creer-vignettes.py
Cette commande détecte les vignettes manquantes (ou celles pour lesquelles l’exercice a été modifié), et les génère.
2.10.2. Ajouter des tests¶
Dans quelques mois ou années, quelqu’un (peut-être vous) voudrait modifier quelque chose de Pyromaths, en se demandant si cela va « casser » un exercice. Pour être sûr que votre exercice soit préservé, il serait sage de le tester. L’ajout d’un test se fait avec la commande suivante.
$ pyromaths test create EquationPremierDegre
Cette commande va générer un exercice, l’afficher (dans un lecteur de pdf externe), et vous demander confirmation. Si l’exercice est correct, validez, et cet exercice sera ajouté aux tests.
Si vous voulez ajouter un exercice particulier (car vous savez qu’il correspond à un cas très particulier), ajouter ce numéro d’exercice à votre commande (1729 ici).
$ pyromaths test create EquationPremierDegre:1729
Plus tard, pour vérifier que votre exercice n’a pas été modifier, vérifiez les tests en utilisant ce même programme.
$ pyromaths test check
2.11. Publication !¶
2.11.1. Ajout des fichiers créés ou modifiés¶
Utilisez git add
pour ajouter les fichiers créés ou modifiés. À priori, cela concerne au moins :
- un fichier python contenant la classe de votre exercice (dans un des dossiers
pyromaths/ex/*
) ; - deux fichiers de template \(LaTeX\) (dans le dossier
pyromaths/data/exercices/templates
) ; - la vignette, et le fichier
md5sum.json
(dans le dossierpyromaths/data/exercices/img
) ; - les fichiers de test (dans le dossier
pyromaths/data/exercices/tests
) ; - et peut-être d’autre, selon votre travail.
2.12. Conclusion¶
Merci de contribuer à Pyromaths !