summaryrefslogtreecommitdiff
path: root/info/expose-expl3-dunkerque-2019/dk19ecrireprem.tex
blob: cff913ae230de9778a1d10aa28f6494c630d0948 (plain)
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
% Time-stamp: <2019-09-24 10:47:32 administrateur>
% Création : 2019-08-24T14:29:55+0200
\section{Rappel}
\label{sec:rappel}

Je commence par redonner le code de Donald E. \textsc{Knuth} mais je
laisse le lecteur rechercher l'article précité de Denis \textsc{Roegel}
pour y trouver toutes les explications nécessaires.

\VerbatimInput[frame=lines, framesep=0.75\baselineskip, 
label={\textsc{Knuth} \textemdash{} \emph{définitions}},
labelposition=topline]{./codes/knuth-def.tex}

L'appel |\primes{15}| permet d'obtenir
\og \primes{15}\fg.

\section{Version avec \Expliii}
\label{sec:versionexpl3}


Commençons par quelques remarques: comme le code est écrit dans un
fichier \texttt{.tex} \TO et non dans un \gls{module} \TF je dois
commencer par enclencher la syntaxe propre à \Expliii{}, ce que je
fais dès la première ligne qui n'est pas un commentaire à usage
interne \TO d'où le décalage de numéro \TF dans le premier fragment
ci-dessous:
%
\Fragment{3}{5}{./codes/henel-def}

\subsection{Une macro privée pour écrire les nombres}
\label{sec:macro+ecrire+entier}

Sur la ligne~\(5\), on lit la définition d'une macro \emph{interne}
\CAD d'une macro qui n'a pas vocation à être appelée directement dans
un document rédigé par l'utilisateur final. On y voit déjà quelques
différences avec du code \LaTeXe{} que je vais préciser maintenant:
\begin{itemize}
\item les caractères |_| et~|:| sont promus au rang de lettres mais
  par l'arobase~|@| cher aux programmeurs en~\LaTeXe{};
  
\item les espaces du code ne donneront pas d'espace dans le
  document. Voilà qui soulage grandement le programmeur puisque
  disparait la phase de recherche des \TdSTrad{blancs
    inopportuns}{spurious blanks}.
\end{itemize}

Autre remarque, d'ordre typographique, la présentation que j'adopte
dans ce document \TO pour des raisons de place\TF n'est pas la
présentation recommandée par l'équipe du projet \LaTeX3. On devrait
plutôt, en effet, adopter la présentation qui suit\index{présentation
  du code}:
%
\begin{Verbatim}[frame=lines, framesep=0.75\baselineskip]
\cs_new:Nn \__yh_ecrire_nombre:n 
  { 
    \(\np{\int_to_arabic:n{ #1 }}\) 
  }
\end{Verbatim}
%
dont on m'accordera, j'espère, qu'elle occupe plus d'espace.

La commande que je définis s'appelle
\Macro{__yh_ecrire_nombre:n}. Ce nom comporte plusieurs parties:
\begin{enumerate}
  
\item les \og |__|\fg initiaux signalent que cette commande est
  \emph{privée} \CAD que son usage est réservée au \gls{module} \TO
  ici le simple fichier contenant le code\TF et n'est pas
  nécessairement documentée. Le programmeur ne s'engage à rien sur
  cette commande et un utilisateur du \gls{module} ne doit pas
  s'attendre à ce que le comportement de cette commande reste stable
  au fil des versions ni même à ce qu'elle soit toujours
  disponible. Il ne s'agit ici que d'une \textsb{convention} car
  \TeX{} est resté ce qu'il est et la gestion des espaces de noms
  n'est pas implémentée\footnote{L'équipe des développeurs de
    \Expliii{} nous dit, dans \autocite[p.~1]{expl3progr} que celà
    pourrait se faire mais avec un surcoût prohibitif.};
  
\item \og |yh|\fg remplace ici ce qui est \TO dans un \gls{module}\TF
  le nom du \gls{module} ou, à tout le moins, une chaine de caractères
  réservée pour ce \gls{module};
  
\item vient le nom explicite de la commande: \og |ecrire_nombre|\fg
  dans lequel le souligné~\og |_|\fg sert de séparateur de mots;
  
\item suivi du caractère~\og |:|\fg qui introduit la \gls{signature}
  de la commande qui est, ici, \og |n|\fg. Cela nous indique que la
  commande est une \gls{fonction} \TO c'est le vocabulaire défini par le
  projet \LaTeX3\TF qui attend un seul argument fourni entre
  accolades.
\end{enumerate}

La fonction \Macro{cs_new:Nn} utilise la \gls{signature} de la
commande qui suit pour déterminer le nombre et le type des arguments
que demande cette commande. Elle vérifie que la commande n'est pas
déjà définie et produit une erreur si c'est le cas. De plus, elle
définit la commande de manière globale \TO penser à \Macro{gdef} avec
les pouvoirs de \Macro{newcommand}\TF.

Elle possède plusieurs variantes dont, \PX{}, \Macro{cs_new:cn} qui
attend comme premier argument le \emph{nom} de la commande plutôt que
la commande elle-même. J'aurais obtenu le même résultat avec:
%
\begin{Verbatim}[frame=lines, framesep=0.75\baselineskip]
\cs_new:cn { __yh_ecrire_nombre:n } { \(\np{\int_to_arabic:n{ #1 }}\) }
\end{Verbatim}
%
code que je livre compacté.

Le 2\ieme argument de \Macro{cs_new:Nn} doit être fourni entre
accolades, c'est la définition de la commande. Ici j'utilise
\Macro{np} de l'extension \Pkg{numprint} en mode mathématique en
ligne \TO à l'aide du bien connu couple |\(| et~|\)| \TF  afin
d'obtenir \og \(\np{3257}\)\fg au lieu de \og \(3257\)\fg.

La \gls{fonction} \Macro{int_to_arabic:n} appartient au \gls{module}
\Mdl{int}, partie du noyau de \LaTeX3, qui regroupe les commandes
traitant les \glspl{entier}. Elle prend un argument dont elle fournit
la représentation en chiffres \emph{arabes}.

\subsection{Commande vérifiant qu'un entier impair est premier}
\label{sec:macro+impair+premier}

La commande \Macro{yh_impair_est_premier:nT} ne se déclare pas comme
privée, dans un \gls{module} elle devrait avoir une interface et une
sortie stable et être documentée car son nom ne commence pas par~|__|
mais ça aurait pu être le cas.

\(2\) étant le seul nombre pair premier, la question de la primarité
ne se pose vraiment que pour les entiers impairs. Je pourrai donc
n'essayer que des diviseurs impairs.

La \gls{signature} de cette \gls{fonction}, \CAD |nT|, nous indique
qu'elle attend deux arguments entre accolades, que le 1\ier sera un
test qui renverra soit~\VRAI{} soit~\FAUX{} et que, \emph{dans le seul
  cas} où le test est~\VRAI{}, le code contenu dans la 2\ieme paire
d'accolades sera considéré.

Regardons le code:
%
\Fragment{7}{25}{./codes/henel-def}

Dans la partie d'initialisation \TO lignes~\(8\) à~\(10\)\TF, je crée
deux \glspl{booleen} \TO \Macro{yh_continue_bool} et
\Macro{yh_est_premier_bool}\TF à l'aide de la \gls{fonction}
\Macro{bool_set_true:N} qui, dans le même temps, leur donne la
valeur~\VRAI.

Les \glspl{fonction} dont le nom contient |set| définissent les
commandes sans vérification et, à moins que le nom contienne |gset|
\TO |g| comme \og global\fg\TF, elles créent ces commandes
\emph{localement} \CAD{} dans le groupe où a lieu la définition.

Ici, contrairement à ma devise\footnote{À savoir: \og ceinture et
  bretelles\fg.}, je coure le risque que ces deux \glspl{booleen}
existent déjà, risque dont je pense qu'il est faible. On verra plus
bas ce que j'aurais pu faire pour sécuriser la définition.

Ces deux \glspl{booleen} ne sont pas des \glspl{fonction} mais des
\glspl{variable} \CAD{} des macros \TO au sens de \TeX{}\TF dont le
seul objet est de contenir une valeur et non pas de \emph{faire}
quelque chose. C'est pour cela que leurs noms n'ont pas de
\gls{signature} et ne se termine pas par~|:| qui signalerait une
\gls{fonction} sans argument.

Dernière étape de l'initialisation, avec \Macro{int_set:Nn} je donne
à la variable entière \textbf{l}ocale temporaire \Macro{l_tmpa_int}
la valeur~\(1\). Plusieurs \glspl{module} du noyau de \LaTeX3
fournissent deux variables locales dont les noms comportent |tmpa|
et~|tmpb| respectivement et deux variables \textbf{g}lobales \TO avec
les noms équivalents\TF.

Suivant la convention générale qui veut que le nom d'une
\gls{variable} se termine par son type \TO c'est le cas des deux
\glspl{booleen} que j'ai définis\TF, les variables temporaires
fournies par un \gls{module} du noyau ont une finale qui donne le type
comme \Macro{l_tmpa_int}. Cependant, contrairement à cette même
convention de nommage, les variables temporaires n'ont pas, en tête de
nom, le nom du module. On aura voulu éviter quelque chose comme
\Macro*{int_l_tmpa_int} dont on peut penser que c'est légèrement
redondant.

En ligne~\(12\), je commence une boucle \TANTQUE avec
\Macro{bool_do_while:Nn} qui attend un premier argument constitué d'un
seul lexème \TO ici \Macro{yh_continue_bool} qui est~\VRAI à l'entrée
dans la boucle\TF et un deuxième argument entre accolades: le corps de
la boucle.

Le corps de cette boucle commence avec l'accolade ouvrante du bout de
la ligne~\(12\) et s'achève avec la 2\ieme accolade fermante de la
ligne~\(24\).

Je commence par ajouter~\(2\) à la valeur courante de
\Macro{l_tmpa_int} à l'aide de la \gls{fonction} \Macro{int_add:Nn};
\Macro{l_tmpa_int} est ici le candidat diviseur.

\Macro{bool_if:nTF} est une forme de~\SIALORSSINON: on a bien les deux
branches de l'alternative puisque la signature se termine en~|TF|. Il
s'agit de savoir si le candidat est bien un diviseur de l'entier
représenté par~|#2|. Pour ce faire, j'utilise la \gls{fonction}
\Macro{int_compare_p:nNn} dont le 2\ieme argument est un opérateur de
comparaison \CAD |>|, |<| ou~|=|. Le 3\ieme argument est
simplement~\(0\). Le premier, lui, est le reste de la division de~|#2|
par \Macro{l_tmpa_int} que l'on obtient à l'aide de la \gls{fonction}
\Macro{int_mod:nn}.

Si le reste est nul, on exécute le code du 2\ieme argument de
\Macro{bool_if:nTF}. On positionne les deux \glspl{booleen} à~\FAUX{}
puisque |#2|, divisible par \Macro{l_tmpa_int}, n'est pas premier et
qu'il n'est donc pas nécessaire de reprendre la boucle.

Si le reste n'est pas nul, je compare l'entier~|#2| et le carré du
candidat \TO on peut utiliser les opérateurs \emph{classiques}
d'opérations: |+|, |-|, |*|, |/| car, derrière ce code, c'est le
mécanisme de la macro \Macro{numexpr} de \hologo{eTeX} qui est à
l'œuvre\TF. Si le carré est plus grand, c'est que |#2|~est premier et
qu'il est temps de s'arrêter d'où les valeurs données aux deux
\glspl{booleen}. Le 3\ieme argument du \Macro{bool_if:nTF} s'arrête
avec la 1\iere accolade fermante de la ligne~\(24\).

À la sortie de la boucle, si |#2| n'est pas premier il n'y a rien à
faire, s'il est premier, on le retourne. Cela est accompli à l'aide
de la \gls{fonction} \Macro{bool_if:nT} qui réalise un saut
conditionnel \SIALORS.

\subsubsection{Compléments}
\label{sec:complementbouclesaut}

Outre les deux \glspl{fonction} \Macro{bool_if:nTF} et
\Macro{bool_if:nT}, \Expliii fournit également la \gls{fonction}
\Macro{bool_if:nF} qui réalise un saut conditionnel \SISINON. Dans le
manuel, on verra que la signature se termine
par~\underline{\texttt{\emph{TF}}} pour signaler la présence des trois
signatures~|T|, |F| et~|TF|.

Par ailleurs, \Expliii fournit essentiellement quatre boucles:
\begin{itemize}
\item \Macro{bool_do_while:Nn}, déjà rencontrée. C'est un \TANTQUE
  dont le corps est exécuté puis le test effectué \CAD qu'en toutes
  circonstances, le corps est exécuté au moins une fois;

\item \Macro{bool_do_until:Nn}, qui inverse le sens du test. C'est un
  \JUSQUA dont le corps est exécuté au moins une fois;\label{booldountilNn}
  
\item \Macro{bool_while_do:Nn}, \TANTQUE qui commence par le
  test. Il se peut donc que le corps ne soit pas exécuté;
  
\item \Macro{bool_until_do:Nn}, \JUSQUA commençant par le
  test.
\end{itemize}

Ces quatres \glspl{fonction} ont des variantes de signatures~|cn|
et~|nn|. Avec~|cn|, le 1\ier argument est le nom d'un \gls{booleen};
avec~|n|, c'est une expression booléenne comme \TO exemple tiré de
\autocite{l3interfaces}\TF
%
\begin{Verbatim}[frame=lines, framesep=0.75\baselineskip]
\int_compare_p:n { 1 = 1 } &&
  (
    \int_compare_p:n { 2 = 3 } ||
    \int_compare_p:n { 4 <= 4 } ||
    \str_if_eq_p:nn { abc } { def }
  ) &&
! \int_compare_p:n { 2 = 4 }
\end{Verbatim}
%
qui permet de mesurer la complexité acceptée.

\subsection{Écrire la liste des nombres premiers}
\label{sec:ecrirelistepremiers}

Avant d'aborder la commande destinée à l'utilisateur \TO dont je dirai
que c'est une \emph{commande de document}, comme le suggère le nom de
la macro qui permettra de la créer tout à l'heure. L'extension
\Pkg{xparse}~\autocite{xparse} parle de \English{document-level
    command}\TF, je présente deux nouvelles macros utilitaires \TO
qui elles aussi auraient pu être \emph{privées}\TF à savoir
\Macro{yh_ecrire_separateur:n} et \Macro{yh_ecrire_si_premier:n}.

\subsubsection{Deux macros utilitaires}

\Fragment{27}{32}{./codes/henel-def}

On voit dans le code ci-dessus, en ligne~\(27\), une pré-déclaration
de \Macro{yh_ecrire_separateur:n} qui permet juste de s'assurer que ce
nom est bien disponible.

La macro suivante \Macro{yh_ecrire_si_premier:n} fait exactement ce
que son nom suggère. 

Passons maintenant à la commande principale.

\subsubsection{La commande de document}
\label{sec:commandededocument}

\Fragment{34}{59}{./codes/henel-def}

Le code permettant de définir cette commande utilise l'extension
\Pkg{xparse}~\autocite{xparse} et sa macro \Macro{NewDocumentCommand}.

La syntaxe de déclaration d'argument change quelque peu! Le |m|,
2\ieme argument de la macro, indique que l'on définit une commande
requérant un unique argument obligatoire fourni entre accolades \TO
|m| pour \English{mandatory} \CAD \og obligatoire\fg\TF.
% Le 1\ier argument est attendu \textsb{entre accolades} \TO pas de
% fantaisie à la \LaTeXe{}, du genre |\new|\0|com|\0|mand|
% |\truc|\dots{}, ici\TF. FAUX!
Le 1\ier argument est, comme on s'y attend, le nom de la commande
créée.

Le corps de la définition commence en ligne~\(35\) en ouvrant un
groupe avec \Macro{group_begin:}  et finit en ligne~\(59\) en fermant
le groupe avec \Macro{group_end:}.

\paragraph{Initialisation}

Dans le groupe, je crée en lignes~\(37\) et~\(38\) deux \glspl{entier}
locaux \TO d'où le |l| initial\TF mais qui seront créés
\emph{globalement}, car il n'y a pas moyen de faire autrement. Leur
caractère local est donc conventionnel mais signale que l'on ne doit
pas s'attendre à ce qu'ils aient une valeur pertinente à l'extérieur
du groupe.

Pour respecter ce caractère local, je donne à ces \glspl{entier}, une
valeur avec \Macro{int_set:Nn} et pas avec \Macro{int_gset:Nn} qui, de
fait, n'est qu'un alias de la première, à moins que ce soit
l'inverse\footnote{Je ne suis pas allé vérifier dans le
  source~\autocite{source3}.}.

Afin que, en sortie de commande, ces deux \glspl{entier} ne soient
plus accessibles \TO \textsl{Vous êtes priés de laisser ces lieux dans l'état
où vous auriez aimé les trouver en entrant!} \TF, je les annihile en
lignes~\(57\) et~\(58\) à l'aide de \Macro{cs_undefine:N}.

Il est temps à présent de définir, localement, avec \Macro{cs_set:Nn},
la fonction \Macro{yh_ecrire_separateur:n}, ce que je fais en
ligne~\(41\) et~\(42\). Comme la définition est faite dans une
définition, on doit, bien entendu, recourir au doublement des
\emph{dièses} d'où le |##1| dans le code de cette fonction.

Dans les 1\ier et 3\ieme arguments de \Macro{int_compare:nNnTF} on
peut écrire \emph{naturellement} des opérations sur les entiers. Je ne
m'en prive pas.

Les tildes~|~| que l'on voit apparaitre en ligne~\(42\) permettent de
coder un espace qui apparaitra dans le document final. Par contre, il
faut utiliser autre chose pour coder un espace insécable \TO la
solution est la macro \Macro{nobreakspace}\TF.

Je suis obligé de regarder si le séparateur est le dernier car, en
français\footnote{Encore que, si j'ai bien compris un article récent
  paru dans la presse britanique, même à Oxford on a fini par
  renoncer à la virgule devant le \emph{and} précédant le dernier mot
  d'une énumération. \textsl{Tout fout l'camp, ma bonne dame!}} il ne
doit pas y avoir de virgule devant le \emph{et}.

Comme l'annonce la ligne~\(43\) je ne calcule pas les deux premiers
nombres premiers \TO Donald \textsc{Knuth} ne le faisait pas\TF et je
ne vérifie pas que l'utilisateur demande au moins trois nombres \TO
même remarque\TF; pour faire comme dans les livres sérieux, je laisse
cette amélioration comme exercice pour le lecteur.

Je prépare maintenant la boucle en initialisant, localement, le
\emph{candidat premier} à~\(3\). On a déjà vu \Macro{int_set:Nn}.
Et je rentre dans la boucle en ligne~\(50\) pour en sortir en
ligne~\(55\). La seule nouveauté de ce fragment de code est l'emploi
de \Macro{int_incr:N} qui permet d'incrémenter localement le compteur
\Macro{l_compteur_int}.

J'ai déjà expliqué ci-dessus l'utilité des lignes~\(57\) à~\(59\).

\subsection{Utilisation de la commande de document}
\label{sec:utilisation}

Il me reste à utiliser cette commande dans ce document:
|\ListePremiers{15}| produit \og \ListePremiers{15}\fg.

Au passage, on notera avec satisfaction que |\primes| et
|\ListePremiers| sont d'accord quant aux quinze premiers nombres
premiers. C'est rassurant.

\subsection{Quelques commentaires subjectifs}
\label{sec:commentairessubjectifs}

J'accorde sans peine qu'avec \Expliii le code est plus verbeux qu'avec
\TeX{} tout seul. Je ne peux, cependant, m'empêcher de penser qu'il
est plus clair. Pour ma part, le gain énorme que je vois en passant
cette fois de \LaTeXe{} à \Expliii{} c'est le systématisme du nommage
des macros, \glspl{fonction} ou \glspl{variable}. L'équipe du projet
\LaTeX3 en fait d'ailleurs, elle-même, un ``\emph{argument de
  vente}''.

La puissance de \Macro{NewDocumentCommand} \TO que l'on a à peine
effleuré ci-dessus\TF, à elle seule, peut inciter à passer à
\Expliii{} pour créer ses propres macros.

%%% Local Variables:
%%% mode: latex
%%% TeX-master: "dun19expl3"
%%% End: