%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% Codes donnés dans le livre %% %% « Apprendre à programmer en TeX » %% %% %% %% Encodage ISO 8859-1 %% %% _____ %% %% %% %% © 2014-2020 Christian Tellechea %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Les codes et les macros commentés donnés dans ce fichier sont diffusés sous % la licence LaTeX project public license (LPPL) 1.2 % % https://www.latex-project.org/lppl/lppl-1-2/ % https://www.latex-project.org/lppl/lppl-1-2.txt % % Attention : ce fichier n'a pas vocation à être compilé \endinput ****************** Code 1 ****************** Voici le code en \TeX{} du premier exemple.% ceci est un commentaire On peut observer l'affichage qu'il produit juste au-dessous ! ****************** Fin code ****************** ****************** Code 2 ****************** 3 tailles : $% entre en mode mathématique (espaces ignorés) 1^{2^3}% 2 est en taille "\scriptstyle" et 3 en taille "\scriptscriptstyle" $% fin du mode math normal $1$, petit $\scriptstyle 2$, très petit $\scriptscriptstyle 3$. ****************** Fin code ****************** ****************** Code 3 ****************** %%% comportement normal %%% a) comportement normal : Ligne 1 Ligne 2 Ligne 3 \medbreak b) Aucun caractère de fin de ligne : %%% aucune insertion en fin de ligne %%% \endlinechar=-1 Ligne 1 Ligne 2 Ligne 3\endlinechar13 \medbreak c) "X" comme caractère de fin de ligne : %%% "X" est inséré à chaque fin de ligne \endlinechar=88 Ligne 1 Ligne 2 Ligne 3 ****************** Fin code ****************** ****************** Code 4 ****************** a) \number"1A \qquad b) \number"AB3FE \qquad c) \number"78 ****************** Fin code ****************** ****************** Code 5 ****************** a) \number'15 \qquad b) \number'674 \qquad c) \number'46 ****************** Fin code ****************** ****************** Code 6 ****************** a) \number`\a \quad %code de caractère de "a" b) \number`\\ \quad % code de caractère de "\" c) \number`\$ \quad % code de caractère de "$" d) \number`\ \quad % code de caractère de l'espace e) \number`\5 \quad % code de caractère de "5" f) \number`\{ \quad % code de caractère de l'accolade ouvrante g) \number`\} % code de caractère de accolade fermante ****************** Fin code ****************** ****************** Code 7 ****************** a) \number\catcode`\a \quad %code de catégorie de "a" b) \number\catcode`\\ \quad % code de catégorie de "\" c) \number\catcode`\$ \quad % code de catégorie de "$" d) \number\catcode`\ \quad % code de catégorie de l'espace e) \number\catcode`\5 \quad % code de catégorie de "5" f) \number\catcode`\{ \quad % code de catégorie de l'accolade ouvrante g) \number\catcode`\} % code de catégorie de accolade fermante ****************** Fin code ****************** ****************** Code 8 ****************** Ici, W est une lettre...\par \catcode`\W=3 % W devient le signe de bascule en mode math Wx+y=3W\par $a+b=cW\par W2^3=8$\par \catcode`\W=11 % W redevient une lettre De nouveau, W est une lettre... ****************** Fin code ****************** ****************** Code 9 ****************** $3+4=7$ \par % $ est la bascule math \catcode`\$=12 % $ devient un caractère affichable $ s'affiche sans problème : $\par \catcode`\$=3 % $ redevient la bascule math $4+3=7$ ****************** Fin code ****************** ****************** Code 10 ****************** Voici les premiers mots de chaque ligne : \catcode`\ =5 % l'espace est déormais de catcode 5 Cette première ligne sera tronquée... La deuxième aussi ! Et la dernière également. \catcode`\ =10 % l'espace reprend son catcode Le comportement normal est restauré. ****************** Fin code ****************** ****************** Code 11 ****************** \def\foo{Bonjour}% définit le texte de remplacement de \foo a) \foo Alice.\qquad% espace ignoré b) {\foo} Bob.\qquad% espace non ignoré c) \foo{} Chris.\qquad% espace non ignoré d) \foo\space Daniel.% \space est remplacé par un espace ****************** Fin code ****************** ****************** Code 12 ****************** \def\startbold{\begingroup \bf} \def\stopbold{\endgroup} Voici \startbold du texte en gras\stopbold{} et la suite. ****************** Fin code ****************** ****************** Code 13 ****************** \def\foo{foo} \begingroup A\aftergroup\foo B\endgroup\par {A\aftergroup X\aftergroup\foo B} ****************** Fin code ****************** ****************** Code 14 ****************** 1) (un {\it cheval})\par% sans correction d'italique 2) (un {\it\aftergroup\/cheval})\par% avec correction d'italique % on peut définir une macro \itacorr qui effectue automatiquement la correction \def\itacorr{\it\aftergroup\/} 3) (un {\itacorr cheval})% avec correction d'italique ****************** Fin code ****************** ****************** Code 15 ****************** \def\bar{Je suis bar.} \let\foo\bar % \foo devient égal à \bar \def\bar{ABC}% \bar est redéfinie \foo\par% affiche "Je suis bar" \bar% affiche "ABC" ****************** Fin code ****************** ****************** Code 16 ****************** Initialement, c'est \TeX.\par \let\TeXsauve\TeX% sauvegarde \def\TeX{tEx}% redéfinition Ici, on a modifié \TeX.\par \let\TeX\TeXsauve% retauration De nouveau, c'est \TeX. ****************** Fin code ****************** ****************** Code 17 ****************** \let\sptoken= %2 espaces avant le "%" La commande \sptoken compose le paragraphe ****************** Fin code ****************** ****************** Code 18 ****************** {% début du groupe \catcode`\W=13 \def W{wagons} Les W constituent le train. }% fin du groupe ****************** Fin code ****************** ****************** Code 19 ****************** \begingroup \catcode`\W=13 \gdef\actiw{% \catcode`\W=13 \def W{wagons}} \endgroup a) Les trains ne sont pas constitués de W !\par b) \begingroup\actiw Ici, les W forment les trains.\endgroup\par c) Ici, les W sont redevenus des lettres. ****************** Fin code ****************** ****************** Code 20 ****************** \begingroup \catcode`\ =13 % rend l'espace actif \gdef\>{\begingroup \catcode`\ =13 \def {\hskip5mm\relax}} \endgroup \let\<=\endgroup a) Du texte normal\par b) \>Des mots très espacés\<\par c) Du texte normal ****************** Fin code ****************** ****************** Code 21 ****************** \begingroup \catcode`\,=13 \def,{\unskip\string,\space\ignorespaces} La rue assourdissante autour de moi hurlait. Longue , mince,en grand deuil , douleur majestueuse , Une femme passa ,d'une main fastueuse Soulevant,balançant le feston et l'ourlet ; \endgroup\medbreak\hfill Charles {\sc Baudelaire} ****************** Fin code ****************** ****************** Code 22 ****************** \begingroup \catcode`\,=13 \def,{\unskip\string, \ignorespaces} \catcode`\^^M=13 \let ^^M\par % rend le retour charriot égal à \par La rue assourdissante autour de moi hurlait. Longue , mince,en grand deuil , douleur majestueuse , Une femme passa , d'une main fastueuse Soulevant,balançant le feston et l'ourlet ; \endgroup\medbreak\hfill Charles {\sc Baudelaire} ****************** Fin code ****************** ****************** Code 23 ****************** {% ouvre un groupe \let\AA=a \let\EE=e \let\II=i \let\OO=o \let\UU=u \let\YY=y % sauvegarder avant de modifier le catcode de a et e : \let\lEt=\let \let\cAtcOdE=\catcode \cAtcOdE`\a=13 \lEt a=\EE \cAtcOdE`\e=13 \lEt e=\II \cAtcOdE`\i=13 \lEt i=\OO \cAtcOdE`\o=13 \lEt o=\UU \cAtcOdE`\u=13 \lEt u=\YY \cAtcOdE`\y=13 \lEt y=\AA Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es catalanes, corses ou grecques assez inattendues. }% ferme le groupe ****************** Fin code ****************** ****************** Code 24 ****************** {% \let\AA=a \let\lEt=\let \let~=\catcode ~`a=13 \lEt a=e ~`e=13 \lEt e=i ~`i=13 \lEt i=o ~`o=13 \lEt o=u ~`u=13 \lEt u=y ~`y=13 \lEt y=\AA Ce texte devenu \`a peine reconnaissable... } ****************** Fin code ****************** ****************** Code 25 ****************** a) \def\foo{Bonjour}\meaning\foo\par b) \let\bar=\foo\meaning\bar\par% \foo est "copiée" vers \bar c) \def\baz{\foo}\meaning\baz\par d) \catcode`\W=13 \def W{Wagons}% W est un caractère actif \meaning W\par e) \meaning\def% \def est une primitive ****************** Fin code ****************** ****************** Code 26 ****************** a) \def\foo{ }Signification de \string\foo{} : \meaning\foo b) Signification de deux retours charriots consécutifs : \meaning ****************** Fin code ****************** ****************** Code 27 ****************** a) \meaning\relax\par% primitive b) \meaning {\par% catcode 1 c) \meaning }\par% catcode 2 d) \meaning $\par% catcode 3 e) \meaning &\par% catcode 4 g) \meaning #\par% catcode 6 h) \meaning ^\par% catcode 7 i) \meaning _\par% catcode 8 j) \meaning a\par% catcode 11 (une lettre) k) \meaning +\par% catcode 12 (caractère "autre") l) \meaning ~\par% catcode 13 (caractère actif) ****************** Fin code ****************** ****************** Code 28 ****************** \begingroup% dans un groupe \let\*=\meaning% rendre \* égal à \meaning \* %<- espace pris en compte \endgroup% fermer le groupe ****************** Fin code ****************** ****************** Code 29 ****************** \begingroup \catcode`\*0 \catcode`\\12 % \ n'est plus un caractère d'échappement *def*foo{bar} *LaTeX{} et la macro *string*foo : *foo. L'ancien caractère d'échappement "\" et \TeX. *endgroup ****************** Fin code ****************** ****************** Code 30 ****************** \def\foo{\bar} \begingroup a) \escapechar=-1 \meaning\foo\qquad% pas de caractère d'échappement b) \escapechar=`\@ \meaning\foo\qquad% "@" est le caractère d'échappement \endgroup c) \meaning\foo% caractère d'échappement par défaut ****************** Fin code ****************** ****************** Code 31 ****************** \begingroup \catcode`\|=0 |catcode`|\=11 |catcode`|2=11 |catcode`|1=11 |gdef|1\2\a{foo} |endgroup Voici la macro : \csname 1\string\2\string\a\endcsname ****************** Fin code ****************** ****************** Code 32 ****************** \newtoks\foo% allocation d'un nouveau registre \foo={Bonjour le monde}% assignation Contenu du registre {\tt\string\foo} : \the\foo. \newtoks\bar% allocation d'un autre registre \bar=\foo% assigne à \bar les tokens du registre \foo Contenu du registre {\tt\string\bar} : \the\bar. ****************** Fin code ****************** ****************** Code 33 ****************** \def\hello#1#2{Bonjour #1 et #2.\par}% définition \hello ab% #1=a et #2=b \hello a b% #1=a et #2=b \hello{foo}{bar}% #1=foo et #2=bar \hello foobar% #1=f et #2=o % (le reste "obar" est lu après que la macro est terminée) ****************** Fin code ****************** ****************** Code 34 ****************** \def\tenlist#1#2#3#4#5#6#7#8#9{(#1,#2,#3,#4,#5,#6,#7,#8,#9\endlist} \def\endlist#1{,#1)} Une liste \tenlist abcdefghij de lettres. ****************** Fin code ****************** ****************** Code 35 ****************** \def\foo#1#2{Bonjour #1 et #2.\par} \begingroup\tt% passer en fonte à chasse fixe a) \foo{monsieur}{madame} b) \foo{{\bf toi}}{moi} c) \foo{}{{}} d) \foo{ }{ } e) \foo{$\pi$} {$\delta$} f) \foo ab g) \foo X Y \endgroup% fin de la fonte à chasse fixe ****************** Fin code ****************** ****************** Code 36 ****************** \def\foo{Programmer en \TeX {} est facile} \meaning\foo\par \def\foo{Program^^6der en \TeX{} est facile} \meaning\foo ****************** Fin code ****************** ****************** Code 37 ****************** a) \gobone XY - \secondoftwo XY\par b) \gobone{ab}{xy} - \secondoftwo{ab}{xy}\par c) \gobone{123}4 - \secondoftwo{123}4\par d) \gobone A{BCD} - \secondoftwo A{BCD} ****************** Fin code ****************** ****************** Code 38 ****************** \gobone {a}{\catcode`\~12 Le <<~>> est un espace ?} Et ici <<~>> ? \secondoftwo{a}{\catcode`\~12 Le <<~>> est un espace ?} Et ici <<~>> ? ****************** Fin code ****************** ****************** Code 39 ****************** \def\visible{\let\choix=\firstoftwo} \def\cache{\let\choix=\secondoftwo} \def\?#1{\choix{#1}{...}}% \def\interro{J'ai ét\?{é} invit\?{é} à gout\?{er} chez Max. Après s'être bien amus\?{é}, nous avons rang\?{é} et il a fallu rentr\?{er}.} Compléter avec << é >> ou << er >> :\par \cache\interro\par\medskip Correction :\par \visible\interro ****************** Fin code ****************** ****************** Code 40 ****************** \def\visible{\let\?=\identity} \def\cache{\def\?##1{...}} \def\interro{J'ai ét\?{é} invit\?{é} à goût\?{er} chez Max. On s'est bien amus\?{é} et ensuite, il a fallu rentr\?{er}.} Compléter avec << é >> ou << er >> :\par \cache\interro\par\medskip Correction :\par \visible\interro ****************** Fin code ****************** ****************** Code 41 ****************** \def\makemacro#1#2{\def#1##1{##1 #2}} \makemacro\LL{Lamport} \makemacro\juin{juin 2014} Paris, le \juin{3}.\medbreak \LL{Cher Monsieur},\smallbreak Vous êtes convoqué à un examen sur \TeX{} le \juin{21} à 9h00 précise dans le bureau de D. Knuth.\smallbreak Veuillez croire, \LL{Monsieur}, en mes sentiments \TeX iens. ****************** Fin code ****************** ****************** Code 42 ****************** \def\showcodes#1{\string#1 : $\number`#1 \rightarrow \number\lccode`#1$} \showcodes A\qquad \showcodes B\qquad \showcodes a\qquad \showcodes b\qquad \showcodes ^ ****************** Fin code ****************** ****************** Code 43 ****************** \lowercase{CACAO foobar}\par \lowercase{\def\foo{Bonjour}}% définit une commande \foo \foo % la commande définie précédemment ****************** Fin code ****************** ****************** Code 44 ****************** \begingroup \lccode`A=`* % change le code minuscule de A \lowercase{CACAO ABRACADABRA}\par \endgroup \lowercase{CACAO ABRACADABRA} ****************** Fin code ****************** ****************** Code 45 ****************** \def\defactive#1{% \catcode`#1=13 \begingroup \lccode`~=`#1 \lowercase{\endgroup\def~}% } \begingroup% les modifications restent locales \defactive W{wagons}% définit le caractère actif "W" Les W constituent les trains. \endgroup %fermeture du groupe, "W" redevient une lettre \par Les W sont des lettres ****************** Fin code ****************** ****************** Code 46 ****************** \defactive W{wagon} 1) Les W constituent le train.\par 2) \catcode`W=11 Les W sont des lettres.\par 3) \catcode`\W=13 Les W constituent-ils le train ? ****************** Fin code ****************** ****************** Code 47 ****************** \def\defactive#1#2{% \catcode`#1=13 % #1 sera actif après la fin du texte de remplacement \begingroup \lccode`~=`#1 % dans \lowercase, changer les ~ (actifs) en "#1", ceux-ci étant actifs \lowercase{\endgroup\def~}{#2}% } \defactive W{Wagon} Un W. ****************** Fin code ****************** ****************** Code 48 ****************** \def\defactive#1{% \catcode`#1=13 % #1 sera actif après la fin du texte de remplacement \begingroup \lccode`~=`#1 % dans \lowercase, changer les ~ (actifs) en "#1", ceux-ci étant actifs \lowercase{\endgroup\def~}% } \defactive W{Wagon} Un W. ****************** Fin code ****************** ****************** Code 49 ****************** \def\defactive#1#2{% \catcode`#1=13 % #1 sera actif après la fin du texte de remplacement \begingroup \uccode`~=`#1 % dans \uppercase, changer les ~ (actifs) en "#1", ceux-ci étant actifs \uppercase{\endgroup\def~}{#2}% } \defactive W{Wagon} Un W. ****************** Fin code ****************** ****************** Code 50 ****************** \def\letactive#1{% \catcode`#1=13 % #1 sera actif après la fin du texte de remplacement \begingroup \uccode`~=`#1 % dans \uppercase, changer les ~ (actifs) en "#1", ceux-ci étant actifs \uppercase{\endgroup\let~}% } \def\W{wagon}% \letactive X\W Un X. ****************** Fin code ****************** ****************** Code 51 ****************** \def\litterate#1{% #1=lit le token frontière choisi \begingroup% ouvre un groupe pour y faire les modifications \def\do##1{\catcode`##1=12 }% \do change le catcode de son argument à 12 \dospecials% rend inoffensifs tous les tokens spéciaux \defactive\^^M{\leavevmode\par}% définit le retour charriot \letactive#1\endgroup% définit #1 qui sera un \endgroup \tt% passe en fonte à chasse fixe } essai 1 : \litterate*\foo # #34{ } &_\ * \medbreak essai 2 : \litterate/une première ligne ,, >> un saut de ligne et la seconde --/ ****************** Fin code ****************** ****************** Code 52 ****************** \def\litterate#1{% #1=lit le token frontière choisi \begingroup% ouvre un groupe pour y faire les modifications \def\do##1{\catcode`##1=12 }% \do change le catcode de son argument à 12 \dospecials% rend inoffensifs tous les tokens spéciaux \defactive\^^M{\leavevmode\par}% définit le retour charriot \defactive\ {\ }% l'espace est actif et devient "\ " \defactive<{\string<{}}\defactive>{\string>{}}% empêche \defactive-{\string-{}}\defactive`{\string`{}}% les \defactive,{\string,{}}\defactive'{\string'{}}% ligatures \letactive#1\endgroup% #1 sera un \endgroup \tt% passe en fonte à chasse fixe } essai 1 : \litterate*\foo # #34{ %} &_\ * \medbreak essai 2 : \litterate/une première ligne ,, >> un saut de ligne et la seconde --/ ****************** Fin code ****************** ****************** Code 53 ****************** \def\baz#1#2#3#{"#1", puis "#2" et enfin "#3".} \baz{abc}def{gh}ij ****************** Fin code ****************** ****************** Code 54 ****************** \catcode`\@11 \long\def\firstto@nil#1#2\@nil{#1} \long\def\remainto@nil#1#2\@nil{#2} a) \firstto@nil foobar\@nil \qquad b) \remainto@nil foobar\@nil \par c) \firstto@nil {foo}bar\@nil \qquad d) \remainto@nil {foo}bar\@nil ****************** Fin code ****************** ****************** Code 55 ****************** \catcode`\@=11 % "@" devient une lettre \def\rightof#1#2{% \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire \right@of#1\@nil% appelle la macro auxiliaire où #1 est la } \catcode`\@=12 %"@" redevient un signe --\rightof{Programmation}{g}--\par --\rightof{Programmation}{gram}--\par --\rightof{Programmation}{on}-- ****************** Fin code ****************** ****************** Code 56 ****************** \catcode`\@=11 % "@" devient une lettre \def\rightof#1#2{% \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire \right@of#1\@nil% appelle la macro auxiliaire } \catcode`\@=12 %"@" redevient un signe --\rightof{Programmation}{z}--\par --\rightof{Programmation}{Gram} ****************** Fin code ****************** ****************** Code 57 ****************** \catcode`\@=11 % "@" devient une lettre \def\rightof#1#2{% \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire \right@of#1#2\@nil% appelle la macro auxiliaire avec le motif #2 } \catcode`\@=12 %"@" redevient un signe --\rightof{Programmation}{g}--\par --\rightof{Programmation}{ti}--\par --\rightof{Programmation}{z}--\par --\rightof{Programmation}{Gram}-- ****************** Fin code ****************** ****************** Code 58 ****************** \catcode`\@=11 % "@" devient une lettre \def\gobto@@nil#1\@@nil{} \def\rightof#1#2{% \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire \right@of#1\gobto@@nil#2\gobto@@nil\@@nil\@nil% appelle la macro auxiliaire } \catcode`\@=12 %"@" redevient un signe --\rightof{Programmation}{g}--\par --\rightof{Programmation}{ti}--\par --\rightof{Programmation}{z}--\par --\rightof{Programmation}{Gram}-- ****************** Fin code ****************** ****************** Code 59 ****************** \catcode`\@=11 % "@" devient une lettre \def\rightof#1#2{% \ifin{#1}{#2}% {% si #1 contient le #2 \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire \right@of#1\@nil% appelle la macro auxiliaire }% {}% sinon, ne rien faire } \catcode`\@=12 %"@" redevient un signe --\rightof{Programmation}{g}--\par --\rightof{Programmation}{z}--\par --\rightof{Programmation}{o}-- ****************** Fin code ****************** ****************** Code 60 ****************** \catcode`\@=11 \def\leftof#1#2{% \def\leftof@i##1#2##2\@nil{##1}% renvoie ce qui est à gauche de #2 \leftof@i #1#2\@nil% ajoute #2 après #1 } \catcode`\@=12 --\leftof{Programmation}{ram}--\par --\leftof{Programmation}{zut}-- ****************** Fin code ****************** ****************** Code 61 ****************** \catcode`\@=11 \def\leftof#1#2{% \def\leftof@i##1#2##2\@nil{\leftof@ii##1#1\@nil}% \def\leftof@ii##1#1##2\@nil{##1}% \leftof@i #1#2\@nil } \catcode`\@=12 --\leftof{Programmation}{ram}--\par --\leftof{Programmation}{zut}-- ****************** Fin code ****************** ****************** Code 62 ****************** \catcode`\@=11 \def\rightof#1#2{% \def\rightof@i##1#2##2\@nil{\rightof@ii##2#2\@nil}% \def\rightof@ii##1#2##2\@nil{##1}% \rightof@i#1#2\@nil } \catcode`\@=12 --\rightof{Programmation}{g}--\par --\rightof{Programmation}{z}-- ****************** Fin code ****************** ****************** Code 63 ****************** \catcode`\@=11 \def\gobto@@nil#1\@@nil{} \def\rightof#1#2{% \def\right@of##1#2##2\@nil{##2}% définit la macro auxiliaire \right@of#1\gobto@@nil#2\gobto@@nil\@@nil\@nil% appelle la macro auxiliaire } \catcode`\@=12 \rightof{abc{1}def}{c{1}d}% affiche ce qui est à droite de "c{1}d" ****************** Fin code ****************** ****************** Code 64 ****************** \expandafter\def\csname ab1c\endcsname{Bonjour} Texte de remplacement de la macro : \csname ab1c\endcsname ****************** Fin code ****************** ****************** Code 65 ****************** \def\defname#1{\expandafter\def\csname#1\endcsname} \defname{ab1c}{Bonjour} Texte de remplacement de \litterate|\abc1| : \csname ab1c\endcsname\par \def\letname#1{\expandafter\let\csname#1\endcsname} \def\foo{abc}\letname{foo1}=\foo% rend la macro "\foo1" égale à \foo Texte de remplacement de \litterate|\foo1| : \csname foo1\endcsname ****************** Fin code ****************** ****************** Code 66 ****************** \def\foo{Bonjour}\catcode`@=11 Résultat : \firstto@nil\foo\@nil \catcode`@=12 ****************** Fin code ****************** ****************** Code 67 ****************** \def\foo{Bonjour}\catcode`@=11 Résultat : \expandafter\firstto@nil\foo\@nil \catcode`@=12 ****************** Fin code ****************** ****************** Code 68 ****************** \newtoks\testtoks \testtoks={Bonjour} \catcode`@=11 Résultat : \expandafter\firstto@nil\the\testtoks\@nil \catcode`@=12 ****************** Fin code ****************** ****************** Code 69 ****************** \def\foo{bonjour} a) \>\csname foo\endcsname< \par b) \>\foo< \par c) \expandafter\>\foo< \par d) \>\foo\foo< \par e) \expandafter\>\foo\foo< ****************** Fin code ****************** ****************** Code 70 ****************** \def\foo{Bonjour} \expandafter\>\expandafter\foo\foo< ****************** Fin code ****************** ****************** Code 71 ****************** \def\foo{Bonjour} \expandafter\def\expandafter\bar\expandafter{\expandafter\foo\foo} \meaning\bar ****************** Fin code ****************** ****************** Code 72 ****************** \def\foo{\foo Bonjour}% définition récursive \foo% l'exécution provoque un dépassement de la capacité de la pile ****************** Fin code ****************** ****************** Code 73 ****************** \long\def\addtomacro#1#2{% \expandafter\def\expandafter#1\expandafter{#1#2}% } \def\foo{Bonjour} \addtomacro\foo{ le monde} \meaning\foo ****************** Fin code ****************** ****************** Code 74 ****************** \long\def\eaddtomacro#1#2{% \expandafter\addtomacro\expandafter#1\expandafter{#2}% } \def\foo{Bonjour} \def\world{ le monde} \eaddtomacro\foo\world \meaning\foo ****************** Fin code ****************** ****************** Code 75 ****************** \long\def\addtotoks#1#2{#1= \expandafter{\the#1#2}} \newtoks\bar \bar={Bonjour}% met "Bonjour" dans le registre \addtotoks\bar{ le monde}% ajoute " le monde" \the\bar% affiche le registre ****************** Fin code ****************** ****************** Code 76 ****************** \long\def\eaddtotoks#1#2{\expandafter\addtotoks\expandafter#1\expandafter{#2}} \bar={Bonjour} \def\macrofoo{ le monde} \eaddtotoks\bar\macrofoo \the\bar ****************** Fin code ****************** ****************** Code 77 ****************** \def\X{Bonjour} \def\Y{\X} \expandafter\expandafter\expandafter\>\Y< ****************** Fin code ****************** ****************** Code 78 ****************** \long\def\swaparg#1#2{#2{#1}} \long\def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}} \def\X{Bonjour} \expandafter\expandafter\expandafter\expandafter \expandafter\expandafter\expandafter \>\expsecond{\X}{\X}< ****************** Fin code ****************** ****************** Code 79 ****************** \expsecond{\def\foo}\secondoftwo{A}{B} ****************** Fin code ****************** ****************** Code 80 ****************** \expsecond{\def\foo}{\secondoftwo{A}{B}} \meaning\foo ****************** Fin code ****************** ****************** Code 81 ****************** \def\exptwoargs#1#2{\expsecond{\expsecond{#1}{#2}}} \def\foo{Bonjour}\def\bar{ le monde} \def\displaytwoargs#1#2{\detokenize{#1#2}}% affiche tels quels les arguments \exptwoargs\displaytwoargs\foo\bar ****************** Fin code ****************** ****************** Code 82 ****************** \def\aa{Bonjour}\def\bb{\aa} \def\cc{ le monde} \edef\foo{\bb\cc} \meaning\foo ****************** Fin code ****************** ****************** Code 83 ****************** \def\aa{Bonjour } \def\bb{\aa} \def\cc{le } \def\dd{ monde} \edef\ee#1{\bb\cc#1 !!!} \meaning\ee\par \expandafter\>\ee{\dd}< ****************** Fin code ****************** ****************** Code 84 ****************** \def\foo{123xyz} a) signification du 1-développement d'un \litterate-\noexpand- : \expandafter\meaning\noexpand\foo b) exécution d'un \litterate-\noexpand- : \noexpand\foo% identique à \relax c) 1-développement de \litterate-\noexpand- dans le texte de remplacement de \litterate-\bar- : \expandafter\def\expandafter\bar\expandafter{\noexpand\foo} \meaning\bar ****************** Fin code ****************** ****************** Code 85 ****************** \def\aa{Bonjour}\def\bb{\aa}\def\cc{\bb} a) \edef\foo{\aa\noexpand\bb\cc}\meaning\foo \qquad b) exécution : \foo\par c) \edef\foo{\aa\noexpand{\aa\cc}}\meaning\foo\qquad d) exécution : \foo ****************** Fin code ****************** ****************** Code 86 ****************** \def\aa{Bonjour}\def\bb{Au revoir} a) \edef\truc{\aa\noexpand\bb\noexpand\aa\bb}\meaning\truc \par b) \edef\truc{\aa\unexpanded{\bb\aa}\bb}\meaning\truc ****************** Fin code ****************** ****************** Code 87 ****************** \def\aa{Bonjour}\def\bb{Au revoir} \toks0={\aa\bb} \edef\foo{\aa\the\toks0 \bb} \meaning\foo ****************** Fin code ****************** ****************** Code 88 ****************** \def\aa{Bonjour} \protected\def\bb{ le}% \ bb est protégée ! \def\cc{ monde} \edef\foo{\aa\bb\cc} Signification de \string\foo~: \meaning\foo \par Exécution de \string\foo~: \foo ****************** Fin code ****************** ****************** Code 89 ****************** \def\aa{Bonjour} \protected\def\bb{ le} \def\cc{ monde} \edef\foo{\expandafter\aa\bb\cc}% le \expandafter 1-développe \bb \meaning\foo ****************** Fin code ****************** ****************** Code 90 ****************** \def\foo{Bonjour}% définition \foo% exécution ****************** Fin code ****************** ****************** Code 91 ****************** \def\foo{abc} \edef\bar{\def\foo{Bonjour}\foo} \meaning\bar ****************** Fin code ****************** ****************** Code 92 ****************** \def\foo{abc} \edef\bar{\def\noexpand\foo{Bonjour}\noexpand\foo} Signification : \meaning\bar\par Exécution : \bar ****************** Fin code ****************** ****************** Code 93 ****************** \long\def\>#1<{\detokenize{#1}} \expandafter\>% provoque le 1-développement de : \csname foo\expandafter\endcsname \csname bar\expandafter\endcsname \csname wee\expandafter\endcsname \csname fin\endcsname< ****************** Fin code ****************** ****************** Code 94 ****************** \long\def\>#1<{\detokenize{#1}} \def\fin{YY} \def\wee{\weeA} \def\weeA{XX} \expandafter\>% \csname foo\expandafter\endcsname \csname bar\expandafter\expandafter\expandafter\expandafter \expandafter\expandafter\expandafter\endcsname \csname wee\expandafter\expandafter\expandafter\endcsname \csname fin\endcsname< ****************** Fin code ****************** ****************** Code 95 ****************** \long\def\>#1<{\detokenize{#1}} \def\fin{YYY} \def\wee{\weeA} \def\weeA{XXX} \expandafter\expandafter\expandafter\expandafter \expandafter\expandafter\expandafter \>% \expandafter\expandafter\expandafter\expandafter \expandafter\expandafter\expandafter \foo \expandafter\expandafter\expandafter\expandafter \expandafter\expandafter\expandafter \bar \expandafter\wee\fin< ****************** Fin code ****************** ****************** Code 96 ****************** \chardef\foo65 a) |\foo|\qquad % employée seule, \foo affiche un A b) |\number\foo|\qquad % si TeX lit un nombre, \foo est le nombre 65 ****************** Fin code ****************** ****************** Code 97 ****************** \mathchardef\foo4944 a) |$\foo$|\qquad % en mode math, affiche le signe "somme" (uniquement en mode maths) b) |\number\foo|\qquad % si TeX lit un nombre, \foo est 4944 ****************** Fin code ****************** ****************** Code 98 ****************** a) "\romannumeral 27 "\quad b) "\romannumeral 4687 "\quad c) "\romannumeral 0 "\quad% 0 donc développement vide d) "\romannumeral -2014 "\quad% négatif donc développement vide e) \def\foo{7}\def\bar{\foo}% "\romannumeral \foo\foo\bar"\quad% 777 f) "\romannumeral 1\bar8\foo"\quad% 1787 g) "\romannumeral 1\bar8 \foo"% 178 (stoppé par l'espace) puis 7 ****************** Fin code ****************** ****************** Code 99 ****************** \long\def\>#1<{\detokenize{#1}} \def\swaparg#1#2{#2{#1}} \def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}} \def\X{Bonjour} \expandafter\expandafter\expandafter\expandafter \expandafter\expandafter\expandafter \>\expsecond\X\X< ****************** Fin code ****************** ****************** Code 100 ****************** \long\def\>#1<{\detokenize{#1}} \def\swaparg#1#2{0 #2{#1}}% le "0 " stoppe ici le développement maximal \def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}} \def\X{Bonjour} \expandafter\>\romannumeral\expsecond\X\X< ****************** Fin code ****************** ****************** Code 101 ****************** \long\def\>#1<{\detokenize{#1}} \def\swaparg#1#2{#2{#1}} \def\expsecond#1#2{\expandafter\swaparg\expandafter{#2}{#1}} \def\X{Bonjour} \expandafter\>\romannumeral\expsecond{0 \X}\X< ****************** Fin code ****************** ****************** Code 102 ****************** \long\def\>#1<{\detokenize{#1}} \def\X{Bonjour} \catcode`\@=11 \expandafter\>\romannumeral\expsecond{\z@\X}{\X}< \catcode`\@=12 ****************** Fin code ****************** ****************** Code 103 ****************** \def\newunivar#1{\def#1[##1]{\csname\string#1[##1]\endcsname}} \def\defunivar#1[#2]{\defname{\string#1[#2]}} \newunivar\foobar \defunivar\foobar[0]{abcd} \defunivar\foobar[1]{1 23} \defunivar\foobar[3]{XY Z} Cellule 0 : \foobar[0]\par Cellule 1 : \foobar[1]\par Cellule 2 : \foobar[2]\par% cellule non définie : \foobar[2] donne \relax Cellule 3 : \foobar[3]\bigbreak \newunivar\client \defunivar\client[nom]{M. Raymond {\sc Tartempion}} \defunivar\client[adr]{5 rue de la paix} \defunivar\client[cod_post]{75000} \defunivar\client[ville]{Paris} % fin des définitions, affichage de l'adresse : \client[nom]\par \client[adr]\par \client[cod_post] \client[ville] ****************** Fin code ****************** ****************** Code 104 ****************** \newcount\foo \newcount\bar \foo=42 \bar=\foo \string\bar\ vaut : \the\bar \par \bar=\foo57 \string\bar\ vaut : \number\bar\par \bar=2014 \string\bar\ vaut : \romannumeral\bar ****************** Fin code ****************** ****************** Code 105 ****************** \newcount\foo \def\fonction#1{% \foo=#1 % assigne l'entier #1 au compteur puis \multiply\foo3 % multiplie par 3 \advance\foo-4 % soustrait 4 \multiply\foo\foo% élève au carré (lmultiplie \foo par lui même) \number\foo% enfin, afficher le résultat } a) \fonction5\qquad b) \fonction{-13} ****************** Fin code ****************** ****************** Code 106 ****************** \newcount\foo \def\fonction#1{% \foo=#1 \multiply\foo3 \advance\foo-4 \multiply\foo\foo \number\foo} \edef\bar{\fonction{5}}% a) Signification : \meaning\bar\par b) Exécution : \bar ****************** Fin code ****************** ****************** Code 107 ****************** a) \number\numexpr2+3*4\relax\qquad b) \romannumeral\numexpr2*3\relax\qquad c) \number\numexpr(6-2*4)*(1-(2-6))\relax\qquad d) \number\numexpr7/4\relax\qquad %7/4 vaut 1,75 e) \edef\foo{\number\numexpr2+3*4\relax}\meaning\foo ****************** Fin code ****************** ****************** Code 108 ****************** \number\numexpr3*\numexpr1+2*5\relax+2\relax ****************** Fin code ****************** ****************** Code 109 ****************** \def\fonction#1{\number\numexpr(3*#1-4)*(3*#1-4)\relax} a) \fonction5\qquad b) \fonction{-13}\qquad c) \edef\bar{\fonction5}\meaning\bar ****************** Fin code ****************** ****************** Code 110 ****************** \def\fonction#1{\exparg\carre{\number\numexpr3*#1-4\relax}} \def\carre#1{\number\numexpr#1*#1\relax} a) \fonction{5}\qquad b) \fonction{-13}\qquad c) \edef\bar{\fonction5}\meaning\bar ****************** Fin code ****************** ****************** Code 111 ****************** \def\truncdiv#1#2{\numexpr(#1-(#2-1)/2)/#2\relax} 8/3 : a) \number\truncdiv83\qquad % doit donner 2 b) \number\truncdiv{-8}3\qquad % doit donner -2 c) \number\truncdiv8{-3}\qquad % doit donner -2 d) \number\truncdiv{-8}{-3}\par % doit donner 2 4/3 : e) \number\truncdiv43\qquad % doit donner 1 f) \number\truncdiv{-4}3\qquad % doit donner -1 g) \number\truncdiv4{-3}\qquad % doit donner -1 h) \number\truncdiv{-4}{-3} % doit donner 1 ****************** Fin code ****************** ****************** Code 112 ****************** a) \ifnum 15=14 vrai\else faux\fi\qquad% test faux b) \ifnum4<200 vrai\else faux\fi\qquad% test vrai c) \newcount\foo \foo=9 \ifnum\foo>9 nombre\else chiffre\fi% test faux ****************** Fin code ****************** ****************** Code 113 ****************** \long\def\>#1<{\detokenize{#1}} \expandafter\>\ifnum5=5 vrai\else faux\fi<\par% test vrai \expandafter\>\ifnum5=6 vrai\else faux\fi<% test faux \fi\fi% rétablir l'équilibre \ifnum et \fi ****************** Fin code ****************** ****************** Code 114 ****************** \def\numtest#1{% \ifnum#1>-10 \ifnum#1<10 chiffre% \else nombre positif% \fi \else nombre négatif% \fi } a) \numtest{3}\qquad b) \numtest{-67}\qquad c) \numtest{-8}\qquad d) \numtest{21}\qquad e) \edef\foo{\numtest{2014}}\meaning\foo ****************** Fin code ****************** ****************** Code 115 ****************** \def\normalise#1{% \ifnum#1>-1 % ne faire quelque chose que si #1 est positif ou nul \ifnum#1<100 % si <100 0% afficher un 0 \ifnum#1<10 % si <10 0%afficher un autre 0 \fi \fi \number#1 %afficher le nombre \fi}% affiche le nombre a) \normalise{749}\qquad b) \normalise{0017}\qquad c) \normalise8\qquad d) \normalise{1789}\qquad e) \normalise{-18}\qquad f) \normalise{0} ****************** Fin code ****************** ****************** Code 116 ****************** \def\mois#1{% \ifcase#1\relax \char`\#\char`\#% afficher "##" si argument = 0 \or janvier\or f\'evrier\or mars\or avril% \or mai\or juin\or juillet\or aout% \or septembre\or octobre\or novembre\or d\'ecembre% \else \char`\#\char`\#% afficher "##" sinon \fi } a) \mois{-4}\qquad b) \mois{3}\qquad c) \mois{11}\qquad d) \mois{20}\qquad e) \edef\foo{\mois{\month}}\meaning\foo% mois en cours ****************** Fin code ****************** ****************** Code 117 ****************** \def\ifletter#1#2#3{% \ifnum`#1<`a% si le caractère est avant "a" #3% exécuter "" \else% sinon \ifnum`#1>`z% si le caractère est après "z" #3% exécuter "" \else% dans tous les autres cas #2% "#1" est une lettre : exécuter "" \fi \fi} a) \ifletter{4}{vrai}{faux}\qquad b) \ifletter{t}{vrai}{faux}\qquad c) \ifletter{D}{vrai}{faux} ****************** Fin code ****************** ****************** Code 118 ****************** \def\ifletter#1{% \ifnum`#1<`a \expandafter\secondoftwo% \expandafter 1-développe "\else...\fi" \else \ifnum`#1>`z \expandafter\expandafter\expandafter% 1-développe "\else...\fi" puis "\fi" \secondoftwo \else \expandafter\expandafter\expandafter%1-développe "\fi" puis "\fi" \firstoftwo \fi \fi} a) \ifletter{4}{vrai}{faux}\qquad b) \ifletter{t}{vrai}{faux}\qquad c) \ifletter{D}{vrai}{faux} ****************** Fin code ****************** ****************** Code 119 ****************** \def\ifletter#1#2#3{% \romannumeral % <- initie le développement maximal \ifnum`#1<`a \expandafter\secondoftwo \else \ifnum`#1>`z \expandafter\expandafter\expandafter\secondoftwo \else \expandafter\expandafter\expandafter\firstoftwo \fi \fi{0 #2}{0 #3}% <- "0 " stoppe le développement maximal } \long\def\>#1<{\detokenize{#1}} a) \expandafter\expandafter\expandafter\>\ifletter{0}{vrai}{faux}<\qquad b) \expandafter\expandafter\expandafter\>\ifletter{x}{vrai}{faux}< ****************** Fin code ****************** ****************** Code 120 ****************** \def\ifletter#1{% \ifnum`#1<`a \let\donext=\secondoftwo \else \ifnum`#1>`z \let\donext=\secondoftwo \else \let\donext=\firstoftwo \fi \fi \donext% exécuter l'action décidée ci-dessus } a) \ifletter{4}{vrai}{faux}\qquad b) \ifletter{t}{vrai}{faux}\qquad c) \ifletter{D}{vrai}{faux} ****************** Fin code ****************** ****************** Code 121 ****************** \def\ifletter#1{% \csname \ifnum`#1<`a second% <- commenter la fin de ligne pour éviter un \else% espace parasite dans le nom de la macro créée \ifnum`#1>`z second% \else first% \fi \fi oftwo% \endcsname } a) \ifletter{4}{vrai}{faux}\qquad b) \ifletter{t}{vrai}{faux}\qquad c) \ifletter{D}{vrai}{faux} ****************** Fin code ****************** ****************** Code 122 ****************** \def\ifletter#1{% \lowercase{\csname \ifnum`#1<`a second% \else \ifnum`#1>`z second\else first\fi \fi oftwo\endcsname }% } a) \ifletter{4}{vrai}{faux}\qquad b) \ifletter{t}{vrai}{faux}\qquad c) \ifletter{D}{vrai}{faux} ****************** Fin code ****************** ****************** Code 123 ****************** \edef\foo{\lowercase{AbCd}}\meaning\foo ****************** Fin code ****************** ****************** Code 124 ****************** \def\ifletter#1{% \csname \ifnum`#1<`A second% \else \ifnum`#1<`Z first% \else \ifnum`#1>`a \ifnum`#1<`z first% \else second% \fi \else second% \fi \fi \fi oftwo% \endcsname } a) \ifletter{4}{vrai}{faux}\qquad b) \ifletter{t}{vrai}{faux}\qquad c) \ifletter{D}{vrai}{faux}\qquad d) \edef\foo{\ifletter{x}{vrai}{faux}}\meaning\foo\qquad e) \edef\foo{\ifletter{=}{vrai}{faux}}\meaning\foo ****************** Fin code ****************** ****************** Code 125 ****************** \def\ifinside#1[#2,#3]{% \csname \ifnum#1<#2 second% si n \else \ifnum#1>#3 second% si n>b, \else first% sinon, \fi \fi oftwo% \endcsname } a) \ifinside 3[1,8]{oui}{non}\qquad b) \ifinside -7[-20,-11]{oui}{non}\qquad c) \ifinside 9[0,6]{oui}{non}\qquad d) \ifinside -1[-5,1]{oui}{non} ****************** Fin code ****************** ****************** Code 126 ****************** \def\ifinside#1[#2,#3]{% \ifnum\numexpr(#1-#2)*(#1-#3)\relax>0 \expandafter\secondoftwo \else \expandafter\firstoftwo \fi } a) \ifinside 3[1,8]{oui}{non}\qquad b) \ifinside -7[-20,-11]{oui}{non}\qquad c) \ifinside 9[0,6]{oui}{non}\qquad d) \ifinside -1[-5,1]{oui}{non} ****************** Fin code ****************** ****************** Code 127 ****************** \def\absval#1{% \ifnum#1<0 \number-#1 \else \number#1 \fi} a) \absval{-87}\qquad b) \absval{75}\qquad c) \absval{0} ****************** Fin code ****************** ****************** Code 128 ****************** \def\absval#1{\number\ifnum#1<0 -\fi#1 } a) \absval{-87}\qquad b) \absval{75}\qquad c) \absval{0} ****************** Fin code ****************** ****************** Code 129 ****************** \def\absval#1{\number\ifnum\numexpr#1\relax<0 -\fi\numexpr#1\relax} a) \absval{-87}\qquad b) \absval{75}\qquad c) \absval{0}\qquad d) \absval{-20+13}\qquad% -7 affiché 7 e) \absval{5-3*(-4)+10-50}% -23 affiché 23 ****************** Fin code ****************** ****************** Code 130 ****************** \catcode`\@11 \def\absval#1{\exparg\absval@i{\number\numexpr#1\relax}} \def\absval@i#1{\number\ifnum#1<\z@-\fi#1 } \catcode`\@12 a) \absval{-87}\qquad b) \absval{75}\qquad c) \absval{0}\qquad d) \absval{-20+13}\qquad% résultat -7 qui est affiché 7 e) \absval{5-3*(-4)+10-50}% résultat -23 qui est affiché 23 ****************** Fin code ****************** ****************** Code 131 ****************** \def\sgn#1{\ifnum#1<0 -\fi} \def\truncdiv#1#2{% \numexpr \sgn{#1}\sgn{#2}1*% multiplie le quotient ci-dessous par +1 ou -1 (2*\absval{#1}-\absval{#2}+1)/(2*\absval{#2}) \relax } a) \number\truncdiv{-8}3\qquad b) \number\truncdiv{-8}{-3}\qquad c) \number\truncdiv8{-3}\qquad d) \number\truncdiv{0}{-5} ****************** Fin code ****************** ****************** Code 132 ****************** \catcode`\@11 \def\sgn#1{\ifnum#1<\z@-\fi} \def\truncdiv#1#2{% \exptwoargs\truncdiv@i{\number\numexpr#1\relax}{\number\numexpr#2\relax}% } \def\truncdiv@i#1#2{% \numexpr(2*\sgn{#1}#1-\sgn{#2}#2+1)/(\sgn{#1}2*#2)\relax } \catcode`\@12 a) \number\truncdiv{-8}3\qquad b) \number\truncdiv{-8}{-3}\qquad c) \number\truncdiv8{-3}\qquad d) \number\truncdiv{0}{-5}\qquad e) \number\truncdiv{20+2*3}{4-5*2}% 26/(-6) doit donner -4 ****************** Fin code ****************** ****************** Code 133 ****************** \def\siecle#1{% \unless\ifnum#1=0 % ne faire quelque chose que si #1 différent de 0 \uppercase\expandafter{\romannumeral\absval{#1}}% chiffres romains majuscules \raise 1ex \hbox{e\ifnum\absval{#1}=1 r\fi} siècle% affiche l'exposant puis "siècle" \ifnum#1<0 {} av. J.-C.\fi% affiche si besoin "av. J.-C." \fi } a) \siecle{19}\qquad b) \siecle{-3}\qquad c) \siecle{1}\qquad d) \siecle{0}\qquad e) \siecle{-1} ****************** Fin code ****************** ****************** Code 134 ****************** \catcode`\@11 \edef\saved@catcode{\number\catcode`\:}% sauvegarde du catcode de ":" \catcode`\:12 % ":" est désormais un caractère non actif normal \def\hdif{% \begingroup% ouvrir un groupe semi-simple \catcode`\:12 % modifier le catcode de ":" \hdif@i% aller lire les deux arguments } \def\hdif@i#1#2{% lit les 2 arguments \hdif@ii#1-#2\@nil% et les transmet à la macro à argument délimités } \def\hdif@ii#1:#2:#3-#4:#5:#6\@nil{% %%%%%% calcul des nombres à afficher %%%%%% \edef\nb@hrs{\number\numexpr#1-#4\relax}% différence des heures \edef\nb@min{\number\numexpr#2-#5\relax}% différence des minutes \edef\nb@sec{\number\numexpr#3-#6\relax}% différence des secondes \ifnum\nb@sec<\z@ % si la différence des secondes est <0 \edef\nb@sec{\number\numexpr\nb@sec+60\relax}% ajouter 60 sec \edef\nb@min{\number\numexpr\nb@min-1\relax}% enlever 1 aux minutes \fi \ifnum\nb@min<\z@ % si les minutes sont <0 \edef\nb@min{\number\numexpr\nb@min+60\relax}% ajouter 60 min \edef\nb@hrs{\number\numexpr\nb@hrs-1\relax}% enlever 1 aux heures \fi %%%%%% affichage du résultat %%%%%% \let\inter@space\empty% pas d'espace avant un nombre pour l'instant \ifnum\nb@hrs>\z@ % si les heures sont >0 \nb@hrs h% afficher les heures et "h" \let\inter@space\space% espace pour plus tard \fi \ifnum\nb@min>\z@ % si les minutes sont >0 \inter@space% afficher une espace éventuelle \nb@min min% afficher les minutes et "min" \let\inter@space\space \fi \ifnum\nb@sec>\z@ % si les secondes sont >0 \inter@space% afficher une espace éventuelle \nb@sec s% afficher les secondes et "s" \fi \endgroup% fermer le groupe ouvert au début } \catcode`\:=\saved@catcode\relax% restaure le code de catégorie de ":" \catcode`\@12 a) \hdif{10:51:20}{9:20:10}\par b) \hdif{14:20:0}{13:50:0}\par c) \hdif{7:50:20}{5:50:20}\par d) \hdif{7:50:10}{6:50:20}\par e) \hdif{20:00:00}{19:59:15}\par f) \hdif{17:13:15}{14:12:45} ****************** Fin code ****************** ****************** Code 135 ****************** \newcount\ii \catcode`\@=11 \def\ncar#1#2{% \ii=0 % initialise le compteur à 0 \def\val@max{#1}% stocke la valeur maximale \def\car@save{#2}% stocke le caractère à afficher \ncar@i% appelle la macro récursive } \def\ncar@i{% \ifnum\ii<\val@max% si la valeur maxi n'est pas atteinte \car@save% afficher le caractère \advance\ii 1 % incrémenter le compteur \ncar@i% recommencer \fi } \catcode`\@=12 \ncar{7}{*}\par \ncar{13}W\par \ncar{10}{foobar } ****************** Fin code ****************** ****************** Code 136 ****************** \newcount\ii \catcode`\@=11 \def\ncar#1#2{% \ii=0 % initialise le compteur à 0 \def\val@max{#1}% stocke la valeur maximale \def\car@save{#2}% stocke le caractère à afficher \ncar@i% appelle la macro récursive } \def\ncar@i{% \ifnum\ii<\val@max% si la valeur maxi n'est pas atteinte \car@save% afficher le caractère \advance\ii 1 % incrémenter le compteur \ncar@i% recommencer \fi } \catcode`\@=12 \ncar{2}{3a} ****************** Fin code ****************** ****************** Code 137 ****************** a) \romannumeral 9600 \qquad b) \romannumeral 12000 ****************** Fin code ****************** ****************** Code 138 ****************** \def\ncar#1#2{% \begingroup \lccode`\m=`#2 % dans \lowercase, les "m" deviennent des "#2" \lowercase\expandafter{\expandafter\endgroup\romannumeral#1000 }% } a) \ncar{7}{*}\qquad b) \ncar{15}{\%}\qquad c) \ncar{10}{4} ****************** Fin code ****************** ****************** Code 139 ****************** \def\ncar#1#2{% \ifnum#1>0 % <- espace après le "0" #2% affiche le caractère \exparg\ncar{\number\numexpr#1-1}{#2}% appel récursif \fi } a) \ncar{7}{*}\qquad b) \ncar{13}{W}\qquad c) \edef\foo{\ncar{5}{7}}\meaning\foo ****************** Fin code ****************** ****************** Code 140 ****************** \def\ncar#1#2{% \ifnum#1>0 \expandafter\identity% si #1>0 exécuter... \else \expandafter\gobone% ...sinon manger \fi% ce qui est entre ces accolades : {#2% afficher le caractère \exparg\ncar{\number\numexpr#1-1}{#2}% appel récursif }% } a) \ncar{7}{*}\qquad b) \ncar{13}{W}\qquad c) \edef\foo{\ncar{5}X}\meaning\foo ****************** Fin code ****************** ****************** Code 141 ****************** \long\def\antefi#1\fi{\fi#1} \def\ncar#1#2{% \ifnum#1>0 \antefi% comme si le \fi était "déplacé" ici #2% affiche le caractère \exparg\ncar{\number\numexpr#1-1}{#2}% appel récursif \fi } a) \ncar{7}{*}\qquad b) \ncar{13}{W}\qquad c) \edef\foo{\ncar{5}X}\meaning\foo ****************** Fin code ****************** ****************** Code 142 ****************** \long\def\afterfi#1#2\fi{#2\fi#1} \def\ncar#1#2{% \ifnum#1>0 \afterfi{\exparg\ncar{\number\numexpr#1-1}{#2}}% <- argument renvoyé après le \fi #2% <- ceci reste avant le \fi \fi } a) \ncar{7}{*}\qquad b) \ncar{13}{W}\qquad c) \edef\foo{\ncar{5}X}\meaning\foo ****************** Fin code ****************** ****************** Code 143 ****************** \def\ncar#1#2{% \ifnum#1>0 #2% afficher le caractère \expandafter\ncar\expandafter% le pont d'\expandafter lance \number et \numexpr {\number\numexpr#1-1\expandafter}% le dernier \expandafter mange "\else...\fi" \else \expandafter\gobone% si le test est faux, manger {#2} ci-dessous \fi{#2}% } a) \ncar{7}{*}\qquad b) \ncar{13}{W}\qquad c) \edef\foo{\ncar{5}X}\meaning\foo ****************** Fin code ****************** ****************** Code 144 ****************** \catcode`\@11 \def\ncar#1#2{% \def\ncar@i##1{% \ifnum##1<#1 % si i < valeur maxi #2% afficher le caractère \exparg\ncar@i{\number\numexpr##1+1\expandafter}% puis recommencer avec i+1 \fi% \fi mangé par le \expandafter en fin de zone de \numexpr }% \ncar@i{0}% commence avec la valeur "compteur" 0 } \catcode`\@12 a) \ncar{7}{*}\qquad b) \ncar{13}{W}\qquad c) \ncar{5}{X} ****************** Fin code ****************** ****************** Code 145 ****************** \catcode`\@11 \newcount\compte@cnt \def\compte#1{% \def\val@max{#1}\compte@cnt=0 % effectue les initialisations \compte@i% appelle la macro principale } \def\compte@i{% \ifnum\compte@cnt<\val@max% si compteur < valeur maxi \advance\compte@cnt1 % incrémenter le compteur \number\compte@cnt, % afficher le nombre, la virgule et l'espace \expandafter\compte@i %recommencer \fi } \catcode`\@12 a) \compte{9}\qquad b) \compte{-4}\qquad c) \compte{2} ****************** Fin code ****************** ****************** Code 146 ****************** \catcode`\@11 \newcount\compte@cnt \def\compte#1{\def\val@max{#1}\compte@cnt=0 \compte@i} \def\compte@i{% \ifnum\compte@cnt<\val@max\relax \advance\compte@cnt1 \number\compte@cnt \ifnum\compte@cnt<\val@max , \fi% afficher ", " si le maxi n'est pas atteint \expandafter\compte@i \fi } \catcode`\@12 a) \compte{9}\qquad b)\compte{-4}\qquad c) \compte{2} ****************** Fin code ****************** ****************** Code 147 ****************** \catcode`\@11 \newcount\compte@cnt \def\compte#1{% \ifnum#1>0 % ne faire quelque chose que si l'argument est positif \def\val@max{#1}\compte@cnt=1 % faire les initialisations \expandafter\compte@i% appeller la macro récrusive \fi } \def\compte@i{% \number\compte@cnt\relax % afficher le nombre \ifnum\compte@cnt<\val@max\relax % si valeur maxi n'est pas atteinte , % afficher la virgule et l'espace \advance\compte@cnt1 % incrémenter le compteur \expandafter\compte@i % recommencer \fi } \catcode`\@12 a) \compte{9}\qquad b) \compte{-4}\qquad c) \compte{2} ****************** Fin code ****************** ****************** Code 148 ****************** \catcode`\@11 \def\compte#1{% \ifnum#1>\z@% ne faire quelque chose que si #1 > 0 \antefi\compte@i{1}{#1}% \fi } \def\compte@i#1#2{% #1% afficher le nombre \ifnum#1<#2 % si le maxi n'est pas atteint \expandafter\identity% exécuter... \else \expandafter\gobone% sinon, manger... \fi% le code entre accolades ci-dessous {, % afficher la virgule et l'espace \exparg\compte@i{\number\numexpr#1+1}{#2}% appel récursif }% } \catcode`\@12 a) \compte{9}\qquad b) \compte{-4}\qquad c) \edef\foo{\compte{2}}\meaning\foo ****************** Fin code ****************** ****************** Code 149 ****************** \catcode`\@11 \def\compte#1{% \ifnum#1>\z@% ne faire quelque chose que si #1 > 0 \antefi\compte@i{1}{#1}% \fi } \def\compte@i#1#2{% #1% afficher le nombre \ifnum#1<#2 % si le maxi n'est pas atteint \antefi% déplace le \fi ici , % affiche la virgule et l'espace \exparg\compte@i{\number\numexpr#1+1}{#2}% appel récursif \fi } \catcode`\@12 a) \compte{9}\qquad b) \compte{-4}\qquad c) \edef\foo{\compte{2}}\meaning\foo ****************** Fin code ****************** ****************** Code 150 ****************** \catcode`\@11 \def\for#1=#2to#3\do#4{% #1=\ #2=n1 #3=n2 #4= \def#1{#2}% initialise la \ à l'entier n1 \def\val@max{#3}% stocke n2 \def\loop@code{#4}% stocke le \for@i#1% appelle la macro récursive et passe la \ en argument } \def\for@i#1{% \unless\ifnum#1>\val@max\relax% tant que la \ est <= n2 \loop@code% effectue le code précédemment sauvegardé \edef#1{\number\numexpr#1+1}% incrémenter la \ \expandafter\for@i\expandafter#1% et recommencer après avoir mangé le \fi \fi } \catcode`\@=12 \for\ii=1to5\do{Maintenant \string\ii\ vaut : \ii.\endgraf}\medbreak "\for\ii=1to0\do{A}"\medbreak% boucle parcourue 0 fois "\for\ii=1to1\do{A}"\medbreak% boucle parcourue 1 fois \for\ii = 0 to 10 \do {[\ii]}. ****************** Fin code ****************** ****************** Code 151 ****************** \catcode`\@11 \long\def\for#1=#2to#3\do#4{% \def\for@i{% \unless\ifnum#1>\val@max\relax% tant que la \ <= n2 #4% code à exécuter \edef#1{\number\numexpr#1+1}% incrémenter \ \expandafter\for@i% et recommencer après avoir mangé le \fi \fi }% \edef#1{\number\numexpr#2\relax}% initialise la variable à n1 \edef\val@max{\number\numexpr#3\relax}% stocke n2 \for@i% appelle la sous-macro récursive } \catcode`\@=12 \for\ii = 1 to 5 \do {Maintenant \string\ii\ vaut : \ii.\par}\medbreak "\for\ii=1to0\do{A}"\medbreak% boucle parcourue 0 fois "\for\ii=1to1\do{A}"\medbreak% boucle parcourue 1 fois \for\ii = 0 to 10 \do {[\ii]}. ****************** Fin code ****************** ****************** Code 152 ****************** \catcode`\@11 \def\for#1=#2to#3\do#4#{% \edef\for@increment{\number\numexpr0#4}% lit et normalise l'argument optionnel \ifnum\for@increment=\z@% s'il est nul, \edef\for@increment{% le redéfinir : \ifnum\numexpr#3\relax<\numexpr#2\relax -% ajoute un signe - si #3<#2 \fi 1% devant "1" }% \for@increment vaut donc 1 ou -1 \fi \ifnum\numexpr\for@increment*(#3-#2)\relax<\z@% si l'argument est incompatible \expandafter\firstoftwo% exécuter le 1er argument qui suit \else \expandafter\secondoftwo% sinon le second \fi {Incrément incompatible !\gobone %\gobone mangera le qui à lire }% {\edef#1{\number\numexpr#2\relax}% initialise la \ \edef\cmp@sgn{\ifnum\for@increment<\z@<\else>\fi}% stocke "<" ou ">" pour plus tard \expandafter\for@i\expandafter#1\expandafter% appelle la macro récursive {\number\numexpr#3\relax}% et lui lui passe la \ (#1) et n2 (#3) }% } % #1 = \ #2 = n2 \long\def\for@i#1#2#3{% l'argument #3 (le ) est lu à ce moment-là \def\for@ii{% \unless\ifnum#1\cmp@sgn#2\relax% tant que la \ n'a pas dépassé n2 #3% exécute le \edef#1{\number\numexpr#1+\for@increment}% incrémente la \ \expandafter\for@ii% recommence après avoir mangé le \fi \fi }% \for@ii% appelle la sous-macro récursive }% \catcode`\@=12 a) \for\ii = -4 to 7 \do{(\ii)}\par b) \for\jj = 20 to -50\do-10 {(\jj)}\par c) \for\xx = 8 to 185\do 20 {[\xx]}\par d) \for\yy = 0 to 10\do -2{(\yy)}\par e) "\for\ii = 1 to 0 \do1{XX}"\par f) "\for\ii = 1 to 1 \do{A}"\par% boucle parcourue 1 fois g) \for\ii=1to4\do{\for\jj=0to2\do{(\ii,\jj)}\par}% imbrication de boucles ****************** Fin code ****************** ****************** Code 153 ****************** \catcode`\@11 \def\for#1=#2to#3\do#4#{% \edef\for@increment{\number\numexpr0#4}% lit et normalise l'argument optionnel \ifnum\for@increment=\z@% s'il est nul, \edef\for@increment{% le redéfinir à -1 (si #3<#2) et 1 sinon \ifnum\numexpr#3-#2\relax<\z@ -1\else 1\fi }% \for@increment vaut donc 1 ou -1 \fi \ifnum\numexpr\for@increment*(#3-#2)\relax<\z@ \expandafter\gobone% si argument optionnel incompatible, manger le {} \else \edef#1{\number\numexpr#2}% initialise la \ \edef\macro@args{% définit et développe les arguments à passer à \for@i %#1=nom de la macro récursive : \expandafter\noexpand\csname for@ii@\string#1\endcsname \ifnum\for@increment<\z@ <\else >\fi% #2=signe de comparaison {\for@increment}% #3=incrément \noexpand#1% #4=\ {\number\numexpr#3\relax}% #5=entier n2 }% \antefi% externalise la ligne ci-dessous de la portée du test \expandafter\for@i\macro@args% appelle \for@i avec les arguments définis ci-dessus \fi } % #1=nom de la macro récursive de type "\for@ii@\" % #2=signe de comparaison % #3=incrément % #4=\ % #5=entier n2 % #6= à exécuter \long\def\for@i#1#2#3#4#5#6{% \def#1{% définit la sous macro récursive \unless\ifnum#4#2#5\relax% tant que la \ variable n'a pas dépassé n2 \afterfi{% rendre la récursivité terminale #6% exécute le code \edef#4{\number\numexpr#4+#3\relax}% incrémente la \ #1% recommence }% \fi }% #1% appelle la sous-macro récursive }% \catcode`\@=12 \for\ii=1to4\do{\for\jj=0to2\do{(\ii,\jj)}\par}\medbreak \for\carA= `\A to `\E \do{\for\carB= `w to `z \do{\char\carA\char\carB}\quad} ****************** Fin code ****************** ****************** Code 154 ****************** \catcode`\@11 \long\def\for@i#1#2#3#4#5#6{% \def\exitfor{\def#1{}}% \def#1{% définit la sous macro récursive \unless\ifnum#4#2#5\relax% tant que la variable n'a pas dépassé l'entier max \afterfi{% rendre la récursivité terminale #6% exécute le code \edef#4{\number\numexpr#4+#3\relax}% incrémente la variable #1 #1% recommence }% \fi }% #1% appelle la sous-macro récursive }% \catcode`\@=12 \for\ii= 1 to 9 \do{\string\ii{} vaut \ii.\ifnum\ii=5 \exitfor\fi\par} ****************** Fin code ****************** ****************** Code 155 ****************** \def\exitfor#1{% #1=\ correspondant à la boucle de laquelle on veut sortir \defname{for@ii@\string#1}{} } \def\ifexitfor#1{% envoie vrai si on est prématurément sorti de la boucle de \ #1 % si la macro récursive est égale à la macro "\empty" \expandafter\ifx\csname for@ii@\string#1\endcsname\empty \expandafter\firstoftwo% c'est qu'on est sortir prématurément, renvoyer "vrai" \else \expandafter\secondoftwo% sinon, renvoyer "faux" \fi } % on ne sort QUE de la boucle intérieure quand \ii=3 donc % les boucles sont normalement exécutées pour \ii=4 et \ii=5 a. \for\ii=1 to 5\do1{% \for\jj=1 to 3\do1{(\ii,\jj) \ifnum\ii=3 \exitfor\jj\fi}% \qquad } % on sort de la boucle extérieure lorsque \ii=3 donc la boucle % intérieure est faite entièrement même lorsque \ii=3 b. \for\ii=1 to 5\do1{% \for\jj=1 to 3\do1{(\ii,\jj) \ifnum\ii=3 \exitfor\ii\fi}% \qquad } % on sort de la boucle intérieure lorsque \ii=3 % et aussi de la boucle extérieure à l'aide de \ifexitfor c. \for\ii=1 to 5\do1{% \for\jj=1 to 3\do1{(\ii,\jj) \ifnum\ii=3 \exitfor\jj\fi}% \ifexitfor\jj{\exitfor\ii}{}% si on est sorti de \jj, sortir aussi de \ii \qquad } ****************** Fin code ****************** ****************** Code 156 ****************** \def\foo{a}\ifx a\foo vrai\else faux\fi ****************** Fin code ****************** ****************** Code 157 ****************** 1) \meaning9\par% un caractère de catcode 12 2) \meaning a\par% une lettre 3) \def\foo{a}\meaning\foo\par% une macro 4) \long\def\foo{a}\meaning\foo\par% une macro déclarée \long 5) \meaning\sdlkfj\par% une macro indéfinie 6) \edef\foo{\string a}\meaning\foo\par%\foo contient un "a" de catcode 12 7) \def\foo{a}\meaning\foo\par%\foo contient un "a" de catcode 11 ****************** Fin code ****************** ****************** Code 158 ****************** a) \ifx abvrai\else faux\fi\quad% a est n'est pas égal à b b) \ifx++vrai\else faux\fi\quad% le caractère "+" est égal à "+" c) \ifx\relax\par vrai\else faux\fi\quad% \relax n'est pas la même primitive que \par d) \ifx\par\par vrai\else faux\fi\quad% \par et \par sont les mêmes primitives e) \ifx\sdfk\qlms vrai\else faux\fi\quad% 2 macros non définies sont égales f) \def\foo{abcd}\def\bar{abc}% \foo et \bar ont des textes de remplacement différents \ifx\foo\bar vrai\else faux\fi\quad g) \def\foo{abcd}\def\bar{abcd }% \foo et \bar ont des textes de remplacement différents \ifx\foo\bar vrai\else faux\fi\quad h) \def\foo{abcd}\def\bar{abcd} \ifx\foo\bar vrai\else faux\fi\quad% \foo et \bar ont les mêmes textes de remplacement i) \long\def\foo{a}\def\bar{a} \ifx\foo\bar vrai\else faux\fi\quad% \foo est \long, \bar ne l'est pas j) \edef\foo{\string a}% \foo contient un "a" de catcode 12 \def\bar{a}% \bar contient un "a" de catcode 11 \ifx\foo\bar vrai\else faux\fi ****************** Fin code ****************** ****************** Code 159 ****************** \def\cmpmacro#1#2{% \begingroup \edef\tempa{\detokenize\expandafter{#1}}\edef\tempb{\detokenize\expandafter{#2}}% \ifx\tempa\tempb% si égalité \endgroup\expandafter\firstoftwo% ferme le groupe et lit 1er argument \else \endgroup\expandafter\secondoftwo% sinon, ferme le groupe et lit le 2e argument \fi } a) \edef\foo{\string a}\def\bar{a} \cmpmacro\foo\bar{vrai}{faux}\qquad b) \edef\foo{\detokenize{$i^2=-1$\relax}}\def\bar{$i^2=-1$\relax} \cmpmacro\foo\bar{vrai}{faux} ****************** Fin code ****************** ****************** Code 160 ****************** a) \let\rien\relax \ifx\rien\relax vrai\else faux\fi\qquad b) \let\AA=a \ifx a\AA vrai\else faux\fi\qquad c) \let\foo=_\let\bar=_ \ifx\foo\bar vrai\else faux\fi ****************** Fin code ****************** ****************** Code 161 ****************** \def\quark{\quark}% définit un quark 1) \def\test{\quark} \ifx\quark\test vrai\else faux\fi \qquad 2) \let\test=\quark \ifx\quark\test vrai\else faux\fi ****************** Fin code ****************** ****************** Code 162 ****************** \def\ifempty#1{% \def\tempa{#1}% stocke l'argument #1 dans \tempa \ifx\tempa\empty% si \tempa = \empty \expandafter\firstoftwo% 1er argument \else \expandafter\secondoftwo% sinon, second \fi } a) \ifempty{abc}{vide}{pas vide}\qquad b) \ifempty{}{vide}{pas vide}\qquad c) \ifempty{ }{vide}{pas vide}\qquad d) \ifempty{\empty}{vide}{pas vide} ****************** Fin code ****************** ****************** Code 163 ****************** \def\empty{} \long\def\ifempty#1{% \ifx_#1_\expandafter\firstoftwo \else\expandafter\secondoftwo \fi} a) \ifempty{abc}{vide}{pas vide}\qquad b) \ifempty{}{vide}{pas vide}\qquad c) \ifempty{ }{vide}{pas vide}\qquad d) \ifempty{\empty}{vide}{pas vide}\qquad e) \edef\foo{\ifempty{abc}{vide}{pas vide}}\meaning\foo ****************** Fin code ****************** ****************** Code 164 ****************** \long\def\ifempty#1{% \ifx W#1W\expandafter\firstoftwo \else \expandafter\secondoftwo \fi} 1) \ifempty{foobar}{vide}{pas vide}\qquad 2) \ifempty{}{vide}{pas vide}\qquad 2) \ifempty{\empty}{vide}{pas vide}\qquad 4) \ifempty{Wagons}{vide}{pas vide} ****************** Fin code ****************** ****************** Code 165 ****************** \long\def\ifempty#1{% \expandafter\ifx\expandafter\relax\detokenize{#1}\relax \expandafter\firstoftwo \else \expandafter\secondoftwo \fi } a) \ifempty{abc}{vide}{pas vide}\qquad b) \ifempty{}{vide}{pas vide}\qquad c) \ifempty{ }{vide}{pas vide}\qquad d) \ifempty{\relax}{vide}{pas vide}\qquad e) \edef\foo{\ifempty{abc}{vide}{pas vide}}\meaning\foo ****************** Fin code ****************** ****************** Code 166 ****************** \catcode`\@11 \def\reverse#1{% \ifempty{#1} {}% s'il n'y a rien à inverse, ne rien faire {\reverse@i#1\@nil\@nil}% initialisation des réservoirs et appeler \reverse@i } \def\reverse@i#1#2\@nil#3\@nil{% #1 est le 1er caractère du réservoir A \ifempty{#2}% si ce qui reste après ce 1er caractère est vide {#1#3}% renvoyer #1#3 qui est le résultat final {\reverse@i#2\@nil#1#3\@nil}% sinon, recommencer en déplaçant #1 % en 1re position du réservoir B } \catcode`\@12 a) \reverse{foobar}\qquad b) \edef\foo{\reverse{Bonjour}}\meaning\foo\qquad c) \reverse{Bonjour le monde}\qquad d) \reverse{Bonjour{ }le{ }monde} ****************** Fin code ****************** ****************** Code 167 ****************** \catcode`\@11 \def\ifin#1#2{% \def\ifin@i##1#2##2\@nil{% définit la macro auxiliaire \ifempty{##2}% si ce qu'il y a derrière le motif est vide \secondoftwo% aller à "faux" \firstoftwo% sinon à "vrai" }% \ifin@i#1#2\@nil% appel de la macro auxiliaire } \catcode`\@12 a) \ifin{abc\relax1}{bc}{vrai}{faux}\qquad b) \ifin{abc \relax1}{c\relax}{vrai}{faux}\qquad c) \ifin{abc \relax1}{ }{vrai}{faux}\qquad d) \ifin{abc \relax1}{}{vrai}{faux}\qquad e) \ifin{}{a}{vrai}{faux} ****************** Fin code ****************** ****************** Code 168 ****************** \catcode`\@11 \def\ifstart#1#2{% \def\ifstart@i##1#2##2\@nil{\ifempty{##1}}% \ifstart@i#1#2\@nil } \catcode`\@12 a) \ifstart{abc}{bc}{vrai}{faux}\qquad b) \ifstart{abc}{ab}{vrai}{faux}\qquad c) \ifstart{ abc}{ }{vrai}{faux}\qquad d) \ifstart{abc}{}{vrai}{faux}\qquad e) \ifstart{}{a}{vrai}{faux} ****************** Fin code ****************** ****************** Code 169 ****************** \catcode`\@11 \def\ifstart#1#2{% \def\ifstart@i##1#2##2\@nil{\ifempty{##1}}% \ifempty{#2}% si motif vide \firstoftwo% exécuter code "vrai" {\ifstart@i#1\relax#2\@nil}% sinon, aller à la macro auxiliaire } \catcode`\@12 a) \ifstart{abc}{bc}{vrai}{faux}\qquad b) \ifstart{abc}{ab}{vrai}{faux}\qquad c) \ifstart{ abc}{ }{vrai}{faux}\qquad d) \ifstart{abc}{}{vrai}{faux}\qquad e) \ifstart{}{a}{vrai}{faux} ****************** Fin code ****************** ****************** Code 170 ****************** \catcode`\@11 \def\ifend#1#2{% \def\ifend@i##1#2##2\@nil{% ##2 = ce qui reste après le motif \ifempty{##2}% si ##2 est vide \firstoftwo% exécuter l'argument "vrai" {\ifin{##2}{#2}% sinon, si ##2 contient le {\ifend@i##2\@nil}% appeler \ifend@i avec ce qui reste \secondoftwo% sinon, exécuter l'argument "faux" }% }% \ifend@i#2#1\@nil% appelle la macro récursive } \catcode`\@12 1) \ifend{abc de}{de}{vrai}{faux}\qquad 2) \ifend{abd de }{de}{vrai}{faux}\qquad 3) \ifend{abc de }{ }{vrai}{faux}\qquad 4) \ifend{}{a}{vrai}{faux}\qquad 5) \ifend{abc de}{}{vrai}{faux} ****************** Fin code ****************** ****************** Code 171 ****************** \catcode`\@11 \def\ifend#1#2{% \def\ifend@i##1#2##2\@nil{% ##2 = ce qui reste après le motif \ifempty{##2}% si ##2 est vide \firstoftwo% exécuter l'argument "vrai" {\ifin{##2}{#2}% sinon, si ##2 contient le {\ifend@i##2\@nil}% appeler \ifend@i avec ce qui reste \secondoftwo% sinon, exécuter l'argument "faux" }% }% \ifempty{#2}% si le motif est vide \firstoftwo% exécuter "vrai" {\ifend@i#2\relax#1\@nil}% sinon, appelle la macro récursive } \catcode`\@12 1) \ifend{abc de}{de}{vrai}{faux}\qquad 2) \ifend{abd de }{de}{vrai}{faux}\qquad 3) \ifend{abc de }{ }{vrai}{faux}\qquad 4) \ifend{}{a}{vrai}{faux}\qquad 5) \ifend{abc de}{}{vrai}{faux} ****************** Fin code ****************** ****************** Code 172 ****************** \catcode`\@11 \def\substin#1#2#3{% \def\substin@i##1{% \ifempty{##1}% si le est vide \relax% ne rien faire -> fin du processus {\ifin{##1}{#2}% sinon, si le contient {\substin@ii##1\@nil}% appeler la macro à argument délimités {##1}% sinon, afficher le }% }% \def\substin@ii##1#2##2\@nil{% ##1#3% afficher ##1 et #3 (qui est ) \substin@i{##2}% et recommencer avec ce qui reste }% \substin@i{#1}% } \catcode`\@12 a) \substin{abracadabra}{a}{A}\qquad b) \substin{abracadabra}{x}{W}\qquad c) \substin{abracadabra}{br}{}\qquad d) \substin{1\relax3}\relax{222}\qquad ****************** Fin code ****************** ****************** Code 173 ****************** \catcode`\@11 \def\substtocs#1#2#3#4{% \def\substtocs@i##1{% \ifempty{##1}% si le est vide \relax% ne rien faire -> fin du processus {\ifin{##1}{#3}% sinon, si le contient {\substtocs@ii##1\@nil}% appeler la macro à argument délimités {\addtomacro#1{##1}}% sinon, ajouter le }% }% \def\substtocs@ii##1#3##2\@nil{% \addtomacro#1{##1#4}% ajouter ##1#4 \substtocs@i{##2}% et recommencer avec ce qui reste }% \let#1=\empty% initialise la macro à vide \substtocs@i{#2}% } \catcode`\@12 \substtocs\foo{abracadabra}{a}{A}\meaning\foo\par \substtocs\foo{abracadabra}{x}{W}\meaning\foo\par \substtocs\foo{abracadabra}{br}{}\meaning\foo\par \substtocs\foo{1\relax3}\relax{222}\meaning\foo\par \substtocs\foo{\ifnum1=2 test vrai\fi}{2}{1}\meaning\foo ****************** Fin code ****************** ****************** Code 174 ****************** \def\majuscule#1{\uppercase{#1}} \def\majmot#1{\substin{\majuscule#1}{ }{ \majuscule}} \majmot{un petit texte}\par \majmot{Un grand texte sans importance} ****************** Fin code ****************** ****************** Code 175 ****************** \def\majuscule#1{\uppercase{#1}} \def\majmot#1{% \begingroup \substtocs\temp{\majuscule#1}{ }{ \majuscule}% \temp% exécute la macro temporaire \endgroup } \majmot{un petit texte}\par \majmot{Un grand texte Sans importance} ****************** Fin code ****************** ****************** Code 176 ****************** \catcode`\@11 \newcount\cnt@occ \def\cnttimes#1#2{% \def\cnttimes@i##1{% \ifempty{##1}% si le est vide {\number\cnt@occ}% afficher le nombre d'occurrences {\ifin{##1}{#2}% sinon, si le contient {\cnttimes@ii##1\@nil}% appeler la macro à argument délimités {\number\cnt@occ}% sinon, afficher le nombre d'occurrences }% }% \def\cnttimes@ii##1#2##2\@nil{% \advance\cnt@occ1 \cnttimes@i{##2}% et recommencer avec ce qui reste }% \cnt@occ=0 % initialise le compteur \cnttimes@i{#1}% } \catcode`\@12 a) \cnttimes{abracadabra}{a}\qquad b) \cnttimes{abracadabra}{ra}\qquad c) \cnttimes{abracadabra}{w}\qquad d) \cnttimes{abc def ghijk l }{ }\qquad e) \cnttimes{\ifnum1=2 vrai\else faux\fi}{\ifnum} ****************** Fin code ****************** ****************** Code 177 ****************** \catcode`\@11 \long\def\cnttimestocs#1#2#3{% #3=macro recevant le résultat \long\def\cnttimestocs@i##1\@nil##2#2##3\@nil{% % ##1=nb d'occurrences rencontrées jusqu'alors % ##2 et ##3=ce qui est avant/après le \ifin{##3}{#2}% si le est dans ce qui reste {\expandafter\cnttimestocs@i% appeler la macro récursive \number\numexpr##1+1\relax\@nil% avec une occurrence de plus ##3\@nil% et ce qui reste }% {\edef#3{\number\numexpr##1+1\relax}}% sinon, stocker 1 occurrence de plus dans #3 }% \ifin{#1}{#2}% si le est dans le {\cnttimestocs@i 0\@nil#1\@nil}% appeler la macro récursive avec 0 occurrence {\def#3{0}}% sinon, mettre 0 dans #3 } \catcode`\@12 a) \cnttimestocs{abracadabra}{a}\foo \meaning\foo\qquad b) \cnttimestocs{abracadabra}{ra}\foo \meaning\foo\qquad c) \cnttimestocs{abracadabra}{w}\foo \meaning\foo\qquad d) \cnttimestocs{abc def ghijk l }{ }\foo \meaning\foo\qquad e) \cnttimestocs{\ifnum1=2 vrai\else faux\fi}{\ifnum}\foo \meaning\foo ****************** Fin code ****************** ****************** Code 178 ****************** \catcode`\@11 \def\multisubst#1#2{% \def\subst@code{#1}% stocke le \multisubst@i#2\@nil% appelle la macro récursive avec la liste de motifs } \def\multisubst@i#1#2#3\@nil{% #1 et #2=paire de motifs #3=motifs restants \expandafter\substtocs\expandafter\subst@code\expandafter% 1-développe {\subst@code}{#1}{#2}% le et effectue la substitution en cours \ifempty{#3}% si la liste des motifs est vide \subst@code% exécuter le obtenu {\multisubst@i#3\@nil}% recommencer avec les motifs qui restent } \catcode`\@12 1) \multisubst{abracadabra}{aA rR}\par 2) \multisubst{Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es catalanes, corses ou grecques assez inattendues.}{a{AA} ya uy ou io ei {AA}e} ****************** Fin code ****************** ****************** Code 179 ****************** \catcode`\@11 \def\quark{\quark} \def\multisubst#1#2{% #1 = #2 = \def\subst@code{#1}% stocke le \multisubst@i#2\quark\quark% appelle la macro récursive avec % 2 arguments spéciaux en fin de liste } \def\multisubst@i#1#2{% #1#2 = paire de motifs en cours \def\arg@a{#1}% stocke le dans une macro \ifx\arg@a\quark% si le motif spécial est atteint \expandafter\subst@code% exécuter le code obtenu \else \expandafter\substtocs\expandafter\subst@code\expandafter% 1-développe {\subst@code}{#1}{#2}% le et effectue la substitution en cours \expandafter\multisubst@i% puis lis la paire de motifs suivants \fi } \catcode`\@12 \multisubst{abracadabra}{aA rR}\par \multisubst{Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es catalanes, corses ou grecques assez inattendues.}{a{AA} ya uy ou io ei {AA}e} ****************** Fin code ****************** ****************** Code 180 ****************** \iftrue foobar\else ceci n'est jamais lu par \TeX\fi\par \iffalse ceci n'est jamais lu par \TeX\else foobar\fi ****************** Fin code ****************** ****************** Code 181 ****************** \newif\ifhomme \def\debutlettre{Ch\ifhomme er Monsieur\else ère Madame\fi}% \hommetrue \debutlettre \medbreak \hommefalse \debutlettre ****************** Fin code ****************** ****************** Code 182 ****************** \catcode`\*=13 \catcode`\+=13 % "*" et "+" sont actifs 1) \def*{xyz}\def+{abc}% définit les caractères actifs \ifcat *+vrai\else faux\fi\qquad 2) \ifcat \noexpand *\noexpand +vrai\else faux\fi\qquad 3) \def\foo{foobar}% \ifcat \noexpand\foo\relax vrai\else faux\fi\qquad% \foo et \relax sont vus égaux 4) \ifcat = vrai\else faux\fi\qquad% "=" et " " n'ont pas le même catcode 5) \ifcat _^vrai\else faux\fi\qquad% "_" et "^" n'ont pas le même catcode 6) \let\foo=& \ifcat &\foo vrai\else faux\fi% "&" et \foo (let-égal à "&") sont vus égaux ****************** Fin code ****************** ****************** Code 183 ****************** \catcode`\@11 \def\ifcs#1{% \ifcat\noexpand#1\relax\expandafter\firstoftwo \else \expandafter\secondoftwo \fi } \catcode`\@12 1) \def\foo{bar}\ifcs\foo{vrai}{faux}\qquad 2) \ifcs\def{vrai}{faux}\qquad 3) \ifcs A{vrai}{faux}\qquad \catcode`\*=13 4) \let*=W \ifcs*{vrai}{faux}\qquad 5) \let*=\relax \ifcs*{vrai}{faux}\qquad 6) \let\foo=A \ifcs\foo{vrai}{faux}\qquad 7) \ifcs\bgroup{vrai}{faux} ****************** Fin code ****************** ****************** Code 184 ****************** 1) \def\foo{xxy}\def\bar{aab} \if\foo\bar vrai\else faux\fi\qquad 2) \if aA vrai\else faux\fi\qquad% "a" et "A" n'ont pas les mêmes charcode 3) \if\relax\noexpand\foo vrai\else faux\fi\qquad% \relax et \foo ont un charcode=256 4) \let\foo=&\if\foo &vrai\else faux\fi\qquad% \foo est vue comme "&" 5) \if\string~\noexpand~vrai\else faux\fi ****************** Fin code ****************** ****************** Code 185 ****************** \catcode`\@11 \def\ifcs#1{% \begingroup \escapechar=`\@ % prend "@" comme caractère d'échappement \if% les premiers caractères de \expandafter\firstto@nil\string#1a\@nil% "#1a" \expandafter\firstto@nil\string\relax\@nil% et "\relax" sont-ils égaux ? \endgroup\expandafter\firstoftwo% si oui, fermer le groupe et renvoyer "vrai" \else% sinon, fermer le groupe et renvoyer "faux" \endgroup\expandafter\secondoftwo \fi } \catcode`\@12 1) \def\foo{bar}\ifcs\foo{vrai}{faux}\qquad 2) \ifcs\def{vrai}{faux}\qquad 3) \ifcs A{vrai}{faux}\qquad \catcode`\*=13 4) \let*=W \ifcs*{vrai}{faux}\qquad 5) \let*=\relax \ifcs*{vrai}{faux}\qquad 6) \let\foo=A \ifcs\foo{vrai}{faux}\qquad 7) \ifcs\bgroup{vrai}{faux} ****************** Fin code ****************** ****************** Code 186 ****************** \edef\specialrelax{\ifnum1=1\fi} \meaning\specialrelax ****************** Fin code ****************** ****************** Code 187 ****************** \edef\specialrelax{\ifnum1=1\fi}% texte de remplacement = \relax spécial \edef\normalrelax{\relax}% texte de remplacement = \relax normal \meaning\specialrelax\par \meaning\normalrelax\par Les 2 \string\relax{} sont \ifx\specialrelax\normalrelax identiques\else différents\fi. ****************** Fin code ****************** ****************** Code 188 ****************** \edef\specialrelax{\ifnum1=1\fi} \expandafter\def\specialrelax{foobar}% redéfinition un \relax spécial ****************** Fin code ****************** ****************** Code 189 ****************** \catcode`\@11 \def\ifnumcase#1{% \begingroup \def\elseif{\elseif}\def\endif{\endif}% définir deux "quarks" locaux \def\nombre@{#1}% stocker le nombre à comparer dans \nombre@ \ifnumcase@i% appeller la macro récursive } \def\ifnumcase@i#1{% \def\nxt@arg{#1}% stocker l'argument suivant \ifx\nxt@arg\elseif% si c'est \elseif \def\next@todo{\endgroup\idto@endif}% fermer le groupe et aller à \idto@endif \else \ifx\nxt@arg\endif% si c'est \endif \let\next@todo\endgroup% fermer le groupe \else \ifnum\nombre@=\nxt@arg% s'il y a égalité \def\next@todo{\endgroup\firstto@endif}% fermer le groupe puis \firstto@endif \else% sinon \let\next@todo=\ifnumcase@ii% aller à \ifnumcase@ii \fi \fi \fi \next@todo% exécuter l'action décidée ci-dessus } \def\ifnumcase@ii#1{\ifnumcase@i} \def\idto@endif#1\endif{#1} \def\firstto@endif#1#2\endif{#1} \catcode`\@12 \def\swaptwo#1#2{#2#1} \def\testenombre#1{% \ifnumcase{#1} {1}{C'est "un" et je prends le premier argument: \firstoftwo} {3}{J'ai vu un "trois" et je mange les deux arguments : \gobtwo} {15}{L'argument est "15" et je prends le second argument : \secondoftwo} \elseif ni 1, ni 3, ni 5 et j'inverse les deux arguments : \swaptwo \endif } \testenombre{3}{foo}{bar}\par \testenombre{16}{foo}{bar}\par \testenombre{15}{foo}{bar}\par \testenombre{1}{foo}{bar} ****************** Fin code ****************** ****************** Code 190 ****************** \catcode`\@11 \def\endif{\endif}\def\elseif{\elseif}% définit les quarks \def\ifnumcase#1#2{% #1= #2=prochain argument \ifx\elseif#2% si #2 est \elseif \expandafter\firstoftwo\else\expandafter\secondoftwo\fi \idto@endif% aller à \idto@endif, sinon : {\ifx\endif#2% si #2 est \endif \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {}% ne rien faire. Sinon #2 est une : {\ifnum#1=#2 % s'il y a égalité entre et \expandafter\firstoftwo\else\expandafter\secondoftwo\fi \firstto@endif% aller à \firstto@endif {\ifnumcase@i{#1}}% sinon aller à \ifnumcase@i }% }% } \def\ifnumcase@i#1#2{% #1= #2= à suivre qui est mangé \ifnumcase{#1}% retourner à \ifnumcase en transmettant #1 } \def\idto@endif#1\endif{#1} \def\firstto@endif#1#2\endif{#1} \catcode`\@12 \def\swaptwo#1#2{#2#1} \def\testenombre#1{% \ifnumcase{#1} {1}{C'est "un" et je prends le premier argument: \firstoftwo} {3}{J'ai vu un "trois" et je mange les deux arguments : \gobtwo} {15}{L'argument est "15" et je prends le second argument : \secondoftwo} \elseif ni 1, ni 3, ni 5 et j'inverse les deux arguments : \swaptwo \endif } \testenombre{3}{foo}{bar}\par \testenombre{16}{foo}{bar}\par \testenombre{15}{foo}{bar}\par \edef\macro{\testenombre{1}{foo}{bar}}\meaning\macro ****************** Fin code ****************** ****************** Code 191 ****************** \catcode`\@11 \def\endif{\endif}\def\elseif{\elseif}% \def\ifxcase#1#2{% #1= #2=prochain argument \ifx\elseif#2% si #2 est \elseif \expandafter\firstoftwo\else\expandafter\secondoftwo\fi \idto@endif% aller à \idto@endif, sinon : {\ifx\endif#2% si #2 est \endif \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {}% ne rien faire. Sinon #2 est un : {\ifx#1#2% s'il y a égalité entre et \expandafter\firstoftwo\else\expandafter\secondoftwo\fi \firstto@endif% aller à \firstto@endif {\ifxcase@i{#1}}% sinon aller à \ifnumcase@i }% }% } \def\ifxcase@i#1#2{% #1= #2= à suivre (qui est mangé) \ifxcase{#1}% retourner à \ifxcase en transmettant #1 } \def\idto@endif#1\endif{#1} \def\firstto@endif#1#2\endif{#1} \catcode`\@12 \def\testtoken#1{% \ifxcase{#1} a{Le token est "a"} +{J'ai vu un "+"} \relax{L'argument est "\string\relax"} \elseif Tous les tests sont négatifs% \endif } 1) \testtoken a\par 2) \let\foo=a\testtoken\foo\par 3) \testtoken+\par 4) \testtoken\relax\par 5) \edef\foo{\testtoken\relax}\meaning\foo\par 6) \edef\foo{\testtoken W}\meaning\foo ****************** Fin code ****************** ****************** Code 192 ****************** \newcount\xx % définit un compteur utilisé dans la boucle \def\compte#1{% \xx=0 % initialise le compteur \loop% début de la boucle \advance\xx1 % incrémente le compteur \ifnum\xx<#1 % s'il est inférieur à la borne \number\xx , % affiche le nombre et la virgule \repeat% et recommence \ifnum#1>0 \number\xx\relax\fi% si le nombre #1 est >0, afficher le dernier nombre } a) \compte{1}.\qquad b) \compte{-3}.\qquad c) \compte{7}. ****************** Fin code ****************** ****************** Code 193 ****************** \catcode`\@11 \newcount\cnt@repeat % définit le compteur de \xrepeat \def\xloop{% \def\xiterate{}% initialiser \xiterate à vide \cnt@repeat\z@% initialiser le compteur de \xrepeat \xloop@i% aller à la macro récursive } \long\def\xloop@i#1\xrepeat{% \addtomacro\xiterate{#1}% ajoute ce qui est avant le premier \xrepeat \exparg\cnttimestocs{\xiterate}\xloop\cnt@loop % combien de \xloop dans \xiterate \ifnum\cnt@loop=\cnt@repeat\relax \expandafter\firstoftwo\else\expandafter\secondoftwo \fi {% autant que de \xrepeat -> \detokenize pour afficher "\detokenize\expandafter{\xiterate}"% } {\addtomacro\xiterate\xrepeat% sinon, ajouter ce \xrepeat \advance\cnt@repeat by 1% incrémenter le compteutr de \xrepeat \xloop@i% et chercher le prochain \xrepeat }% } \let\xrepeat\fi \catcode`\@12 \xloop a\xloop b\xloop 12\xrepeat c\xrepeat \xloop X\xrepeat\xrepeat ****************** Fin code ****************** ****************** Code 194 ****************** \catcode`\@11 \newcount\cnt@repeat \newcount\cnt@nested \cnt@nested=0 % compteur d'imbrications \def\xloop{% \global\advance\cnt@nested by 1 % augmente le compteur d'imbrications \expandafter\def\csname xiterate@\number\cnt@nested\endcsname{}% \cnt@repeat\z@% initialise le compteur de \xrepeat à 0 \xloop@i% aller à la macro \xloop@i } \long\def\xloop@i#1\xrepeat{% \expandafter\addtomacro\csname xiterate@\number\cnt@nested\endcsname{#1}% \expandafter\expandafter\expandafter\cnttimestocs\expandafter\expandafter\expandafter {\csname xiterate@\number\cnt@nested\endcsname}\xloop\cnt@loop \ifnum\cnt@loop=\cnt@repeat\relax \expandafter\firstoftwo\else\expandafter\secondoftwo \fi {\expandafter\eaddtomacro\csname xiterate@\number\cnt@nested\expandafter\endcsname {\expandafter\expandafter\csname xiterate@\number\cnt@nested\endcsname\fi}% %\expandafter\show\csname xiterate@\number\cnt@nested\endcsname \csname xiterate@\number\cnt@nested\endcsname \letname{xiterate@\number\cnt@nested}\relax \global\advance\cnt@nested by -1 } {\expandafter\addtomacro\csname xiterate@\number\cnt@nested\endcsname\xrepeat \advance\cnt@repeat by 1 \xloop@i }% }% \let\xrepeat\fi \catcode`\@12 \newcount\cntxx \cntxx=1 % compteur des lignes \newcount\cntyy \xloop \cntyy=1 % compteur des colonnes \xloop (\number\cntxx,\number\cntyy)% affiche "(ligne,colonne)" \ifnum\cntyy<5 % tant que colonne<5 \advance\cntyy1 % incrémente colonne , % <- affiche ", " \xrepeat% et recommence \ifnum\cntxx<3 % tant que ligne<3 \advance\cntxx1 % incrémente ligne \par % va à la ligne \xrepeat% et recommence ****************** Fin code ****************** ****************** Code 195 ****************** \catcode`\@11 \def\end@foreach{\end@foreach}% définit un quark \long\def\doforeach#1\in#2#3{% \def\loop@code{#3}% assigne le à \loop@code % appel à \doforeach@i : mettre la macro #1 en premier, puis la liste de valeurs #2 \doforeach@i#1#2,\end@foreach,% ajouter à la fin ",\end@foreach," % une fois les boucles finies, neutraliser les macros déjà définies \let#1\relax \let\loop@code\relax } \long\def\doforeach@i#1#2,{% #1=\ #2=valeur courante \def#1{#2}% stocke la valeur en cours dans la \ \unless\ifx\end@foreach#1% si \end@foreach n'est pas atteint \antefi% amène le \fi ici \loop@code% exécute le code \doforeach@i#1% et recommencer \fi } \catcode`@12 \doforeach\x\in{a,bcd,{efg},hi}{\meaning\x.\par} ****************** Fin code ****************** ****************** Code 196 ****************** \catcode`\@11 \def\end@foreach{\end@foreach} \def\defseplist#1{% \long\def\doforeach##1\in##2##3{% \def\loop@code{##3}% assigne le à \loop@code \doforeach@i##1##2#1\end@foreach#1% ajouter ",\end@foreach," et aller à \doforeach@i % après être sorti des boucles, neutraliser les macros déjà définies \let\loop@code\relax \let##1\relax }% \long\def\doforeach@i##1##2#1{% \def##1{##2}% stocke la valeur en cours dans la \ \unless\ifx\end@foreach##1% si la fin n'est pas atteinte \antefi% amène le \fi ici \loop@code% exécute le code \doforeach@i##1% et recommencer \fi }% } \defseplist{,}% définit les macros avec le séparateur par défaut \catcode`@12 \doforeach\x\in{a,bcd,efg}{Argument courant : "\x".\par}\medbreak \defseplist{--}% séparateur "--" \doforeach\nn\in{4,19--0,5--8,575}{Nombre lu : "\nn"\par} ****************** Fin code ****************** ****************** Code 197 ****************** \catcode`\@11 \newcount\cnt@nest \cnt@nest=0 % définit et initialise le compteur d'imbrication \def\end@foreach{\end@foreach} \def\defseplist#1{% \long\def\doforeach##1\in##2##3{% \global\advance\cnt@nest1 % entrée de boucle : incrémenter le compteur d'imbrication \defname{loop@code@\number\cnt@nest}{##3}% assigne le à \loop@code@ \doforeach@i##1##2#1\end@foreach#1% ajouter ",\end@foreach," et aller à \doforeach@i % une fois fini : \let##1\empty% dans ce cas, neutraliser les macros déjà définies \letname{loop@code@\number\cnt@nest}\empty% \global\advance\cnt@nest-1 %sortie de boucle : décrémenter le compteur d'imbrication }% \long\def\doforeach@i##1##2#1{% \def##1{##2}% stocke la valeur en cours dans la \ \unless\ifx\end@foreach##1% tant que la fin n'est pas atteinte \antefi% amène le \fi ici \csname loop@code@\number\cnt@nest\endcsname% exécute le code \doforeach@i##1% et recommencer \fi }% } \catcode`@12 \defseplist{,} \doforeach\xxx\in{1,2,3}{% Ligne \xxx{} : \doforeach\yyy\in{a,b,c,d,e}{(\xxx,\yyy)}.\par } ****************** Fin code ****************** ****************** Code 198 ****************** \catcode`\@11 \newcount\cnt@nest \cnt@nest=0 % définit et initialise le compteur d'imbrication \def\end@foreach{\end@foreach} \long\def\save@macro#1{% sauvegarde la macro #1 \ifdefined#1% si la macro #1 est déjà définie... \letname{saved@var@\number\cnt@nest}#1% ...la sauvegarder \fi } \long\def\restore@macro#1{% restaure la macro #1 % le \csname donne \relax si la sauvegarde n'a pas été faite \expandafter\let\expandafter#1\csname saved@var@\number\cnt@nest\endcsname } \def\defseplist#1{% \long\def\doforeach##1\in##2##3{% \global\advance\cnt@nest1 % entrée de boucle : incrémenter le compteur d'imbrication \save@macro##1% sauvegarde la macro ##1 \defname{loop@code@\number\cnt@nest}{##3}% assigne le à \loop@code@ \doforeach@i##1##2#1\end@foreach#1% ajouter ",\end@foreach," et aller à \doforeach@i % une fois fini, neutraliser la macro contenant le \letname{loop@code@\number\cnt@nest}\empty \restore@macro##1% restaurer la \ \global\advance\cnt@nest-1 % et décrémenter le compteur d'imbrication }% \long\def\doforeach@i##1##2#1{% \def##1{##2}% stocke la valeur en cours dans la \ \unless\ifx\end@foreach##1% si la fin n'est pas atteinte \antefi% amène le \fi ici \csname loop@code@\number\cnt@nest\endcsname% exécute le code \doforeach@i##1% et recommencer \fi% }% } \catcode`@12 \defseplist{,} \doforeach\par\in{a,b,c,y,z}{$\par_i$} \meaning\par% \par doit être redevenu une primitive \doforeach\xxx\in{1,2,3}{% Ligne \xxx{} : \doforeach\yyy\in{a,b,c,d,e}{(\xxx,\yyy)}.\par } ****************** Fin code ****************** ****************** Code 199 ****************** \let\foo=\djfhdsldv% \foo est rendue \let-égale à une macro indéfinie a) \meaning\foo\par % puis \djfhdsldv est rendue \let-égale à \foo (ou \foo est construit avec \csname) \expandafter\let\expandafter\djfhdsldv\csname foo\endcsname b) \meaning\djfhdsldv ****************** Fin code ****************** ****************** Code 200 ****************** \catcode`\@11 \newcount\cnt@nest \cnt@nest=0 % définit et initialise le compteur d'imbrication \def\end@foreach{\end@foreach} \long\def\save@macro#1#2{\letname{saved@var@\number\cnt@nest#1}#2} \long\def\save@macro@i#1/#2{\save@macro{a}#1\save@macro{b}#2} \long\def\restore@macro#1#2{% \expandafter\let\expandafter#2\csname saved@var@\number\cnt@nest#1\endcsname } \long\def\restore@macro@i#1/#2{\restore@macro{a}#1\restore@macro{b}#2} \def\defseplist#1{% \long\def\doforeach##1\in##2##3{% \global\advance\cnt@nest1 % entrée de boucle : incrémenter le compteur d'imbrication \global\let\allow@recurse\identity% permet l'appel récursif plus bas \ifin{##1}{/}% si ##1 contient "/" {\save@macro@i##1% sauvegarde les macros \doforeach@ii% appeler la macro récursive avec les arguments {##3}% 1) code à exécuter ##1% 2) variables sous la forme \/\ ##2#1\end@foreach/\end@foreach#1% puis la liste ##2 suivie de % ",\end@foreach/\end@foreach," \restore@macro@i##1% une fois sorti de toutes les boucles, restaurer les macros }% si ##1 ne contient pas "/" {\save@macro{}##1% sauvegarde la macro \doforeach@i% appeler la macro récursive {##3}% mettre en premier le ##1% puis la variable ##1 en 2e position ##2#1\end@foreach#1% enfin la liste ##2 suivie de ",\end@foreach," \restore@macro{}##1% une fois sorti de toutes les boucles, restaurer la macro }% \global\advance\cnt@nest-1 % décrémente le compteur d'imbrications }% % ##1 = code à exécuter, ##2= variable, ##3=valeur courante \long\def\doforeach@i##1##2##3#1{% \ifx\end@foreach##3% si la fin est atteinte \expandafter\gobone\else\expandafter\identity\fi% manger sinon exécuter: {\def##2{##3}% fait l'assignation à la variable ##1% le code puis \allow@recurse{\doforeach@i{##1}##2}% recommencer }% }% % ##1 = code à exécuter, ##2/##3= variables, ##4/##5=valeurs courantes \long\def\doforeach@ii##1##2/##3##4/##5#1{% \ifx\end@foreach##4% si la fin est atteinte \expandafter\gobone\else\expandafter\identity\fi% manger sinon exécuter: {\def##2{##4}\def##3{##5}% fait l'assignation des deux variables ##1% le code puis \allow@recurse{\doforeach@ii{##1}##2/##3}% recommencer }% }% % macro qui, si elle remplace \allow@recurse, annule l'appel récursif \long\def\forbid@recurse##1\end@foreach#1{}% tout manger jusqu'à "\end@foreach," } \def\doforeachexit{\global\let\allow@recurse\forbid@recurse} \catcode`\@12 \defseplist{,} \doforeach\par\in{a,b,c,y,z}{$\par_i$}\medskip \doforeach\sujet/\terminaison\in{Je/e,Tu/es,Il/e,Nous/ons,Vous/ez,Ils/ent} {\sujet\ programm\terminaison{} en \TeX\par}\medskip Les voyelles lues sont : \doforeach\ii\in{a,e,i,o,u,y}{\ii\if o\ii\doforeachexit\fi}.\medskip \doforeach\xx\in{a,b,c,d,e,f} {\doforeach\ii\in{1,2,3,4}{\xx\ii{} \ifnum\ii=3 \doforeachexit\fi}\par} ****************** Fin code ****************** ****************** Code 201 ****************** \newdimen\dimA \newdimen\dimB% alloue deux registres de dimension a) \dimA=59.5pt \the\dimA\qquad% doit afficher 59.5pt b) \dimA=1.5cm \the\dimA\qquad% convertit à l'affichage 1.5cm en pt c) \dimB=7pt % assigne 7pt à \dimB \dimA\dimB% rend \dimA égal à dimB \the\dimA\qquad d) \dimA=6\dimB% rend \dimA égal à 6 fois \dimB \the\dimA ****************** Fin code ****************** ****************** Code 202 ****************** \newdimen\foo \foo=25pt % assigne 25pt au registre \foo \divide\foo 4 % le multiplie par 4 \advance\foo1.72pt % lui ajoute 1.72pt \multiply\foo3 % le multiplie par 3 \the\foo% affiche la dimension obtenue ****************** Fin code ****************** ****************** Code 203 ****************** \begingroup% ouvrir un groupe \edef\temp{\endgroup\def\noexpand\removept##1.##2\string p\string t}% \temp{#1\ifnum#2>0 .#2\fi}% et le fermer avant de définir \removept \newdimen\foo a) \foo=15pt \expandafter\removept\the\foo\qquad b) \foo=3.14pt \expandafter\removept\the\foo ****************** Fin code ****************** ****************** Code 204 ****************** \def\dimtodec{\expandafter\removept\the} \newdimen\foo a) \foo=15pt \dimtodec\foo \qquad b) \foo=3.14pt \dimtodec\foo ****************** Fin code ****************** ****************** Code 205 ****************** a) \the\dimexpr 1cm + 0.5cm\relax \qquad b) \the\dimexpr 1pt + 1pt\relax \qquad c) \the\dimexpr 1pt + 2pt * 3\relax\qquad d) \the\dimexpr (1pt + 2pt) * 3\relax\qquad e) \the\dimexpr (1.2pt + 0.8pt) * 5\relax\qquad f) \newdimen\foo \foo=15pt \the\dimexpr\foo-(\foo + 1pt) / 4\relax ****************** Fin code ****************** ****************** Code 206 ****************** \the\dimexpr0,7pt + 0.4pt\relax ****************** Fin code ****************** ****************** Code 207 ****************** a) \newdimen\foodim \foodim=1cm \number\foodim\relax\qquad b) \number\dimexpr 0.7pt + 0.4pt\relax\qquad c) \number\dimexpr 1.1pt\relax ****************** Fin code ****************** ****************** Code 208 ****************** \catcode`\@11 \def\FOR#1=#2to#3\do#4#{% \ifempty{#4} {\let\FOR@increment\z@} {\edef\FOR@increment{\the\dimexpr#4pt\relax}}% lit et normalise l'argument optionnel \ifdim\FOR@increment=\z@% s'il est nul, \edef\FOR@increment{% le redéfinir à -1pt (si #3<#2) et 1pt sinon \ifdim\dimexpr#3pt-#2pt\relax<\z@ -1\else 1\fi pt }% \FOR@increment vaut donc 1 ou -1 \fi \ifdim\dimtodec\dimexpr#3pt-#2pt\relax\dimexpr\FOR@increment\relax<\z@ \expandafter\gobone% si argument optionnel incompatible, manger le {} \else \edef#1{\dimtodec\dimexpr#2pt\relax}% initialise la \ \edef\macro@args{% définit et développe les arguments à passer à \FOR@i %#1=nom de la macro récursive : \expandafter\noexpand\csname FOR@ii@\string#1\endcsname \ifdim\FOR@increment<\z@ <\else >\fi% #2=signe de comparaison {\FOR@increment}% #3=incrément \noexpand#1% #4=\ {\the\dimexpr#3pt\relax}% #5=dimension n2 }% \antefi% externalise la ligne ci-dessous de la portée du test \expandafter\FOR@i\macro@args% appelle \FOR@i avec les arguments définis ci-dessus \fi } % #1=nom de la macro récursive de type "\FOR@ii@\" % #2=signe de comparaison % #3=incrément % #4=\ % #5=dimension n2 % #6= à exécuter \long\def\FOR@i#1#2#3#4#5#6{% \def#1{% définit la sous macro récursive \unless\ifdim#4pt#2#5\relax% tant que la \ variable n'a pas dépassé n2 \afterfi{% rendre la récursivité terminale #6% exécute le code \edef#4{\dimtodec\dimexpr#4pt+#3\relax}% incrémente la \ #1% recommence }% \fi }% #1% appelle la sous-macro récursive }% \def\exitFOR#1{% #1=\ correspondant à la boucle de laquelle on veut sortir \defname{FOR@ii@\string#1}{}% } \def\ifexitFOR#1{% envoie vrai si on est prématurément sorti de la boucle de \ #1 % si la macro récursive est \empty \expandafter\ifx\csname FOR@ii@\string#1\endcsname\empty \expandafter\firstoftwo% c'est qu'on est sortir prématurément, renvoyer "vrai" \else \expandafter\secondoftwo% sinon, renvoyer "faux" \fi } \catcode`\@=12 a) \FOR\xx=1.5 to -5\do-1{"\xx" }\par b) \FOR\ii=1 to 2.742\do.25{"\ii" }\par c) \FOR\ii=0 to 1\do0.1{"\ii" \ifdim\ii pt>0.5pt \exitFOR\ii\fi} ****************** Fin code ****************** ****************** Code 209 ****************** \newdimen\foo \foo=15.2pt \foo=1.5\foo \the\foo% vaut 15.2*1.5 ****************** Fin code ****************** ****************** Code 210 ****************** \catcode`\@11 \newdimen\dim@a \def\decmul#1#2{% \dim@a=#2pt % range la dimension #2pt dans le registre de dimension \dim@a=#1\dim@a% multiplier le registre par le décimal #1 \dimtodec\dim@a% convertir la dimension en décimal } \catcode`\@12 a) \decmul{15.2}{1.5}\qquad b) \decmul{48.2}{.375} ****************** Fin code ****************** ****************** Code 211 ****************** \def\decmul#1#2{\dimtodec\dimexpr#1\dimexpr#2pt\relax\relax} a) \decmul{15.2}{1.5}\qquad b) \edef\foo{\decmul{48.2}{.375}}\meaning\foo ****************** Fin code ****************** ****************** Code 212 ****************** \def\decdiv#1#2{% divise le décimal #1 par le décimal #2 \dimtodec \dimexpr \numexpr \dimexpr #1pt \relax * 65536 / \dimexpr #2pt \relax \relax sp \relax } 1) \decdiv{4.5}{0.075}\qquad% doit donner 60 2) \decdiv{8}{0.1}\qquad% doit donner 80 3) \decdiv{3.14}{1.6}\qquad% doit donner 1.9625 4) \decdiv{687.59829}{5.29871}\qquad% doit donner 129.76706 4) \edef\foo{\decdiv{0.37}{2.5}}\foo% doit donner 0.148 ****************** Fin code ****************** ****************** Code 213 ****************** \def\convertunit#1#2{% \dimtodec \dimexpr \numexpr \dimexpr #1 \relax * 65536 / \dimexpr 1#2 \relax \relax sp \relax } a) \convertunit{15cm}{mm}\qquad b) \convertunit{9,14in}{cm}\qquad c) \convertunit{100000sp}{mm}\qquad d) \convertunit{160.5pt}{cm}\qquad e) \edef\foo{\convertunit{2,5cm}{cc}}\meaning\foo ****************** Fin code ****************** ****************** Code 214 ****************** \the\glueexpr 5pt plus 2pt minus 1.5pt + 7pt plus0.5pt minus 3pt\relax \par \the\glueexpr 25pt plus2fil + 35pt plus 0.1fill\relax ****************** Fin code ****************** ****************** Code 215 ****************** foo% les caractères font entrer TeX en mode horizontal \kern0.5cm % espace insérée en mode horizontal bar\par% \par fait passer en mode vertical \kern0.5cm % espace insérée en mode vertical boo ****************** Fin code ****************** ****************** Code 216 ****************** Une première ligne\par \nointerlineskip% n'insère pas de ressort d'interligne ici Un second paragraphe constitué de plusieurs lignes. Un second paragraphe constitué de plusieurs lignes. Un second paragraphe constitué de plusieurs lignes. \par% le ressort d'interligne sera inséré ici Une dernière ligne ****************** Fin code ****************** ****************** Code 217 ****************** \begingroup \offinterlineskip La macro \litterate-\offinterlineskip-, en modifiant de façon appropriée les trois primitives \litterate-\baselineskip-, \litterate-\lineskip- et \litterate-\lineskiplimit-, rend les boites consécutives jointives. On peut constater dans ces deux paragraphes où \litterate-\offinterlineskip- a été appelée, que les lignes sont placées verticalement au plus près les unes des autres ce qui rend la lecture très pénible et démontre que le ressort d'interligne est une nécessité typographique !\par \endgroup Désactiver le ressort d'interligne ne se justifie que lorsque l'on doit composer des boites contenant autre chose que du texte, sauf à vouloir des effets typographiques spéciaux. ****************** Fin code ****************** ****************** Code 218 ****************** \baselineskip=12pt début \vtop{\hbox{ligne du haut ligne du haut ligne du haut} \hbox{ligne du bas ligne du bas ligne du bas} } \par Et la suite du texte suite du texte suite du texte ****************** Fin code ****************** ****************** Code 219 ****************** \baselineskip=12pt début \vtop{\hbox{ligne du haut ligne du haut ligne du haut} \hbox{ligne du bas ligne du bas ligne du bas} \xdef\sprevdepth{\the\prevdepth}% sauvegarde la valeur de \prevdepth } \par\prevdepth=\sprevdepth\relax% ment sur la boite précédente Et la suite du texte suite du texte suite du texte ****************** Fin code ****************** ****************** Code 220 ****************** \def\dummytext{Voici un texte qui ne revêt aucune importance et dont le seul but est de meubler artificiellement un paragraphe. } \begingroup% à l'intérieur d'un groupe, \leftskip=0pt plus 0.5\hsize\relax \rightskip=\leftskip\relax % modifier les ressort d'extrémités \spaceskip=0.3em plus 0.05em\relax% dimension de l'espace intermot \parfillskip=0pt \relax% annuler le ressort de fin de paragraphe \dummytext\dummytext\dummytext% corps du paragraphe \par% compose la paragraphe courant Juste une phrase% \par% compose la paragraphe courant \endgroup% avant de sortir du groupe ****************** Fin code ****************** ****************** Code 221 ****************** \def\narrowtext{% \par \begingroup \advance\leftskip 0.1\hsize \advance\rightskip 0.1\hsize } \def\endnarrowtext{\par\endgroup} \def\dummytext{Ceci est un texte sans importance destiné à meubler un paragraphe. } \dummytext\dummytext\dummytext \narrowtext \dummytext\dummytext\dummytext \narrowtext \dummytext\dummytext\dummytext \endnarrowtext \dummytext\dummytext\dummytext \endnarrowtext ****************** Fin code ****************** ****************** Code 222 ****************** a) \hfill Composition au fer à droite\par b) Composition au fer à gauche\hfill\kern0pt\par c) \hfill Centrage\hfill\kern0pt\par d) Des \hfill mots\hfill régulièrement\hfill espacés ****************** Fin code ****************** ****************** Code 223 ****************** a% le caractère "a" fait passer en mode horizontal \hbox{b}c\hbox{d}% les \hbox sont ajoutées en mode horizontal \medbreak ...à comparer avec... \medbreak a \par% \par fait passer en mode vertical \hbox{b}% \hbox est ajoutée à la liste verticale c% "c" fait passer en mode horizontal \hbox{d}% la \hbox est ajoutée en mode horizontal ****************** Fin code ****************** ****************** Code 224 ****************** Ligne de base : .........% \vbox{\hbox{ligne du haut}\hbox{ligne du milieu}\hbox{ligne du bas}}% .........% \vtop{\hbox{ligne du haut}\hbox{ligne du milieu}\hbox{ligne du bas}}......... ****************** Fin code ****************** ****************** Code 225 ****************** \def\dummytext{Bla, bla, bla, un texte sans importance. } Ligne de base.........% \vbox{\hsize=4cm \dummytext\dummytext}% .........% \vtop{\hsize=4cm \dummytext\dummytext}......... ****************** Fin code ****************** ****************** Code 226 ****************** \newbox\foobox a) \setbox\foobox=\hbox{foobar}% registre non vide Le registre est \ifvoid\foobox vide\else non vide\fi\par b) \setbox\foobox=\hbox{foobar} "\box\foobox" et le registre est \ifvoid\foobox vide\else non vide\fi\par c) \setbox\foobox=\hbox{foobar} "\copy\foobox" et le registre est \ifvoid\foobox vide\else non vide\fi\par d) \setbox\foobox=\hbox{} Le registre est \ifvoid\foobox vide\else non vide\fi ****************** Fin code ****************** ****************** Code 227 ****************** \newbox\foobox \setbox\foobox=\hbox{Programmer est facile.} <<\copy\foobox>> mesure \the\wd\foobox\ de long, \the\dp\foobox\ de profondeur et \the\ht\foobox\ de haut. \medbreak \setbox\foobox=\vbox{\hbox{Programmer}\hbox{est}\hbox{facile.}} <<\copy\foobox>> mesure \the\wd\foobox\ de long, \the\dp\foobox\ de profondeur et \the\ht\foobox\ de haut. \medbreak \setbox\foobox=\vtop{\hbox{Programmer}\hbox{est}\hbox{facile.}} <<\copy\foobox>> mesure \the\wd\foobox\ de long, \the\dp\foobox\ de profondeur et \the\ht\foobox\ de haut. ****************** Fin code ****************** ****************** Code 228 ****************** \def\vdim#1{\dimexpr\ht#1+\dp#1\relax} a) \setbox\foobox=\vbox{\hbox{Programmer}\hbox{est}\hbox{facile.}} Verticalité de la \litterate-\vbox- = \the\vdim\foobox\par b) \setbox\foobox=\vtop{\hbox{Programmer}\hbox{est}\hbox{facile.}} Verticalité de la \litterate-\vtop- = \the\vdim\foobox ****************** Fin code ****************** ****************** Code 229 ****************** \def\countallchar#1{% Il y a % \setbox0=\hbox{\tt#1}% met #1 dans la boite \edef\arglength{\number\wd0 }% stocke la largeur de la boite en sp \setbox0=\hbox{\tt A}% met "A" dans la boite \edef\charlength{\number\wd0 }% stocke la largeur d'un caractère $\number\arglength/\charlength % affiche la division =\number\numexpr\arglength/\charlength\relax$ % affiche le quotient caractères% } \countallchar{abcd efgh}\par \countallchar{A5 xW5 64 a1}\par \countallchar{affligeant} ****************** Fin code ****************** ****************** Code 230 ****************** \catcode`@11 \def\countchar#1#2{% \setbox\z@\hbox{\tt#2}% met #2 dans boite 0 \edef\len@a{\number\wd\z@}% mesure la boite \setbox\z@\hbox{\tt\substin{#2}{#1}{}}% recommencer sans "#1" \edef\len@b{\number\wd\z@}% mesure la boite \setbox\z@\hbox{\tt A}% met "A" dans la boite \edef\charlength{\number\wd\z@}% stocke la largeur du caractère \number\numexpr(\len@a-\len@b)/\charlength% et affiche le quotient } \catcode`@12 a) \countchar{a}{abracadabra}\qquad b) \countchar{b}{zigzag}\qquad c) \countchar{ }{a bc de f ghi j k } ****************** Fin code ****************** ****************** Code 231 ****************** Programmer \raise1ex\hbox{en} \lower1ex\hbox{\TeX} \lower2ex\hbox{est} facile. ****************** Fin code ****************** ****************** Code 232 ****************** \def\cbox#1{% \setbox0\vbox{#1}% met le contenu dans une \vbox \lower\dimexpr(\ht0-\dp0)/2\relax\box0 % l'abaisse } ......\cbox{\hbox{$x^2$}}......\cbox{\hbox{foo}\hbox{et bar}}......% \cbox{\hbox{Programmer}\hbox{en \TeX}\hbox{est facile}}....... ****************** Fin code ****************** ****************** Code 233 ****************** ......\cbox{\hbox{foo}\hbox{et bar}}......$\vcenter{\hbox{foo}\hbox{et bar}}$...... ****************** Fin code ****************** ****************** Code 234 ****************** \def\htmath{\begingroup \setbox0=\hbox{$\vcenter{}$}\the\ht0 \endgroup } L'axe mathématique se trouve à \htmath{} de la ligne de base. ****************** Fin code ****************** ****************** Code 235 ****************** 1) \hbox{foobar}\par 2) \hbox spread 5pt{foo\hfil bar}\par 3) \hbox spread10pt{foo\hfil bar} ****************** Fin code ****************** ****************** Code 236 ****************** foobar|\rlap{/////}123456\qquad foobar|\llap{/////}123456 ****************** Fin code ****************** ****************** Code 237 ****************** \def\clap#1{\hbox to0pt{\hss#1\hss}} a) avant la macro|\clap{SURIMPRESSION}après la macro\medbreak b) avant la macro|\raise2.5ex\clap{Au-dessus}\lower2.5ex\clap{Au-dessous}% après la macro\medbreak c) avant la macro|\raise2.5ex\llap{Au-dessus avant}\lower2.5ex\rlap{Au-dessous après}% après la macro ****************** Fin code ****************** ****************** Code 238 ****************** \setbox0=\hbox{\tt//////////} \wd0=0pt % fait croire à TeX que la larguer de la boite est nulle Voici \copy0 du texte partiellement barré... ****************** Fin code ****************** ****************** Code 239 ****************** \def\printdim{largeur=\the\wd0 \qquad hauteur=\the\ht0 \qquad profondeur = \the\dp0 } \setbox0=\hbox{Programmer en \TeX{} est facile} a) \printdim\par b) \wd0=0pt \ht0=0pt \dp0=0pt% rend toutes le dimensions nulles \printdim\par c) \setbox0=\hbox{\unhbox0 }% reprend les dimensions d'origine \printdim ****************** Fin code ****************** ****************** Code 240 ****************** \setbox0=\hbox{}% le registre 0 contient une boite vide Le registre \ifvoid0 est vide\else n'est pas vide\fi ****************** Fin code ****************** ****************** Code 241 ****************** \catcode`\@11 \def\ifzerodimbox#1{% #1=registre de boite % revoie vrai si le registre est vide ou contient une boite de dimensions nulles \csname% former la macro "\firstoftwo" ou "\secondoftwo" \ifvoid#1first%% si le registre est vide "first" \else% sinon \ifdim\wd#1=\z@% si la largeur \ifdim\ht#1=\z@% la hauteur \ifdim\dp#1=\z@ first% et la profondeur=0pt, "first" \else second% dans les autres cas "second" \fi \else second% \fi \else second% \fi \fi oftwo% compléter avec "oftwo" \endcsname } \catcode`\@12 a) \setbox0=\hbox{}\ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par b) \box0 % affiche la boite vide, le registre est maintenant "void" \ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par c) \setbox0=\hbox{x}\ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par d) \wd0=0pt \ht0=0pt \dp0=0pt % rend toutes les dimensions nulles \ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par e) \setbox0=\vbox{}\ifzerodimbox0{vrai}{faux} (et \ifvoid0 void\else non void\fi) ****************** Fin code ****************** ****************** Code 242 ****************** \def\ifvoidorempty#1{% teste si le registre #1 est vide ou contient une boite vide \ifvoid#1\relax \expandafter\firstoftwo \else \begingroup% dans un groupe \setbox0=% affecter à la boite 0 \ifhbox#1\hbox\bgroup\unhcopy% un boite horizontale \else \vbox\bgroup\unvcopy% ou verticale \fi% dans laquelle on compose #1\relax% #1 en dimensions naturelles \expandafter\egroup% sauter la fin de la boite \expandafter% et le \endgroup \endgroup \ifnum\lastnodetype=-1 % et tester si le dernier noeud est vide \expandafter\expandafter\expandafter\firstoftwo \else \expandafter\expandafter\expandafter\secondoftwo \fi \fi } a) \setbox0=\hbox{}\ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par b) \box0 % affiche la boite vide, le registre est maintenant "void" \ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par c) \setbox0=\hbox{x}\ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par d) \wd0=0pt \ht0=0pt \dp0=0pt % rend toutes les dimensions nulles \ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi)\par e) \setbox0=\vbox{}\ifvoidorempty0{vrai}{faux} (et \ifvoid0 void\else non void\fi) ****************** Fin code ****************** ****************** Code 243 ****************** \newdimen\hmaxsize \def\cvtop#1{% \hmaxsize=-\maxdimen% initialise à la plus petite longueur \doforeach\htext\in{#1}% pour chaque élément : {\setbox0=\hbox{\htext}% stocker l'élément "\htext" dans une \hbox \ifdim\wd0 >\hmaxsize% si sa longueur est supérieure à \hmaxsize \hmaxsize=\wd0 % mettre à jour \hmaxsize \fi }% \vtop{% dans une \vtop... \doforeach\htext\in{#1}% pour chaque élément : {\hbox to\hmaxsize{\hss\htext\hss}% le centrer dans une \hbox de longueur \hmaxsize }% }% } texte avant...\cvtop{Ligne du haut,Très grande ligne du milieu,Ligne du bas pour finir}% ...texte après ****************** Fin code ****************** ****************** Code 244 ****************** \newdimen\hmaxsize \def\cvtop#1{% \hmaxsize=-\maxdimen% initialise à la plus petite longueur \doforeach\htext\in{#1}% pour chaque élément : {\setbox0=\hbox{\htext}% stocker l'élément "\htext" dans une \hbox \ifdim\wd0 >\hmaxsize% si sa longueur est supérieure à \hmaxsize \hmaxsize=\wd0 % mettre à jour \hmaxsize \fi }% \vtop{% dans une \vtop... \hsize\hmaxsize % longueur de la \vtop = \maxhsize \parindent=0pt % pas d'indentation \leftskip=0pt plus1fil minus0pt \rightskip=\leftskip% ressorts de centrage \doforeach\htext\in{#1}% pour chaque élément : {\htext\par}% le composer et finir le paragraphe }% } texte avant...\cvtop{Ligne du haut,Très grande ligne du milieu,Ligne du bas pour finir}% ...texte après ****************** Fin code ****************** ****************** Code 245 ****************** \halign{X#**\ignorespaces&#&\hfil #\cr % préambule foo &foo bar&123 456\cr % première ligne deuxième ligne &1 &12\cr % deuxième ligne a &\TeX &1 2 3 4 5 6\cr % troisième ligne \crcr} ****************** Fin code ****************** ****************** Code 246 ****************** \def\cvtop#1{% \vtop{% \vtop assure que l'on est en mode vertical \substtocs\temp{#1}{,}{\cr}% dans \temp, remplacer les "," par "\cr" \halign{% \hfil##\hfil\cr% préambule : centrer le contenu \temp\crcr}% mettre \temp dans l'alignement }% \temp est détruite en sortant de la boite } texte avant...\cvtop{Ligne du haut,Très grande ligne du milieu,Ligne du bas pour finir}% ...texte après ****************** Fin code ****************** ****************** Code 247 ****************** \def\calcmaxdim#1#2{% \setbox0=\vtop{% \vtop assure que l'on est en mode vertical (interne) \substtocs\temp{#2}{,}{\cr}% dans \temp, remplacer les "," par "\cr" \halign{##\hfil\cr% préambule : composer au fer à gauche \temp\crcr}% mettre \temp dans l'alignement }% \temp est détruite en sortant de la boite \edef#1{\the\wd0 }% } a) La plus grande longueur vaut \calcmaxdim\foo{Ligne du haut,Très grande ligne du milieu,Ligne du bas pour finir}\foo b) Vérification : \setbox0=\hbox{Très grande ligne du milieu}\the\wd0 ****************** Fin code ****************** ****************** Code 248 ****************** a) .......\vbox{\hrule\hbox{foo}\hbox{ligne du bas}\hrule}.......\medbreak b) .......\vrule\vtop{\hbox{foo}\hbox{ligne du bas}}\vrule....... ****************** Fin code ****************** ****************** Code 249 ****************** Une réglure de 1.5cm :\hrule width1.5cm foo\vrule width 2pt height .5cm depth .2cm bar ****************** Fin code ****************** ****************** Code 250 ****************** \def\showdim#1{% \vrule width 0.4pt height 1ex depth 0pt % trait vertical gauche \vrule width #1 height0.4pt depth 0pt % réglure horizontale de longueur #1 \vrule width 0.4pt height 1ex depth 0pt % trait vertical droit \relax% stoppe la lecture de la précédente dimension } a) une longueur de 1 cm : \showdim{1cm}\par b) une longueur de 137,4 pt : \showdim{137,4pt}\par c) une longueur de 2 mm : \showdim{2mm} ****************** Fin code ****************** ****************** Code 251 ****************** \frboxsep=5pt \frboxrule=1pt \leavevmode ...% \vbox{% \hrule height\frboxrule% réglure supérieure \kern\frboxsep% espace verticale haute \hbox{\kern\frboxsep Programmation\kern\frboxsep}% contenu + espaces horizontales \kern\frboxsep% espace verticale basse \hrule height\frboxrule% réglure inférieure }% ... ****************** Fin code ****************** ****************** Code 252 ****************** \frboxsep=5pt \frboxrule=0.6pt \def\FRbox#1{% /!\ ne change pas le mode H ou V en cours \hbox{% mettre à la suite horizontalement les 3 choses suivantes : \vrule width\frboxrule% 1) réglure gauche \vbox{% 2) un empilement vertical comprenant \hrule height\frboxrule% a) réglure supérieure \kern\frboxsep% b) espace verticale haute \hbox{% c) contenu + espaces en mode H \kern\frboxsep#1\kern\frboxsep }% \kern\frboxsep% d) espace verticale basse \hrule height\frboxrule% e)réglure inférieure }% \vrule width\frboxrule% 3) réglure droite }% } Ligne de base : ...\FRbox{Programmation}...% \frboxrule=2pt \FRbox{Programmation}...% \frboxsep=0pt \FRbox{Programmation}...% \frboxrule0.4pt \FRbox{Programmation}... ****************** Fin code ****************** ****************** Code 253 ****************** Persuadez-vous que : \vtop{ \vbox{ \hbox{Programmer} \hbox{en} \hbox{\TeX} \hbox{est}% <- ligne de base de la \vtop } \hbox{\it tout sauf} \hbox{facile.} } ****************** Fin code ****************** ****************** Code 254 ****************** %\newdimen\frboxrule \newdimen\frboxsep \frboxrule=0.4pt \frboxsep=2pt \def\frbox#1{% ne pas changer le mode H ou V en cours \hbox{% enferme dans une \hbox \vrule width\frboxrule% réglure gauche \vtop{% \vbox{% 1er élément de la \vtop \hrule height\frboxrule% réglure supérieure \kern\frboxsep% espace haut \hbox{% \kern\frboxsep% espace gauche #1% contenu \kern\frboxsep% espace droite }% }% puis autres éléments de la \vtop, sous la ligne de base \kern\frboxsep% espace bas \hrule height\frboxrule% réglure inférieure }% \vrule width\frboxrule% réglure droite }% } Ligne de base : ......\frbox{Programmation}......% \frboxrule=2pt \frbox{Programmation}......% \frboxsep=0pt \frbox{Programmation}......% \frboxrule0.4pt \frbox{Programmation}...... ****************** Fin code ****************** ****************** Code 255 ****************** \def\centretitre#1{% \medbreak% passe en mode v puis saute une espace verticale \noindent% pas d'indentation et passe en mode horizontal \frbox{% encadre \vbox{% une boite verticale \hsize=\dimexpr\hsize-2\frboxrule-2\frboxsep\relax \parindent=0pt % pas d'indentation \leftskip=0pt plus1fil \rightskip=\leftskip% ressorts de centrage \parfillskip=0pt % annule le ressort de fin de paragraphe #1% insère le titre \endgraf% et le compose }% }% \medbreak% passe en mode v puis saute une espace verticale \ignorespaces% mange les espaces situés après la macro \centretitre } \frboxrule=0.8pt \frboxsep=5pt Voici un \centretitre{Titre} puis un \centretitre{Texte très long pour composer un titre qui va prendre plusieurs lignes et pour s'assurer que la composition s'effectue correctement} ****************** Fin code ****************** ****************** Code 256 ****************** Une réglure en hauteur : \vrule width 1cm height 10.4pt depth -10pt ****************** Fin code ****************** ****************** Code 257 ****************** Une réglure en dessous: \vrule width 1cm depth 2.4pt height -2pt ****************** Fin code ****************** ****************** Code 258 ****************** \def\souligne#1{% \setbox0=\hbox{#1}% stocke le contenu dans le registre no 0 \setbox0=\hbox{% puis, dans une \hbox, construit une réglure \vrule width\wd0 % de la longueur du contenu depth\dimexpr\dp0 + 1.4pt\relax % dp = profondeur texte + 1.4pt height\dimexpr-\dp0 - 1pt\relax % ht = -profondeux texte - 1pt }% \wd0=0pt \dp0=0pt \ht0=0pt % annule toutes les dimensions \leavevmode \box0 % affiche la réglure #1% puis le contenu } Voici \souligne{du texte normal}.\par Voici \souligne{du texte profond}. ****************** Fin code ****************** ****************** Code 259 ****************** \def\Souligne#1{% \setbox0=\hbox{#1}% \setbox0=\hbox{\vrule width\wd0 depth1.4pt height-1pt }% \wd0=0pt \dp0=0pt \ht0=0pt \leavevmode \box0 #1% } Voici \Souligne{du texte normal}.\par Voici \Souligne{du texte profond}. ****************** Fin code ****************** ****************** Code 260 ****************** \def\Souligne#1{% \setbox0=\hbox{#1}% \lower 1pt % abaisser à 1pt sous la ligne de base \rlap{% une \hbox en surimpression vers la droite \vrule width\wd0 height0pt depth0.4pt % contenant le soulignement }% #1% puis afficher le } Voici \Souligne{du texte normal}.\par Voici \Souligne{du texte profond}. ****************** Fin code ****************** ****************** Code 261 ****************** \newdimen\stackwd \stackwd=3em % dimension horizontale interne des cadres \catcode`@11 \def\stackbox#1{% \par% termine le paragraphe en cours \noindent \stackbox@i#1\\\quark\\% ajoute "\\\quark\\" à la fin et appelle \stackbox@i \par } \def\stackbox@i#1\\{% #1=ligne courante \def\temp@{#1}% stocke la ligne courante \unless\ifx\quark\temp@% si ce n'est pas la fin \hfill % ressort infini de centrage (et fait passer en mode horizontal) \noindent \doforeach\current@item\in{#1}% pour chaque élément dans la ligne courante... {\frbox{% ...encadrer \hbox to\stackwd{% une \hbox de largeur \stackwd \hss% ressort de centrage \current@item% l'élément courant \hss% ressort de centrage } }% fin de la \frbox }% fin \doforeach \hfill% ressort infini de centrage \null% assure que le dernier ressort est pris en compte \par% finir le paragraphe \nobreak% interdire une coupure de page \nointerlineskip% ne pas insérer le ressort d'interligne \expandafter\stackbox@i% et recommencer \fi } \catcode`@12 \frboxrule=0.5pt \frboxsep=3pt \stackbox{a,bc,,d\\e,foobar,g\\123,456,$\alpha$,$x+y$,} ****************** Fin code ****************** ****************** Code 262 ****************** a\vrule width0.2pt height15pt depth0pt \quad a\vrule width0.2pt height0pt depth5pt \quad a\vrule width0.2pt height10pt depth10pt \quad a\vrule width1cm height0.2pt depth0pt ****************** Fin code ****************** ****************** Code 263 ****************** \frboxsep0pt %encadrement au plus proche \leavevmode \frbox{a\vrule width0pt height15pt depth0pt }\quad \frbox{a\vrule width0pt height0pt depth5pt }\quad \frbox{a\vrule width0pt height10pt depth10pt }\quad \frbox{a\vrule width1cm height0pt depth0pt } ****************** Fin code ****************** ****************** Code 264 ****************** \def\rectangle#1#2{% \begingroup% dans un groupe \frboxsep = 0pt % encadrer au plus proche \frboxrule= 0.4pt % en traits assez fins \frbox{% \vrule width#1 height0pt depth0pt %strut horizontal \vrule width0pt height#2 depth0pt %strut vertical }% \endgroup% fermer le groupe } Carré de 0.5 cm : \rectangle{0.5cm}{0.5cm}\smallskip Rectangle de 2.5cm par 3pt : \rectangle{2.5cm}{3pt} ****************** Fin code ****************** ****************** Code 265 ****************** \newdimen\stackwd \stackwd=3em \catcode`\@11 \def\stackbox#1{% \par% termine le paragraphe en cours \begingroup% dans un groupe semi-simple \parindent=0pt% pas d'indentation \parskip=0pt% annuler le \parskip \setbox0\hbox{Àgjp}% boite pour le strut \edef\stack@strut{\vrule width\z@ height\the\ht0 depth\the\dp0 }% définit le strut \stackbox@i#1\\\quark\\% ajoute "\\\quark\\" à la fin et appelle \stackbox@i \unkern% annule la dernière compensation verticale \par \endgroup } \def\stackbox@i#1\\{% #1=ligne courante \def\temp@{#1}% stocke la ligne courante \unless\ifx\quark\temp@% si ce n'est pas la fin \hfill % ressort infini de centrage (passe en mode horizontal) \doforeach\current@item\in{#1}% pour chaque élément dans la ligne courante... {\frbox{% ...encadrer \hbox to\stackwd{% une \hbox de largeur \stackwd contenant \hss% 1) ressort de centrage \stack@strut% 2) strut de dimension verticale \current@item%3) l'élement courant \hss}% 4)ressort de centrage }% fin de la \fbox \kern-\frboxrule% revenir en arrière pour superposer les réglures verticales }% fin de \doforeach \unkern% annuler la dernière compensation horizontale \hfill% ressort infini de centrage \null% fait prendre en compte le dernier ressort \par% termine le paragraphe \nobreak% interdit une coupure de page \nointerlineskip% sinon, ne pas ajouter le ressort d'interligne \kern-\frboxrule% superposer les réglures horizontales \expandafter\stackbox@i% et recommencer \fi } \frboxrule=0.5pt \frboxsep=3pt \stackbox{a,bc,,d\\e,foobar,g\\123,456,$\alpha$,$x+y$,} ****************** Fin code ****************** ****************** Code 266 ****************** \catcode`\@11 \def\lineboxed#1{% \par% termine le paragraphe en cours \begingroup% dans un groupe semi-simple \parindent=0pt% pas d'indentation \parskip=0pt% annuler le \parskip \setbox0\hbox{Àgjp}% boite pour le strut \edef\stack@strut{\vrule width\z@ height\the\ht0 depth\the\dp0 }% définit le strut \lineboxed@i#1\\\quark\\% ajoute "\\\quark\\" à la fin et appelle la macro récursive \unkern% annule la dernière compensation verticale \par \endgroup } \def\lineboxed@i#1\\{% #1=ligne courante \def\temp@{#1}% stocke la ligne courante \unless\ifx\quark\temp@% si ce n'est pas la fin \cnttimestocs{#1,}{,}\nb@args% reçoit le nombre d'arguments dans la ligne courante \edef\dim@box{\the\dimexpr(\hsize-\frboxrule*(\nb@args+1)- \frboxsep*2*\nb@args)/\nb@args}% \hbox{% \doforeach\current@item\in{#1}% pour chaque élément dans la ligne courante... {\frbox{% ...encadrer \hbox to\dim@box{% une \hbox de largeur \dim@box contenant \hss% 1) ressort de centrage \stack@strut% 2) strut de dimension verticale \current@item%3) l'élement courant \hss}% 4)ressort de centrage }% fin de la \fbox \kern-\frboxrule% revenir en arrière pour superposer les réglures verticales }% fin de \doforeach \unkern% annuler la dernière compensation horizontale }% \par% termine le paragraphe \nobreak% interdit une coupure de page \nointerlineskip% sinon, ne pas ajouter le ressort d'interligne \kern-\frboxrule% superposer les réglures horizontales \expandafter\lineboxed@i% et recommencer \fi } \catcode`\@12 \lineboxed{a,bc,,d\\e,foobar,g\\123,456,$\alpha$,$x+y$,}\medbreak \frboxrule=1.5pt \frboxsep=3pt \lineboxed{,,,,,,,\\,,\\,,,,,,} ****************** Fin code ****************** ****************** Code 267 ****************** \xunit=0.5cm \yunit=0.5cm \mainrule=0.8pt \subrule=0.2pt \def\vlap#1{\vbox to0pt{\vss#1\vss}} \catcode`@11 \def\grid#1#2#3#4{% \vbox{% empiler les éléments verticalement \offinterlineskip% pas de ressort d'interligne \edef\total@wd{\the\dimexpr\xunit*#1\relax}% largeur totale ds réglures \for\ii = 1 to #3 \do1% pour chaque carreau vertical (\ii=variable muette) {\vlap{\hrule width\total@wd height\mainrule}% tracer la réglure horizontale \kern\yunit% insérer l'espace vertical }% \vlap{\hrule width\total@wd height\mainrule}% dernière réglure horizontale }% } \catcode`@12 \setbox0\hbox{\grid{4}{}{3}{}}% range la quadrillage dans le registre no 0 Essai \copy0{} qui a pour largeur=\convertunit{\wd0}{cm} cm et pour hauteur=\convertunit{\ht0}{cm} cm ****************** Fin code ****************** ****************** Code 268 ****************** Début\vbox to0pt{\hbox{sous}\hbox{la ligne de base}\vss}suite ****************** Fin code ****************** ****************** Code 269 ****************** \xunit=0.5cm \yunit=0.5cm \mainrule=0.8pt \subrule=0.2pt \def\vlap#1{\vbox to0pt{\vss#1\vss}} \catcode`@11 \def\grid#1#2#3#4{% \vbox{% empiler les éléments verticalement \offinterlineskip% pas de ressort d'interligne \edef\total@wd{\the\dimexpr\xunit*#1\relax}% largeur totale de la boite \edef\sub@unit{\the\dimexpr\yunit/#4\relax}% hauteur verticale de la subdivision \for\ii = 1 to #3 \do% pour chaque unité verticale en partant du haut, tracer : {\vlap{\hrule width\total@wd height\mainrule}% la réglure horizontale principale % et dessous, les réglures horizontales secondaires : \vbox to\z@{% dans une \vbox de hauteur nulle, \for\jj = 2 to #4 \do 1% insérer #4-1 fois sous la position courante : {\kern\sub@unit % l'espace verticale \vlap{\hrule width\total@wd height\subrule}% et la réglure secondaire }% \vss% ressort qui se comprime pour satisfaire la hauteur nulle }% \kern\yunit% insérer l'espace vertical entre réglures principales }% \vlap{\hrule width\total@wd height\mainrule}% dernière réglure principale du bas }% } \catcode`@12 \setbox0=\hbox{\grid{4}{}{3}{5}} Essai \copy0{} qui a pour largeur=\convertunit{\wd0}{cm} cm et pour hauteur=\convertunit{\ht0}{cm} cm ****************** Fin code ****************** ****************** Code 270 ****************** \mainrule=0.8pt \subrule=0.2pt \def\vlap#1{\vbox to0pt{\vss#1\vss}} \catcode`@11 \def\grid#1#2#3#4{% \vbox{% empiler les éléments verticalement \offinterlineskip% pas de ressort d'interligne % #################### Tracé des réglures verticales #################### \vbox to\z@{% dans une \vbox de hauteur nulle \edef\total@ht{\the\dimexpr\yunit*#3\relax}% hauteur totale \edef\sub@unit{\the\dimexpr\xunit/#2\relax}% espace entre 2 subdivisions \rlap{% mettre à droite de la position sans bouger \for\ii = 1 to #1 \do 1% pour chaque unité horizontale {\clap{\vrule width\dimexpr\mainrule height\total@ht}% réglure principale \rlap{% mettre à droite de la position sans bouger \for\jj = 2 to #2 \do 1% insérer #2-1 fois {\kern\sub@unit % l'espace horizontal \clap{\vrule width\subrule height\total@ht}% et la réglure verticale }% }% \kern\xunit % insérer l'espace entre réglures horizontales }% \clap{\vrule width\mainrule height\total@ht}% dernière réglure principale }% \vss% compense la hauteur=0pt de la \vbox }% % #################### Tracé des réglures horizontales #################### \edef\total@wd{\the\dimexpr\xunit*#1\relax}% largeur totale de la boite \edef\sub@unit{\the\dimexpr\yunit/#4\relax}% espace entre 2 subdivisions \for\ii = 1 to #3 \do 1% pour chaque carreau vertical en partant du haut : {\vlap{\hrule width\total@wd height\mainrule}% réglure horizontale principale % et dessous, les réglures secondaires : \vbox to\z@{% dans une \vbox de hauteur nulle, \for\jj = 2 to #4 \do 1% insérer #4-1 fois sous la position courante : {\kern\sub@unit % l'espace vertical \vlap{\hrule width\total@wd height\subrule}% et la réglure secondaire }% \vss% ressort qui se comprime pour satisfaire la hauteur nulle }% \kern\yunit% insérer l'espace vertical entre réglures principales }% \vlap{\hrule width\total@wd height\mainrule}% dernière réglure horizontale }% } \catcode`@12 Grille 1 : \xunit=1cm \yunit=1cm \grid{5}{10}{2}{10}\smallskip Grille 2 : \xunit=0.5cm \yunit=0.5cm \grid{7}{1}{3}{1}\smallskip Grille 3 : \xunit=1.5cm \yunit=0.5cm \grid{4}{3}{3}{2} ****************** Fin code ****************** ****************** Code 271 ****************** \vrule\hbox to10cm{\leaders\hbox to1.5cm{\hss A\hss}\hfill}\vrule ****************** Fin code ****************** ****************** Code 272 ****************** \frboxsep=-\frboxrule \def~{\leavevmode\raise.75ex\hbox{\vrule height.2pt width1em}} ~\hbox to10cm{\leaders\hbox{\frbox{\hbox to1.5cm{\hss A\hss}}}\hfill}~ ~\hbox to10cm{\cleaders\hbox{\frbox{\hbox to1.5cm{\hss A\hss}}}\hfill}~ ~\hbox to10cm{\xleaders\hbox{\frbox{\hbox to1.5cm{\hss A\hss}}}\hfill}~ ****************** Fin code ****************** ****************** Code 273 ****************** a) \leavevmode\hbox to10cm{\leaders\hrule\hfill}\smallskip b) \hbox to10cm{\leaders\hbox{\vrule height0.2pt width2.5mm \kern1.5mm}\hfill} c) \vrule width0.2pt height1ex depth0pt % première réglure verticale \hbox to10cm{% puis répétition de "_|" \leaders\hbox{\vrule width2em height0.2pt \vrule width0.2pt height1ex}\hfill} d) \hbox to10cm{\leaders\hbox{% \vrule height.2pt width.5em% 1/2 palier bas \vrule height5pt width0.2pt% montée au palier haut \vrule height5pt depth-4.8pt width1em% palier haut \vrule height5pt width0.2pt% descente au palier bas \vrule height.2pt width.5em% 1/2 palier bas }\hfill} ****************** Fin code ****************** ****************** Code 274 ****************** \mainrule=0.8pt \subrule=0.2pt \def\carreau#1#2{% #1=nb subdiv H #2=nb subdiv V \vbox to\yunit{% dans un \vbox de hauteur \yunit \offinterlineskip% pas de ressort d'interligne \vlap{\hrule height\mainrule width\xunit}% réglure principale du haut \leaders% répéter (ici ce sera donc #2-1 fois) \vbox to\dimexpr\yunit/#2\relax% une \vbox de hauteur \yunit/#2 {\vss% ressort qui va s'étirer à \yunit/#2 \vlap{\hrule height\subrule width\xunit}% réglure de subdiv de dim 0pt }\vfill \kern\dimexpr\yunit/#2\relax% dernière espace verticale \vlap{\hrule height\mainrule width\xunit}% réglure principale du bas }% } \yunit=1cm \leavevmode \carreau{}{4} puis \carreau{}{10} ****************** Fin code ****************** ****************** Code 275 ****************** \mainrule=0.8pt \subrule=0.2pt \def\carreau#1#2{% #1=nb subdiv H #2=nb subdiv V % ######## réglures horizontales ######## \rlap{% mettre à droite de la position sans bouger \vbox to\yunit{% dans un \vbox de hauteur \yunit \offinterlineskip% pas de ressort d'interligne \vlap{\hrule height\mainrule width\xunit}% réglure principale du haut \leaders% répéter (ici ce sera #2-1 fois) \vbox to\dimexpr\yunit/#2\relax% une \vbox de hauteur \yunit/#2 {\vss% ressort qui va s'étirer à \yunit/#2 \vlap{\hrule height\subrule width\xunit}% réglure de subdiv de dim 0pt }\vfill% ressort de \leaders \kern\dimexpr\yunit/#2\relax% derniere espace verticale \vlap{\hrule height\mainrule width\xunit}% réglure principale du bas }% }% % ######## réglures verticales ######## \hbox to\xunit{% dans une \hbox de longueur \xunit \clap{\vrule height\yunit width\mainrule}% réglure principale de gauche \leaders% répéter (ici ce sera #1-1 fois) \hbox to\dimexpr\xunit/#1\relax {\hss% ressort qui va s'étirer à \xunit/#1 \clap{\vrule height\yunit width\subrule}% réglure H de dimension 0 }\hfill% ressort de \leaders \kern\dimexpr\xunit/#1\relax% dernière espace H \clap{\vrule height\yunit width\mainrule}% réglure principale de droite }% } \yunit=1cm \xunit=2cm \leavevmode \carreau{3}{4} puis \carreau{8}{10} ****************** Fin code ****************** ****************** Code 276 ****************** \mainrule=0.8pt \subrule=0.2pt \def\grid#1#2#3#4{% \vbox to#3\yunit{% dans une boite verticale de hauteur #3*\yunit \leaders% répéter verticalement \hbox to#1\xunit{% une boite de longueur #1*\xunit \leaders% dans laquelle se répète horizontalement \hbox{\carreau{#2}{#4}}% le carreau de largeur \xunit \hfill}% \vfill }% } Grille 1 : \xunit=1cm \yunit=1cm \grid{5}{10}{2}{10}\smallskip Grille 2 : \xunit=0.5cm \yunit=0.5cm \grid{7}{1}{3}{1}\smallskip Grille 3 : \xunit=1.5cm \yunit=0.5cm \grid{4}{3}{3}{2} ****************** Fin code ****************** ****************** Code 277 ****************** \outer\def\foo{Bonjour} \def\bar{\foo} ****************** Fin code ****************** ****************** Code 278 ****************** \outer\def\foo{Bonjour} \expandafter\def\expandafter\bar\expandafter{\noexpand\foo} \meaning\bar \edef\baz{\noexpand\foo} \meaning\baz ****************** Fin code ****************** ****************** Code 279 ****************** \catcode`\@11 \long\def\filedef#1#2{% \begingroup \let\input\@@input% <- utilisateurs de latex uniquement \everyeof{\eof@nil\noexpand}% insère "\eof@nil\noexpand" à la fin du fichier \expandafter\filedef@i\expandafter#1% développe \input #2 \expandafter\relax\input #2 } \long\def\filedef@i#1#2\eof@nil{% \endgroup \expandafter\def\expandafter#1\expandafter{\gobone#2}% mange le \relax } \catcode`\@12 \filedef\foo{test.txt} \meaning\foo ****************** Fin code ****************** ****************** Code 280 ****************** % canal de lecture employé dans tout ce chapitre \def\iffileexists#1#2{% #1=canal de lecture #2=nom du fichier \openin#1=#2 \ifeof#1% le fichier n'existe pas \closein#1 \expandafter\secondoftwo% renvoyer faux \else \closein#1 \expandafter\firstoftwo% sinon renvoyer vrai \fi } a) \iffileexists\rtest{test.txt}{vrai}{faux}\qquad b) \iffileexists\rtest{foobar.txt}{vrai}{faux} ****************** Fin code ****************** ****************** Code 281 ****************** \openin\rtest =filetest.txt \read\rtest to \foo% lit la première ligne 1) Signification : \meaning\foo.\par% signification de ce qui a été lu 1) Exécution : \foo\par \read\rtest to \foo% lit la deuxième ligne 2) Signification : \meaning\foo.\par 2) Exécution : \foo\par \read\rtest to \foo% lit la dernière ligne 3) Signification : \meaning\foo.\par 3) Exécution : \foo \closein\rtest ****************** Fin code ****************** ****************** Code 282 ****************** \openin\rtest =filetest.txt \readline\rtest to \foo% lit la première ligne 1) Signification : \meaning\foo.\par% signification de ce qui a été lu 1) Exécution : \foo\par \readline\rtest to \foo% lit la deuxième ligne 2) Signification : \meaning\foo.\par 2) Exécution : \foo\par \readline\rtest to \foo% lit la dernière ligne 3) Signification : \meaning\foo.\par 3) Exécution : \foo \closein\rtest ****************** Fin code ****************** ****************** Code 283 ****************** Valeur de \string\endlinechar = \number\endlinechar\par Caractère correspondant : << \char\endlinechar{} >> ****************** Fin code ****************** ****************** Code 284 ****************** \openin\rtest =filetest.txt Ligne 1 : {\endlinechar=-1 \global\read\rtest to \foo}% lit la première ligne \meaning\foo.\par% donne ce qui a été lu Ligne 2 : {\endlinechar=-1 \global\read\rtest to \foo}% lit la deuxième ligne \meaning\foo.\par Ligne 3 : {\endlinechar=-1 \global\read\rtest to \foo}% lit la dernière ligne \meaning\foo. \closein\rtest ****************** Fin code ****************** ****************** Code 285 ****************** \def\xread#1to#2{% \ifcs{#2}% si #2 est une séquence de contrôle {\edef\restoreendlinechar{\endlinechar=\the\endlinechar}% \endlinechar=-1 % supprime le caractère mis en fin de ligne \read#1to#2% lit la ligne et l'assigne à la macro #2 \restoreendlinechar\relax% restaure le \endlinechar }% si #2 n'est pas une séquence de contrôle, {\xread#1to}% ignorer #2, et recommencer } \openin\rtest =filetest.txt Ligne 1 : \xread\rtest to \foo% lit la première ligne \meaning\foo.\par% donne ce qui a été lu Ligne 2 : \xread\rtest to \foo% lit la deuxième ligne \meaning\foo.\par Ligne 3 : \xread\rtest to \foo% lit la dernière ligne \meaning\foo. \closein\rtest ****************** Fin code ****************** ****************** Code 286 ****************** \def\xread{% doit être suivie de " to \" \edef\restoreendlinechar{\endlinechar=\the\endlinechar}% \endlinechar=-1 % neutralise \endlinechar \afterassignment\restoreendlinechar% après l'assignation, restaurer \endlinechar \read% attend to \ pour effectuer l'assignation } \catcode`\@12 \openin\rtest =filetest.txt Ligne 1 : \xread\rtest to \foo% lit la première ligne \meaning\foo.\par% donne ce qui a été lu Ligne 2 : \xread\rtest to \foo% lit la deuxième ligne \meaning\foo.\par Ligne 3 : \xread\rtest to \foo% lit la dernière ligne \meaning\foo. \closein\rtest ****************** Fin code ****************** ****************** Code 287 ****************** \def\macroname{% se développe en le nom de la macro qui suit sans % le caractère d'échappement \ifnum\escapechar>-1 % si le caractère d'échappement est positif \ifnum\escapechar<256 % et inférieur à 256, développer les 2 "\fi" \expandafter\expandafter\expandafter\expandafter% et le "\string", puis \expandafter\expandafter\expandafter\gobone% manger le "\" avec \gobone \fi \fi \string% doit être suivi d'une macro } \catcode`\@11 \newcount\field@cnt \def\searchitem#1#2#3#4{% #1= canal #2=nom fichier #3=référence #4=macro à définir \let#4\gobone% pour l'instant, #4=\gobone \openin#1=#2\relax \unless\ifeof#1% si le fichier existe \lowercase{\def\sought@firstfield{#3}}% stocke le 1er champ à chercher \edef\macro@name{\macroname#4}% nom de la macro sans "\" \xread#1 to \current@line% lire la première ligne \field@cnt=0 % initialiser le compteur de champs % ################ sauvegarde du nom des champs ################ \expsecond{\doforeach\current@field\in}\current@line% pour chaque champ {\advance\field@cnt1 % incrémenter le compteur de champs \lowercase\expandafter{% e texte de remplacement de \current@field en minuscule \expandafter\def\expandafter\current@field\expandafter{\current@field}% }% % sauvegarder chaque champ de la 1re ligne (qui sont les intitulés) dans une macro \letname{fieldname\number\field@cnt}=\current@field }% \edef\field@num{\number\field@cnt}% nombre de champs % ################ lecture des lignes de données ################ \loop% tant que... \unless\ifeof#1\relax% ...la fin du fichier n'est pas atteinte \xread#1 to \current@line% lire une ligne \unless\ifx\current@line\empty% si elle n'est pas vide % examniner les champs qu'elle contient (aller à \test@field) \expsecond{\expandafter\test@field\current@line\@nil}\macro@name% \fi \repeat \fi \closein#1\relax } \def\test@field#1,#2\@nil#3{% #1=champ no 1 #2=autres champs #3=nom de la macro sans "\" \def\current@firstfield{#1}% stocke le premier champ de la ligne en cours \ifx\current@firstfield\sought@firstfield% s'il est égal à celui cherché \defname{#3.\csname fieldname1\endcsname}{#1}% définir la macros \<#3."champ 1"> \field@cnt=1 % initialiser le compteur de champ \doforeach\current@field\in{#2}% puis, pour i>2, définir les macros \<#3."champ i"> {\advance\field@cnt1 % incrémenter le compteur de champ \letname{#3.\csname fieldname\number\field@cnt\endcsname}=\current@field }% \defname{#3}##1{% et définir la macro \<#3> \ifcsname#3.##1\endcsname% si la macro \<#3."argument"> existe déjà \csname#3.##1\expandafter\endcsname% l'exécuter après avoir mangé le \fi \fi }% \fi } \catcode`\@12 \searchitem\rtest{fournitures.txt}{4562u}\monarticle réf = \monarticle{ref}, dénomination = \monarticle{item}, prix = \monarticle{prix}, fournisseur = \monarticle{fournisseur}, champ non existant = \monarticle{foobar}. \searchitem\rtest{fournitures.txt}{truc}\essai% référence "truc" n'existe pas réf = \essai{ref}, dénomination = \essai{item}, prix = \essai{prix}, fournisseur = \essai{fournisseur}. ****************** Fin code ****************** ****************** Code 288 ****************** \def\macroname{% se développe en le nom de la macro qui suit sans % le caractère d'échappement \ifnum\escapechar>-1 % si le caractère d'échappement est positif \ifnum\escapechar<256 % et inférieur à 256, développer les 2 "\fi" \expandafter\expandafter\expandafter\expandafter% et le "\string", puis \expandafter\expandafter\expandafter\gobone% manger le "\" avec \gobone \fi \fi \string% doit être suivi d'une macro } \catcode`\@11 \newcount\field@cnt \newif\ifsearch@repeat \def\assign@arg#1=#2\@nil{% \def\sought@fieldnumber{#1}% no du champ à chercher \def\sought@fielvalue{#2}% et sa valeur } \def\searchitem#1#2#3#4{% #1= canal #2=nom fichier #3=champ cherché #4=macro à définir \let#4\gobone% pour l'instant, #4=\gobone \openin#1=#2\relax% \unless\ifeof#1% si le fichier existe \edef\macro@name{\macroname#4}% nom de la macro sans "\" \xread#1 to \current@line% lire et ignorer la première ligne \ifin{#3}{=}% si #3 contient = {\assign@arg#3\@nil}% trouver le no de champ et sa valeur {\def\sought@fieldnumber{1}% sinon, no du champ = 1 \def\sought@fielvalue{#3}% et sa valeur = #3 }% % ################ lecture des lignes de données ################ \search@repeattrue% poursuite de la boucle loop : vraie \loop% tant que... \ifeof#1\relax% ...la fin du fichier n'est pas atteinte \search@repeatfalse% sortir de la boucle loop \else \xread#1 to \current@line% lire une ligne \unless\ifx\current@line\empty% si elle n'est pas vide % examniner les champs qu'elle contient (aller à \test@field) \expsecond{\expandafter\test@field\current@line\@nil}\macro@name% \fi \fi \ifsearch@repeat% ne poursuivre que si le booléen en vrai \repeat \fi \closein#1\relax } \def\test@field#1\@nil#2{% #1=champs #2=nom de la macro sans "\" \field@cnt=0 % initialiser le compteur de champ \doforeach\current@field\in{#1}% parcourir les champs de la ligne en cours {\advance\field@cnt1 % incrémenter le compteur de champ \ifnum\field@cnt=\sought@fieldnumber\relax% si c'est le bon numéro de champ \ifx\current@field\sought@fielvalue% et si le champ correspond à celui cherché \search@repeatfalse% sortir de la boucle loop \doforeachexit% sortir de la boucle \doforeach en cours \fi \fi }% \unless\ifsearch@repeat% si la ligne a été trouvée \field@cnt=0 % initialiser le compteur de champ \doforeach\current@field\in{#1}% parcourir à nouveau les champs de la ligne {\advance\field@cnt1 % incrémenter le compteur de champ \letname{#2.\number\field@cnt}=\current@field% faire l'assignation }% \defname{#2}##1{% et définir la macro \<#2> \ifcsname#2.##1\endcsname% si la macro \<#2."argument"> existe déjà \csname#2.##1\expandafter\endcsname% l'exécuter après avoir mangé le \fi \fi }% \fi } \catcode`\@12 a) \searchitem\rtest{basecourse.txt}{3=283}\foo "\foo1", "\foo2", "\foo3", "\foo4", "\foo5", "\foo6", "\foo7" b) \searchitem\rtest{basecourse.txt}{Valet}\bar "\bar1", "\bar2", "\bar3", "\bar4", "\bar5", "\bar6", "\bar{abcd}" ****************** Fin code ****************** ****************** Code 289 ****************** \def\showfilecontent#1#2{% #1=canal de lecture #2=nom de fichier \begingroup \tt% sélectionner la fonte à chasse fixe \openin#1=#2\relax \ifeof#1% si la fin du fichier est déjà atteinte, il n'existe pas et Le fichier n'existe pas% afficher le message \else% le fichier existe \def\do##1{\catcode`##1=12 }% \dospecials% neutraliser tous les tokens spéciaux \obeyspaces% rendre l'espace actif \loop \xread#1 to \currline% lire une ligne \unless\ifeof#1% si la fin du fichier n'est pas atteinte \leavevmode\par% aller à la ligne \currline% afficher la ligne lue \repeat% recommencer \fi \closein#1\relax \endgroup } Contenu du fichier : "\showfilecontent\rtest{readtest.txt}", affiché tel quel ****************** Fin code ****************** ****************** Code 290 ****************** \def\showfilecontent#1#2{% #1=canal de lecture #2=nom de fichier \begingroup \tt% sélectionner la fonte à chasse fixe \openin#1=#2\relax \ifeof#1% si la fin du fichier est déjà atteinte, il n'existe pas et Le fichier n'existe pas% afficher le message \else% le fichier existe \def\do##1{\catcode`##1=12 }% \dospecials% neutraliser tous les tokens spéciaux \obeyspaces% rendre l'espace actif \def\magicpar{\let\magicpar=\par}% \loop \xread#1 to \currline% lire une ligne \unless\ifeof#1% si la fin du fichier n'est pas atteinte \leavevmode\magicpar% former le paragraphe (sauf à la 1er itération) \currline% afficher la ligne \repeat% recommencer \fi \closein#1\relax \endgroup } Contenu du fichier : "\showfilecontent\rtest{readtest.txt}", affiché tel quel ****************** Fin code ****************** ****************** Code 291 ****************** % sera le canal d'écriture dans tout ce chapitre \immediate\openout\wtest=writetest.txt % lie \wtest au fichier \immediate\write\wtest{Programmer en \noexpand\TeX{} est facile.}% écrit une ligne \immediate\write\wtest{Et utile...}% puis une autre \immediate\closeout\wtest% défait la liaison a) Contenu du fichier :\par "\showfilecontent\rtest{writetest.txt}"% affiche le contenu du fichier \medbreak % 2e tentative : b) Contenu du fichier :\par \immediate\openout\wtest=writetest.txt % lie \wtest au fichier \immediate\write\wtest{Essai d'écriture}% écrit une ligne \immediate\closeout\wtest% défait la liaison "\showfilecontent\rtest{writetest.txt}"% affiche le contenu du fichier ****************** Fin code ****************** ****************** Code 292 ****************** \def\noexpwrite#1#2{% #1=numéro de canal #2=texte à écrire \write#1{\unexpanded{#2}}% } \immediate\openout\wtest=writetest.txt \immediate\noexpwrite\wtest{Programmer en \TeX{} est facile.}% \immediate\noexpwrite\wtest{Et utile...}% \immediate\closeout\wtest Contenu du fichier :\par \showfilecontent\rtest{writetest.txt}% affiche le contenu du fichier ****************** Fin code ****************** ****************** Code 293 ****************** \catcode`\@11 \def\exactwrite#1{% #1=numéro de canal \begingroup \def\canal@write{#1}% sauvegarde le numéro de canal \for\xx=0 to 255\do{\catcode\xx=12 }% donne à tous les octets le catcode 12 \exactwrite@i% aller lire le } \def\exactwrite@i#1{% #1 est le de catcode 12 \def\exactwrite@ii##1#1{% ##1 est le à envoyer vers le fichier \exactwrite@iii##1\@nil% envoyer à \exactwrite@iii }% \exactwrite@ii% va lire tout ce qui se trouve jusqu'au prochain } {\catcode`\^^M 12 \gdef\EOL@char{^^M}}% définit le caractère de catcode 12 \def\exactwrite@iii#1\@nil{% #1 = à écrire dans le fichier \expsecond{\ifin{#1}}\EOL@char% si #1 contient "retour charriot" {\write@line#1\@nil% écrire la première ligne de #1 } {\immediate\write\canal@write{#1}% sinon : dernière ligne atteinte, l'écrire \endgroup% puis sortir du groupe }% } % les \expandafter provoquent le 1-développement de \EOL@char : \expandafter\def\expandafter\write@line\expandafter#\expandafter1\EOL@char#2\@nil{% \immediate\write\canal@write{#1}% écrit la ligne (ce qui se trouve avant ^^M) \exactwrite@iii#2\@nil% recommencer avec ce qui se trouve après "^^M" } \catcode`\@12 \immediate\openout\wtest=writetest.txt % lie le canal \wtest au fichier \exactwrite\wtest|Programmer en \TeX{} est facile ! Et très utile.| \immediate\closeout\wtest% et fermer le fichier Le contenu du fichier "writetest.txt" est :\par "\showfilecontent\rtest{writetest.txt}" ****************** Fin code ****************** ****************** Code 294 ****************** \catcode`\@11 \newcount\exo@number% compteur pour le numéro d'exercice \def\exocalctotal#1\endtotal{\edef\total@points{\dimtodec\dimexpr#1}} \def\initexo#1{% \def\exo@canal{#1}% sauvegarde le canal d'écriture \exo@number=0 % initialiser le compteur d'exo \iffileexists\exo@canal{\jobname.pts}% si le fichier .pts existe {\input \jobname.pts }% le lire et exécuter son contenu {\def\total@points{\char`\#\char`\#}}% sinon, définir un total alternatif \immediate\openout\exo@canal=\jobname.pts % ouvrir le fichier .pts \immediate\write\exo@canal{\noexpand\exocalctotal}% et commencer à y écrire dedans } \def\exo#1{% définti la macro qui affiche l'exercice \bigbreak% sauter une grande espace verticale \immediate\write\exo@canal{+#1}% écrire "+#1" dans le fichier .pts \advance\exo@number by 1 % incrémenter le numéro de l'exercice \noindent\vrule height1ex width1ex depth0pt % trace le carré \kern1ex% insérer une espace horizontale {\bf Exercice \number\exo@number}% afficher "Exercice " \leaders\hbox to.5em{\hss.\hss}\hfill% afficher les pointillés #1/\total@points% puis #1/ \smallbreak% composer la ligne précédente et sauter une espace verticale } \def\stopexo{% \immediate\write\exo@canal{\relax\noexpand\endtotal}% \immediate\closeout\exo@canal } \catcode`\@12 \initexo\wtest \hfill{\bf Interrogation écrite. Sujet : \TeX{}}\hfill\null \par \leavevmode\hfill\vrule height.4pt width 2cm depth0pt\hfill\null \exo{3pt} Élaborer un test \litterate/\ifonebyte{}{}{}/ qui teste, pour une compilation avec un moteur 8 bits, si le \litterate// est codé sur un seul octet. Ce test pourrait être utilisé pour déterminer si l'encodage d'un document est à plusieurs octets (comme l'est UTF8) en prenant comme \litterate// les caractères <<~é~>>, <<~à~>>, etc. \exo{5,5pt} Si \verb-#1- est un nombre entier, quel est le test fait par ce code ? \smallbreak \hfill \litterate/\if\string l\expandafter\firstto@nil\romannumeral#1\relax\@nil/ \hfill\null \exo{4,5pt} On a vu que pour provoquer un $n$-développement, les \litterate/\expandafter/se plaçaient en nombre égal à $2^n-1$ devant chaque token précédant celui que l'on veut développer. Or, ce nombre est {\it impair\/}. Trouver un exemple ou un cas particulier où il faut placer un nombre {\it pair\/} d'\litterate/\expandafter/ devant un token (on pourra envisager le cas de 2 \litterate/\expandafter/). \stopexo ****************** Fin code ****************** ****************** Code 295 ****************** \def\identite{Foo Bar}% Prénom Nom \def\beforespace#1 #2\nil{#1} \def\afterspace#1 #2\nil{#2} \def\prenom{\beforespace\identite\nil} \def\nom{\afterspace\identite\nil} Mon prénom : \expandafter\expandafter\prenom Mon nom : \expandafter\expandafter\nom ****************** Fin code ****************** ****************** Code 296 ****************** \newlinechar`\^^J \immediate\openout\wtest=test1.txt \immediate\write\wtest{Une première ligne^^JEt une seconde.} \immediate\closeout\wtest \showfilecontent\rtest{test1.txt} ****************** Fin code ****************** ****************** Code 297 ****************** \newlinechar`\^^J \immediate\openout\wtest=test2.txt \immediate\write\wtest{Une première ligne^^JEt une seconde.} \immediate\write\wtest{Et la dernière.} \immediate\closeout\wtest {\endlinechar`\X % insère "X" à chaque fin de ligne \openin\rtest=test2.txt % les fins de lignes sont commentées \loop% pour éviter que \endlinechar \read\rtest to \foo% ne soit inséré à chaque fin de \unless\ifeof\rtest% ligne du code source \meaning\foo\par% affiche le texte de remplacement de \foo \repeat% \closein\rtest}% ****************** Fin code ****************** ****************** Code 298 ****************** \catcode`\@11 \def\exactwrite#1{% #1=numéro de canal \begingroup \for\xx=0 to 255\do{\catcode\xx=12 }% donne à tous les octets le catcode 12 \newlinechar=`\^^M % caractère de fin de ligne = retour charriot \exactwrite@i{#1}% donne le d'écriture comme premier argument } \def\exactwrite@i#1#2{% #2 est le caractère délimiteur de catcode 12 \def\exactwrite@ii##1#2{% ##1 est le à envoyer vers le fichier \immediate\write#1{##1}% écrire le dans le fichier \endgroup% puis, sortir du groupe }% \exactwrite@ii% traiter tout ce qui se trouve jusqu'au prochain #2 } \catcode`\@12 \immediate\openout\wtest=writetest.txt % lie le canal \wtest au fichier \exactwrite\wtest|Programmer en \TeX{} est facile ! Et très utile.| \immediate\closeout\wtest% et fermer le fichier Le contenu du fichier "writetest.txt" est :\par "\showfilecontent\rtest{writetest.txt}" ****************** Fin code ****************** ****************** Code 299 ****************** \newlinechar`\^^J \message{^^JQuel est votre nom ? } \xread-1 to \username \message{Bonjour \username.^^J% Depuis combien d'années pratiquez-vous TeX ? } \read-1 to \yeartex \message{% \ifnum\yeartex<5 Cher \username, vous êtes encore un débutant ! \else\ifnum\yeartex<10 Bravo \username, vous êtes un TeXpert ! \else\ifnum\yeartex<15 Félicitations \username, vous êtes un TeXgourou. \else Passez à autre chose, par exemple à Metafont et Metapost ! \fi\fi\fi^^J} ****************** Fin code ****************** ****************** Code 300 ****************** \catcode`\@11 \def\answer@plus{+}\def\answer@minus{-}\def\answer@equal{=}% \def\nb@found#1{% macro appelée lorsque le nombre (argument #1) est trouvé \message{^^JVotre nombre est #1.^^JMerci d'avoir joué avec moi.^^J}% } \def\nbguess#1#2{% \message{Choisissez un nombre entre #1 et #2.^^J Tapez entrée lorsque c'est fait.}% \read-1 to \tex@guess% attend que l'on tape sur "entrée" \nbguess@i{#1}{#2}% } \def\nbguess@i#1#2{% \ifnum#1<#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi % si #1<#2 (donc le nombre n'est pas trouvé), % mettre dans \tex@guess la troncature de la moyenne de #1 et #2 {\edef\tex@guess{\number\truncdiv{\numexpr#1+#2\relax}2}% \message{Je propose \tex@guess.^^J% afficher sur le terminal Votre nombre est-il plus grand (+), plus petit (-) ou égal (=) : }% \read -1 to \user@answer% lire la réponse de l'utilisateur \edef\user@answer{% \expandafter\firstto@nil\user@answer\relax\@nil% ne garder que le 1er caractère }% \ifxcase\user@answer% envisager les cas "+", "-" et "=" \answer@plus{\exparg\nbguess@i{\number\numexpr\tex@guess+1\relax}{#2}}% \answer@minus{\expsecond{\nbguess@i{#1}}{\number\numexpr\tex@guess-1\relax}}% \answer@equal{\nb@found\tex@guess}% \elseif% si la réponse ne commence pas par "+", "-" ou "=" \message{Je n'ai pas compris votre réponse}% afficher message erreur \nbguess@i{#1}{#2}% et recommencer avec les mêmes nombres \endif } % si #1>=#2, le nombre est trouvé {\nb@found{#1}}% } \catcode`\@12 \nbguess{1}{100} ****************** Fin code ****************** ****************** Code 301 ****************** \def\syracuse#1{% #1% affiche le nombre \ifnum#1>1 % si le nombre est >1 , % afficher une virgule+espace \ifodd#1 % s'il est pair \exparg\syracuse% appeler la macro \syracuse {\number\numexpr3*#1+1% avec 3*n+1 \expandafter\expandafter\expandafter}% après avoir rendu la macro terminale \else % s'il est pair \expandafter\syracuse\expandafter% appeler la macro \syracuse {\number\numexpr#1/2% avec n/2 \expandafter\expandafter\expandafter}% après avoir rendu la macro terminale \fi \else% si le nombre est 1 .% afficher un point \fi } a) \syracuse{20}\par b) \syracuse{14}\par c) \syracuse{99}\par d) \edef\foo{\syracuse{15}}\meaning\foo ****************** Fin code ****************** ****************** Code 302 ****************** \def\syracuse#1{% #1% afficher le nombre \ifnum#1>1 % si le nombre est >1 , % afficher une virgule+espace \exparg\syracuse{% appeler la macro \syracuse \number\numexpr% avec le nombre : \ifodd#1 3*#1+1% 3n+1 si #1 est impair \else #1/2% n/2 sinon \fi% \expandafter}% avant de rendre la récursivité terminale \else .% si #1=1, afficher un point \fi } a) \syracuse{20}\par b) \syracuse{14}\par c) \syracuse{99}\par d) \edef\foo{\syracuse{15}}\meaning\foo ****************** Fin code ****************** ****************** Code 303 ****************** \def\factorielle#1{% \ifnum#1=0 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {1}% "1" si #1=0} {#1*\exparg\factorielle{\number\numexpr#1-1}}% } a) \factorielle{0}\qquad b) \factorielle{3}\qquad c) \edef\foo{\factorielle{8}}\meaning\foo ****************** Fin code ****************** ****************** Code 304 ****************** \catcode`\@11 \def\factorielle#1{% \number\numexpr\factorielle@i{#1}\relax% appelle \factorielle@i % en lançant le développement maximal } \def\factorielle@i#1{% \ifnum#1=0 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {1}% "1" si #1= {#1*\exparg\factorielle@i{\number\numexpr#1-1}}% } \catcode`\@12 a) \factorielle{0}\qquad b) \factorielle{3}\qquad c) \edef\foo{\factorielle{8}}\meaning\foo ****************** Fin code ****************** ****************** Code 305 ****************** \catcode`\@11 \def\PGCD#1#2{% \ifnum#1<#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\PGCD@i{#2}{#1}}% si #1<#2, mettre #2 (le grand) dans le premier argument {\PGCD@i{#1}{#2}}% } \def\PGCD@i#1#2{% #1=a #2=b avec a>b \exptwoargs\PGCD@ii% appeler la macro récursive avec {\number\numexpr#1-#2*\truncdiv{#1}{#2}}% le reste de a/b {#2}% et le divisieur b } \def\PGCD@ii#1#2{% #1=reste r #2=diviseur b \ifnum#1=\z@\expandafter\firstoftwo\else\expandafter\secondoftwo\fi {#2}% si le reste est nul, renvoyer b {\PGCD@i{#2}{#1}}% sinon, recommencer avec b et r } \catcode`\@12 a) \PGCD{120}{75}\qquad b) \PGCD{64}{180}\qquad c) \PGCD{145}{64}\qquad d) \edef\foo{\PGCD{1612}{299}}\meaning\foo ****************** Fin code ****************** ****************** Code 306 ****************** \catcode`\@11 \def\calcPGCD#1#2{% \ifhmode\par\fi% si en mode horizontal, former le paragraphe \ifnum#1<#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\calcPGCD@i{#2}{#1}}% si #1<#2, mettre #2 (le grand) dans le premier argument {\calcPGCD@i{#1}{#2}}% } \def\calcPGCD@i#1#2{% #1=a #2=b avec a>b \edef\calcPGCD@quotient{\number\truncdiv{#1}{#2}}% stocke le quotient $#1=\calcPGCD@quotient\times#2% en mode maths, afficher "a=q*b" (à suivre) \exptwoargs\calcPGCD@ii% appeler la macro récursive avec {\number\numexpr#1-#2*\calcPGCD@quotient}% le reste de a/b {#2}% et le divisieur b } \def\calcPGCD@ii#1#2{% #1=reste r #2=diviseur b +#1$\par% (suite du mode math) afficher "+r", fermer le mode math et \par \ifnum#1=\z@\expandafter\firstoftwo\else\expandafter\secondoftwo\fi% {}% si le reste est nul, ne rien faire {\calcPGCD@i{#2}{#1}}% sinon, recommencer avec b et r } \catcode`\@12 \calcPGCD{39}{15}\medbreak \calcPGCD{1612}{299} ****************** Fin code ****************** ****************** Code 307 ****************** \frboxsep=0pt % encadrer au plus proche \leavevmode\frbox{$=$} n'est pas identique à \frbox{${}={}$} ****************** Fin code ****************** ****************** Code 308 ****************** \catcode`\@11 \def\calcPGCD#1#2{% \vtop{% mettre l'alignement dans une \vtop \halign{% les "#" doivent être doublés puisqu'à l'intérieur d'une macro $\hfil##$&${}=\hfil##$&${}\times##\hfil$&${}+##\hfil$% préambule \cr% fin du préambule et début de la première cellule \ifnum#1<#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\calcPGCD@i{#2}{#1}}% si #1<#2, mettre #2 (le grand) dans le premier argument {\calcPGCD@i{#1}{#2}}% \crcr% fin de l'alignement }% }% } \def\calcPGCD@i#1#2{% #1=a #2=b avec a>b \xdef\calcPGCD@quotient{\number\truncdiv{#1}{#2}}% stocke le quotient #1 & \calcPGCD@quotient & #2 &% afficher "a=q*b" (à suivre) \exptwoargs\calcPGCD@ii% appeler la macro récursive avec {\number\numexpr#1-#2*\calcPGCD@quotient}% le reste de a/b {#2}% et le divisieur b } \def\calcPGCD@ii#1#2{% #1=reste r #2=diviseur b #1% (suite de l'alignement) afficher "+r" \cr% et terminer la ligne en cours \ifnum#1=\z@\expandafter\firstoftwo\else\expandafter\secondoftwo\fi% {}% si le reste est nul, ne rien faire {\calcPGCD@i{#2}{#1}}% sinon, recommencer avec b et r } \catcode`\@12 a) \calcPGCD{39}{15}\medbreak b) \calcPGCD{1612}{299} ****************** Fin code ****************** ****************** Code 309 ****************** \catcode`\@11 \def\baseconv#1{% \unless\ifnum#1=\z@ % si #1 est différent de 0 \number\numexpr#1-2*\truncdiv{#1}2\relax% écrire le reste \exparg\baseconv{\number\truncdiv{#1}2\expandafter}% recommencer avec #1/2 \fi% après que le \fi ait été lu } \catcode`\@12 a) \baseconv{43}\qquad b) \baseconv{32}\qquad c) \edef\foo{\baseconv{159}}\meaning\foo ****************** Fin code ****************** ****************** Code 310 ****************** \catcode`\@11 \def\baseconv#1{% \baseconv@i{}{#1}% } \def\baseconv@i#1#2{% #1=restes #2=n \ifnum#2=\z@\expandafter\firstoftwo\else\expandafter\secondoftwo\fi% si n=0 {#1}% si =0, afficher tous les restes {% sinon, recommencer en \exptwoargs\baseconv@i% ajoutant le reste courant avant #1 {\number\numexpr#2-2*\truncdiv{#2}2\relax #1} {\number\truncdiv{#2}2}% et en prenant n:=n/2 }% } \catcode`\@12 a) \baseconv{43}\qquad b) \baseconv{32}\qquad c) \edef\foo{\baseconv{159}}\meaning\foo ****************** Fin code ****************** ****************** Code 311 ****************** \catcode`\@11 \def\z@@{\expandafter\z@\expandafter} \def\basedigit#1{% \ifcase#1 \z@@ 0% \or\z@@ 1\or\z@@ 2\or\z@@ 3\or\z@@ 4\or\z@@ 5\or\z@@ 6\or\z@@ 7% \or\z@@ 8\or\z@@ 9\or\z@@ A\or\z@@ B\or\z@@ C\or\z@@ D\or\z@@ E% \or\z@@ F\or\z@@ G\or\z@@ H\or\z@@ I\or\z@@ J\or\z@@ K\or\z@@ L% \or\z@@ M\or\z@@ N\or\z@@ O\or\z@@ P\or\z@@ Q\or\z@@ R\or\z@@ S% \or\z@@ T\or\z@@ U\or\z@@ V\or\z@@ W\or\z@@ X\or\z@@ Y\or\z@@ Z% \fi } \long\def\>#1<{\detokenize{#1}} a) \expandafter\>\romannumeral\basedigit{23}<\quad b) \expandafter\>\romannumeral\basedigit{6}< \catcode`@12 ****************** Fin code ****************** ****************** Code 312 ****************** \catcode`\@11 \def\baseconv#1#2{% #1=base #2=nombre à convertir \ifnum#1<37 % base maxi = 36 (10 signes chiffres + 26 signes lettres) \antefi \baseconv@i{}{#2}{#1}% \fi } \def\baseconv@i#1#2#3{% #1=restes #2=n #3=base \ifnum#2=\z@ \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {#1}% si n=0, afficher tous les restes {% si non, transmettre en dernier argument \expsecond{\baseconv@ii{#1}{#2}{#3}}% {\number\truncdiv{#2}{#3}}% le quotient }% } \def\baseconv@ii#1#2#3#4{% #1=restes #2=n #3=base #4=q \exparg\baseconv@i% recommencer, en ajoutant le avant les restes {\romannumeral\basedigit{\number\numexpr#2-#4*#3\relax}#1}% {#4}% et en remplaçant n par q {#3}% } \def\z@@{\expandafter\z@\expandafter}% \def\basedigit#1{% \ifcase#1 \z@@ 0% \or\z@@ 1\or\z@@ 2\or\z@@ 3\or\z@@ 4\or\z@@ 5\or\z@@ 6\or\z@@ 7% \or\z@@ 8\or\z@@ 9\or\z@@ A\or\z@@ B\or\z@@ C\or\z@@ D\or\z@@ E% \or\z@@ F\or\z@@ G\or\z@@ H\or\z@@ I\or\z@@ J\or\z@@ K\or\z@@ L% \or\z@@ M\or\z@@ N\or\z@@ O\or\z@@ P\or\z@@ Q\or\z@@ R\or\z@@ S% \or\z@@ T\or\z@@ U\or\z@@ V\or\z@@ W\or\z@@ X\or\z@@ Y\or\z@@ Z% \fi } \catcode`\@12 a) "\baseconv{20}{21587}"\qquad b) "\baseconv{16}{32}"\qquad c) \edef\foo{\baseconv{16}{159}}% "\meaning\foo"\qquad d) "\baseconv{2}{43}" ****************** Fin code ****************** ****************** Code 313 ****************** \catcode`\@11 \def\quark@list{\quark@list}% quark de fin de liste \def\finditem#1{% #1 = \, la position est lue plus tard par \finditem@i \exparg\finditem@i{#1}% 1-développe la \ } \def\finditem@i#1#2{% #1 = liste #2=position cherchée \finditem@ii{1}{#2}#1,\quark@list,% appelle la macro récursive } \def\finditem@ii#1#2#3,{% #1=position courante #2=position cherchée #3=élément courant \ifx\quark@list#3\expandafter\firstoftwo\else\expandafter\secondoftwo\fi {}% si la fin de liste est atteinte, ne rien renvoyer {% sinon \ifnum#1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {% si la position est la bonne \finditem@iii{#3}% renvoyer #3 et manger les éléments restants } {% si la position n'est pas la bonne, recommencer en incrémentant #1 \exparg\finditem@ii{\number\numexpr#1+1}{#2}% }% }% } \def\finditem@iii#1#2\quark@list,{% renvoyer #1 et manger le reste de la liste #1% } \catcode`\@12 \def\liste{a,bcd,{ef},g,hij,kl} a) \edef\foo{\finditem\liste5}\meaning\foo\qquad b) \edef\bar{\finditem\liste3}\meaning\bar ****************** Fin code ****************** ****************** Code 314 ****************** \catcode`\@11 \def\quark@list{\quark@list}% quark de fin de liste \def\finditem#1{% #1 = \, la position est lue plus tard par \finditem@i \exparg\finditem@i{#1}% 1-développe la \ } \def\finditem@i#1#2{% #1 = liste #2=position cherchée \ifnum#2>\z@% ne faire quelque chose que si la position est >0 \antefi \finditem@ii{1}{#2}\relax#1,\quark@list,% appelle la macro récursive \fi } \def\finditem@ii#1#2#3,{% #1=position courante #2=position cherché #3=élément courant \expandafter\ifx\expandafter\quark@list\gobone#3% \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {}% si la fin de liste est atteinte, ne rien renvoyer {% sinon \ifnum#1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {% si la position est la bonne \finditem@iii{#3}% renvoyer #3 et manger les éléments restants } {% si la position n'est pas la bonne, recommencer en incrémentant #1 \exparg\finditem@ii{\number\numexpr#1+1}{#2}\relax }% }% } \def\finditem@iii#1#2\quark@list,{% renvoyer #1 et manger le reste de la liste \gobone#1% } \def\finditemtocs#1#2#3{% #1 = \ #2=position #3=macro à définir \def\finditemtocs@iii##1##2\quark@list,{% renvoyer #1 et manger le reste de la liste \expandafter\def\expandafter#3\expandafter{\gobone##1}% }% \let#3=\empty \exparg\finditemtocs@i{#1}{#2}% 1-développe la \ } \def\finditemtocs@i#1#2{% #1 = liste #2=position cherchée \ifnum#2>\z@% ne faire quelque chose que si la position est >0 \antefi\finditemtocs@ii{1}{#2}\relax#1,\quark@list,% appelle la macro récursive \fi } \def\finditemtocs@ii#1#2#3,{% % #1=position courante #2=position cherché #3=\relax + élément courant \expandafter\ifx\expandafter\quark@list\gobone#3% \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {}% si fin de liste ne rien faire. Sinon, si position bonne {\ifnum#1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\finditemtocs@iii{#3}% renvoyer #3 et manger les éléments restants }% si la position n'est pas la bonne, recommencer en incrémentant #1 {\exparg\finditemtocs@ii{\number\numexpr#1+1}{#2}\relax% }% }% } \catcode`\@12 \def\liste{a,bcd,{ef},g,hij,kl} a) "\finditem\liste5"\qquad b) \edef\bar{\finditem\liste3}\meaning\bar\qquad c) \finditemtocs\liste{3}\foo\meaning\foo ****************** Fin code ****************** ****************** Code 315 ****************** \catcode`\@11 \def\quark@list{\quark@list}% quark de fin de liste \def\positem#1{% #1 = \, la position est lue plus tard par \positem@i \def\positem@endprocess{\pos@item}% hook : afficher la position \exparg\positem@i{#1}% 1-développe la \ } \def\positem@i#1#2{% #1 = liste #2=élément cherché \def\sought@item{#2}% définir l'élément cherché \positem@ii{1}\relax#1,\quark@list,% appelle la macro récursive } \def\positem@ii#1#2,{% #1=position courante #2=\relax + élément courant \expandafter\def\expandafter\current@item\expandafter{\gobone#2}% \ifx\current@item\quark@list\expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\def\pos@item{0}% si la fin de liste est atteinte, renvoyer 0 \positem@endprocess% et aller au hook }% sinon {\ifx\current@item\sought@item\expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\def\pos@item{#1}% si la position est la bonne, définir la position \positem@goblist% et manger les éléments restants }% si la position n'est pas la bonne, recommencer en incrémentant #1 {\exparg\positem@ii{\number\numexpr#1+1}\relax }% }% } \def\positem@goblist#1\quark@list,{\positem@endprocess}% manger la liste et aller au hook \def\positemtocs#1#2#3{% #1=\ #2=élément à chercher #3=macro à définir \def\positem@endprocess{\let#3=\pos@item}% hook : mettre le résultat dans #3 \exparg\positem@i{#1}{#2}% 1-développe la \ } \catcode`\@12 \def\liste{a,bcd,{ef},g,hij,,kl} a) \positem\liste{g}\qquad b) \positem\liste{ef}\qquad c) \positem\liste{{ef}}\qquad d) \positem\liste{}\medbreak \def\liste{a,bcd,{ef},g,hij,,kl} a) \positemtocs\liste{g}\foo\meaning\foo\qquad b) \positemtocs\liste{ef}\foo\meaning\foo\qquad c) \positemtocs\liste{{ef}}\foo\meaning\foo\qquad d) \positemtocs\liste{}\foo\meaning\foo\qquad ****************** Fin code ****************** ****************** Code 316 ****************** \catcode`\@11 \def\quark@list{\quark@list}% quark de fin de liste \def\insitem#1#2#3{% #1 = macro #2=position cherchée #3=élément à insérer \let\item@list=\empty \ifnum#2<1 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi % si position < 1 {\addtomacro\item@list{#3,}% ajouter l'élement à insérer en premier \eaddtomacro\item@list#1% puis la liste entière } % si la position > 1 {% définir la macro récursive \def\insitem@i##1##2,{% ##1 = position courante ##2=\relax + élément courant \expandafter\ifx\expandafter\quark@list\gobone##2% \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\addtomacro\item@list{#3}}% si fin de liste, ajouter l'élément en dernier {% sinon, si la position cherchée est atteinte \ifnum##1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\addtomacro\item@list{#3,}% ajouter l'élement \add@remainlist##2,% et ##2 (en supprimant le \relax) et le reste }% si la position n'est pas atteinte {\eaddtomacro\item@list{\gobone##2,}% ajouter l'élément \exparg\insitem@i{\number\numexpr##1+1}\relax% et recommencer }% }% }% % appel de la macro récursive \expandafter\insitem@i\expandafter1\expandafter\relax#1,\quark@list,% }% \let#1=\item@list% rendre #1 égal au résultat } \def\add@remainlist#1,\quark@list,{% \eaddtomacro\item@list{\gobone#1}% ajouter #1 ainsi que les autres } \catcode`\@12 \def\liste{a,bra,{cA},da,brA}\insitem\liste3{XX}\meaning\liste\par \def\liste{a,bra,{cA},da,brA}\insitem\liste0{XX}\meaning\liste\par \def\liste{a,bra,{cA},da,brA}\insitem\liste9{XX}\meaning\liste ****************** Fin code ****************** ****************** Code 317 ****************** \catcode`\@11 \def\quark@list{\quark@list}% quark de fin de liste \def\delitem#1#2{% #1 = macro #2=position cherchée \ifnum#2>0 % ne faire quelque chose que si la position est >0 \def\delitem@i##1##2,{% ##1 = position courante ##2=\relax + élément courant \expandafter\ifx\expandafter\quark@list\gobone##2% \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {}% si fin de liste, ne rien faire {% sinon, si la position cherchée est atteinte \ifnum##1=#2 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\add@reaminingitems% et ##2 (en supprimant le \relax) et le reste }% si la position n'est pas la bonne {\eaddtomacro\item@list{\gobone##2,}% ajouter l'élément \exparg\delitem@i{\number\numexpr##1+1}\relax% et recommencer }% }% }% \let\item@list=\empty% initialiser la macro tempporaire % appel de la macro récursive \expandafter\delitem@i\expandafter1\expandafter\relax#1,\quark@list,% \let#1=\item@list% rendre #1 égal au résultat \fi } \def\add@reaminingitems#1\quark@list,{% \eaddtomacro\item@list{#1}% ajouter tout jusqu'au quark } \catcode`\@12 \for\xx=0 to 8 \do{% \def\liste{a,bcd,{ef},g,hij,kl} position \xx{} : \expandafter\delitem\expandafter\liste\xx\meaning\liste.\par } ****************** Fin code ****************** ****************** Code 318 ****************** \catcode`\@11 \def\moveitem#1#2#3{% #1 = liste, #2=position départ, #3=position arrivée \ifnum#2>0 % ne faire quemque chose que si #2>0 \finditemtocs#1{#2}\temp@item% sauvegarder l'élément \delitem#1{#2}% supprimer l'élément \expsecond{\insitem#1{#3}}\temp@item% insérer l'élément \fi } \catcode`\@12 % déplace "b" en 5e position a) \def\liste{a,b,c,d,e,f} \moveitem\liste25 "\liste"\par % déplace "d" en 1e position b) \def\liste{a,b,c,d,e,f} \moveitem\liste41 "\liste"\par % déplace "c" en 9e position c) \def\liste{a,b,c,d,e,f} \moveitem\liste39 \liste"\par % position départ=0 -> sans effet d) \def\liste{a,b,c,d,e,f} \moveitem\liste02 "\liste" ****************** Fin code ****************** ****************** Code 319 ****************** \catcode`\@11 \def\runlist#1\with#2{% #1=liste #2=macro \def\runlist@i##1,{% \ifx\quark@list##1\relax\else% si la fin n'est pas atteinte \addtomacro\collect@run{#2{##1}}% ajouter "\{<élément>}"" \expandafter\runlist@i% et recommencer en lisant l'élément suivant \fi }% \begingroup% fait la collecte dans un groupe \let\collect@run=\empty% initialiser la macro \expandafter\runlist@i#1,\quark@list,% appeler \runlist@i \expandafter\endgroup% ferme le groupe et détruit \collect@run \collect@run% après l'avoir développé ! } \catcode`\@12 \newcount\foocnt \foocnt=0 % compteur utilisé pour numéroter les éléments dans la macro \foo \def\foo#1{% la macro qui va exécuter chaque élément de la liste. #1 = l'élément \advance\foocnt1 % incrémente le compteur L'argument \number\foocnt\ est : {\bf #1}\par% } \def\liste{a,bra,ca,da,BRA}% \runlist\liste\with\foo% ****************** Fin code ****************** ****************** Code 320 ****************** \catcode`\@11 \def\ifspacefirst#1{% \expandafter\ifspacefirst@i\detokenize{#1W} \@nil% "W" se prémunit d'un argument vide } \def\ifspacefirst@i#1 #2\@nil{\ifempty{#1}}% renvoyer vrai s'il n'y a rien avant " " \catcode`\@12 a) \ifspacefirst{a bc d}{vrai}{faux}\qquad b) \ifspacefirst{ a bc d}{vrai}{faux}\qquad c) \ifspacefirst{ }{vrai}{faux}\qquad d) \ifspacefirst{}{vrai}{faux}\qquad e) \ifspacefirst{{ } }{vrai}{faux}\qquad f) \ifspacefirst{ {x} }{vrai}{faux} ****************** Fin code ****************** ****************** Code 321 ****************** \catcode`\@11 \expandafter\def\expandafter\gobspace\space{} \def\removefirstspaces#1{% \ifspacefirst{#1}% si #1 commence par un espace {\exparg\removefirstspaces{\gobspace#1}}% recommencer sans le 1er espace {#1}% sinon, renvoyer l'argument } \catcode`\@12 a) "\removefirstspaces{12 {\bf3}4 567}"\qquad b) "\removefirstspaces{ 12 {\bf3}4 567}"\qquad c) \edef\foo{\space\space\space 12 34 567} "\exparg\removefirstspaces\foo" ****************** Fin code ****************** ****************** Code 322 ****************** \catcode`\@11 \def\removefirstspaces{% \romannumeral% lance le développement maximal \removefirstspaces@i% et passe la main à la macro récursive } \def\removefirstspaces@i#1{% \ifspacefirst{#1}% si #1 commence par un espace {\exparg\removefirstspaces@i{\gobspace#1}}% recommencer sans le 1er espace {\z@#1}% sinon, renvoyer l'argument où \z@ stoppe l'action de \romannumeral } \catcode`\@12 \long\def\>#1<{"\detokenize{#1}"} a) \expandafter\expandafter\expandafter\>\removefirstspaces{12 {\bf3}4 567}<\qquad b) \expandafter\expandafter\expandafter\>\removefirstspaces{ 12 {\bf3}4 567}<\qquad c) "\removefirstspaces{ 12 {\bf3}4 }" ****************** Fin code ****************** ****************** Code 323 ****************** \catcode`\@11 \edef\catcodezero@saved{\number\catcode0 }% stocke le catcode de ^^00 \catcode0=12 % le modifie à 12 \def\removelastspaces#1{% \romannumeral% lance le développement maximal \removelastspaces@i\relax#1^^00 ^^00\@nil% mettre un \relax au début % et passer la main à \removelastspaces@i } \def\removelastspaces@i#1 ^^00{% prendre ce qui est avant " W" \removelastspaces@ii#1^^00% ajouter "W" } \def\removelastspaces@ii#1^^00#2\@nil{% #1=ce qui est avant "W" #2=reliquat \ifspacefirst{#2}% si le reliquat commence par un espace {\removelastspaces@i#1^^00 ^^00\@nil}% recommencer sans passer par \removelastspaces {\expandafter\z@\gobone#1}% sinon supprimer le \relax ajouté au début % et stopper l'action de \romannumeral avec \z@ } \catcode0=\catcodezero@saved\relax% restaure le catcode de ^^00 \catcode`\@12 \long\def\>#1<{"\detokenize{#1}"} a) \expandafter\expandafter\expandafter\>\removelastspaces{ 12 {\bf3}4 }<\qquad b) \expandafter\expandafter\expandafter\>\removelastspaces{12 {\bf3}4}<\qquad c) "\removelastspaces{ 12 {\bf3}4 }" ****************** Fin code ****************** ****************** Code 324 ****************** \catcode`\@11 \def\removetrailspaces#1{% \romannumeral% lance le développement maximal \expandafter\expandafter\expandafter% le pont d'\expandafter \removelastspaces \expandafter\expandafter\expandafter% fait agir \removefirstspaces en premier {% \expandafter\expandafter\expandafter \z@% stoppe le développement initié par \romannumeral \removefirstspaces{#1}% }% } \catcode`\@12 \long\def\>#1<{"\detokenize{#1}"} a) \expandafter\expandafter\expandafter\>\removetrailspaces{ 12 {\bf3}4 }<\qquad b) \expandafter\expandafter\expandafter\>\removetrailspaces{12 {\bf3}4}<\par c) \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter \foo\expandafter\expandafter\expandafter{\removetrailspaces{ 12 {\bf3}4 }}% signification : \meaning\foo.\par c) exécution : "\foo"\par d) "\removetrailspaces{ 12 {\bf3}4 }" ****************** Fin code ****************** ****************** Code 325 ****************** \catcode`\@11 \def\sanitizelist#1{% #1 = liste \let\item@list\empty% initialise le réceptacle de la liste assainie \def\sanitizelist@endprocess{% définit le hook de fin \expandafter\remove@lastcomma\item@list\@nil% supprimer dernière virgule \let#1=\item@list% et assigner le résultat à #1 }% \expandafter\sanitizelist@i\expandafter\relax#1,\quark@list,% aller à la macro récursive } \def\sanitizelist@i#1,{% #1="\relax" + élément courant \expandafter\ifx\expandafter\quark@list\gobone#1% \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\sanitizelist@endprocess% si fin de liste, hook de fin }% si la fin de la liste n'est pas atteinte : {\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter \addtomacro% 1-développer \gobone pour retirer \relax \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter \item@list% puis 2-développer \removetrailspaces \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter {\expandafter\removetrailspaces\expandafter{\gobone#1},}% "#1" \sanitizelist@i\relax% et continuer avec l'élément suivant }% } \catcode`\@12 \frboxsep=0pt % encadrer au plus proche \def\foo#1{\frbox{\tt\strut#1} }% boite avec un strut et en fonte "tt" puis espace \def\liste{ Programmer, en \TeX{} ,est ,{\bf facile}, et utile } a) \runlist\liste\with\foo% encadrer les items b) \sanitizelist\liste% supprimer les espaces inutiles des items \runlist\liste\with\foo ****************** Fin code ****************** ****************** Code 326 ****************** \catcode`\@11 \def\boxsentence#1{% \leavevmode% se mettre en mode horizontal \boxsentence@i#1\quark% transmet "#1+\quark" à boxsentence@i } \def\boxsentence@i#1{% #1= argument lu \def\current@arg{#1}% stocke l'argument dans une macro temporaire \unless\ifx\quark\current@arg% si la fin n'est pas atteinte \frbox{#1}% encadrer cet argument \expandafter\boxsentence@i% lire l'argument suivant \fi } \catcode`@12 \frboxsep=1pt \frboxrule=0.2pt \boxsentence{Programmer en \TeX\ est facile} ****************** Fin code ****************** ****************** Code 327 ****************** \catcode`\@11 \edef\star@macro{\string *}% stocke une étoile de catcode 12 \def\expo#1{% \def\temp@arg{#1}% stocke l'argument lu \ifx\star@macro\temp@arg\expandafter\firstoftwo\else\expandafter\secondoftwo\fi {% si l'argument est une étoile de catcode 12 $^\dag$% affiche une dague }% sinon {$^\ddag$% affiche une double dague {#1}% puis ré-écrit l'argument {#1} }% } A\expo B\expo*C ****************** Fin code ****************** ****************** Code 328 ****************** \def\boxsentence#1{% \readtok#1\quark% met le token d'arrêt \quark à la fin de #1 } \def\readtok{\afterassignment\cmptok\let\nxttok= } \def\cmptok{% \unless\ifx\nxttok\quark% si la fin n'est pas atteinte \frbox{\nxttok}% encadrer le token lu \expandafter\readtok% puis aller lire le suivant \fi } \frboxsep=1pt \frboxrule=0.2pt \leavevmode\boxsentence{Programmer en \TeX\ est facile} ****************** Fin code ****************** ****************** Code 329 ****************** \def\permutstart{\afterassignment\cmptok\let\nxttok= } \def\permutend{\permutend}% \def\cmptok{% \unless\ifx\permutend\nxttok% tant que la fin n'est pas atteinte : \ifxcase\nxttok ae% si le token lu est "a", afficher un "e" ei io ou uy ya% etc pour les autres lettres \elseif \nxttok% si ce n'est aucune voyelle, afficher le token \endif \expandafter\permutstart% aller lire le token suivant \fi } \permutstart Un "a" puis "e puis "i" ensuite, un "o", un "u" et "y".\permutend ****************** Fin code ****************** ****************** Code 330 ****************** \def\boxsentence#1{\readtok#1\boxsentence} \def\readtok{\afterassignment\cmptok\let\nxttok= } \def\cmptok{% %\show\nxttok% à décommenter pour débogage \unless\ifx\nxttok\boxsentence \frbox{\nxttok}% \expandafter\readtok \fi } \frboxsep=1pt \frboxrule=0.2pt \leavevmode\boxsentence{Pro{gra}mmer en \TeX\ est facile} ****************** Fin code ****************** ****************** Code 331 ****************** \catcode`\@11 \def\Litterate{% \begingroup% ouvrir un groupe \tt% et adopter une fonte à chasse fixe \afterassignment\Litterate@i% après l'assignation, aller à \Litterate@i \expandafter\let\expandafter\lim@tok\expandafter=\string% \lim@tok = token délimiteur } \def\Litterate@i{% \afterassignment\Litterate@ii%après avoir lu le prochain token, aller à \Litterate@ii \expandafter\let\expandafter\nxttok\expandafter=\string% lit le token suivant } \def\Litterate@ii{% \ifx\nxttok\lim@tok% si le token suivant="token délimiteur" \endgroup% fermer le groupe et finir \else \nxttok% sinon, afficher ce token \expandafter\Litterate@i% et lire le token suivant \fi } \catcode`\@12 \Litterate|Programmer en \TeX {} est << facile >> !| ****************** Fin code ****************** ****************** Code 332 ****************** \catcode`\@11 \def\Litterate{% \begingroup% ouvrir un groupe \tt% et adopter une fonte à chasse fixe \afterassignment\Litterate@i% après l'assignation, aller à \Litterate@i \expandafter\let\expandafter\lim@tok\expandafter=\string% \lim@tok = token délimiteur } \def\Litterate@i{% \afterassignment\Litterate@ii% après avoir lu un token, aller à \Litterate@ii \expandafter\expandafter\expandafter% 1-développer \string \let \expandafter\expandafter\expandafter% puis 1-développer \space en " " \nxttok \expandafter\expandafter\expandafter =\expandafter\space\string% et lire le token obtenu } \def\Litterate@ii{% \ifx\nxttok\lim@tok% si le token suivant="token délimiteur" \endgroup% fermer le groupe et finir \else \nxttok% sinon, afficher ce token \expandafter\Litterate@i% et lire le token suivant \fi } \catcode`\@12 \Litterate|Programmer en \TeX {} est << facile >> : $~$^_#| ****************** Fin code ****************** ****************** Code 333 ****************** \catcode`@11 \newskip\ltr@spc \def\letterspace#1#2{% \ltr@spc=#1\relax % assigne le ressort \letterspace@i#2\letterspace@i% appelle la macro \letterspace@i } \def\letterspace@i{% \afterassignment\letterspace@ii \let\nxttok= } \def\letterspace@ii{% \ifx\nxttok\letterspace@i% si la fin est atteinte \unskip% supprimer le dernier ressort qui est de trop \else \nxttok% afficher le token \hskip\ltr@spc\relax% insère le ressort \expandafter\letterspace@i% va lire le token suivant \fi } \catcode`@12 Voici "\letterspace{0.3em}{des lettres espacées}" et voici "\letterspace{-0.075em}{de la compression}". ****************** Fin code ****************** ****************** Code 334 ****************** \catcode`@11 \newcount\test@cnt \def\ifinteger#1{% \ifstart{#1}{-}% si "-" est au début de #1 {\exparg\ifinteger{\gobone#1}% l'enlever et recommencer } {\ifempty{#1}% sinon, si #1 est vide \secondoftwo% lire l'argument {\afterassignment\after@number% sinon, après l'assignation, aller à \after@number \test@cnt=0#1\relax% faire l'assignation %(\relax termine le nombre et n'est pas mangé) }% }% } \def\after@number#1\relax{% #1 est ce qui reste après l'assignation \ifempty{#1}% teste si #1 est vide et lit l'argument ou qui suit } \catcode`@12 1) \ifinteger{5644}{oui}{non}\qquad 2) \ifinteger{-987}{oui}{non}\qquad 3) \ifinteger{6a87}{oui}{non}\qquad 4) \ifinteger{abcd}{oui}{non}\qquad 5) \ifinteger{-a}{oui}{non}\qquad 6) \ifinteger{-}{oui}{non}\qquad 7) \ifinteger{3.14}{oui}{non} ****************** Fin code ****************** ****************** Code 335 ****************** \catcode`\@11 \def\teststar{% \futurelet\nxttok\teststar@i% % pioche le token suivant puis aller à \teststar@i } \def\teststar@i{% \ifx *\nxttok\expandafter\firstoftwo\else\expandafter\secondoftwo\fi {% si le token suivant est une étoile \afterassignment\teststar@bf% après avoir lu "*", aller à \teststar@bf \let\nxttok= % lire l'étoile }% si le token n'est pas une étoile {\teststar@it% aller à \teststar@it }% } \def\teststar@bf#1{{\bf#1}}% lit l'argument et le compose en gras dans un groupe \def\teststar@it#1{{\it#1\/}}% lit l'argument et le compose en italique \catcode`\@12 Un essai \teststar{réussi} et un essai \teststar*{étoilé} réussi aussi. ****************** Fin code ****************** ****************** Code 336 ****************** \catcode`@11 \def\deftok#1#2{\let#1= #2\empty}% définit le token #1 (\empty au cas où #2 est vide) \deftok\sptoken{ } \def\ifnexttok#1#2#3{% lit les 3 arguments : #1=token #2=code vrai #3=code faux \deftok\test@tok{#1}% stocke le token cherché \def\true@code{#2}\def\false@code{#3}% et les codes à exécuter \ifnexttok@i% aller à la macro récursive } \def\ifnexttok@i{% \futurelet\nxttok\ifnexttok@ii% piocher le token d'après et aller à \ifnexttok@ii } \def\ifnexttok@ii{% \ifx\nxttok\sptoken% si le prochain token est un espace \def\donext{% \afterassignment\ifnexttok@i% recommencer après \let\nxttok= % après avoir absorbé cet espace }% \else \ifx\nxttok\test@tok% si le prochain token est celui cherché \let\donext\true@code% exécuter le code vrai \else \let\donext\false@code% sinon code faux \fi \fi \donext% faire l'action décidée ci-dessus } \catcode`@12 \ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : }W.\par \ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : }a.\par \ifnexttok *{je vais lire une étoile : }{je ne vais pas lire une étoile : } *.\par \ifnexttok *{je vais lire une étoile : }{je ne vais pas lire une étoile : }a. ****************** Fin code ****************** ****************** Code 337 ****************** \catcode`@11 \newif\iftestspace \testspacefalse \def\deftok#1#2{\let#1= #2}\deftok\sptoken{ } \def\ifnexttok#1#2#3{% #1=token #2=code vrai #3=code faux \let\test@tok= #1% stocke le token à tester \def\true@code{#2}\def\false@code{#3}% et les codes à exécuter \iftestspace \def\ifnexttok@i{\futurelet\nxttok\ifnexttok@ii}% \else \def\ifnexttok@i{\futurelet\nxttok\ifnexttok@iii}% \fi% après avoir défini la macro récursive selon le booléen, \ifnexttok@i% l'exécuter } \def\ifnexttok@ii{% macro "normale" qui ne teste pas les espaces \ifx\nxttok\test@tok \expandafter\true@code% exécuter le code vrai \else \expandafter\false@code% sinon code faux \fi } \def\ifnexttok@iii{% macro qui ignore les espaces \ifx\nxttok\sptoken% si le prochain token est un espace \def\donext{% \afterassignment\ifnexttok@i% lire le token d'après \let\nxttok= % après avoir absorbé l'espace }% \else \let\donext\ifnexttok@ii% sinon, faire le test "normal" \fi \donext% faire l'action décidée ci-dessus } \catcode`@12 \testspacefalse \ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : }W.\par \ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : } W.\medbreak \testspacetrue \ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : }W.\par \ifnexttok W{je vais lire un W : }{je ne vais pas lire un W : } W. ****************** Fin code ****************** ****************** Code 338 ****************** \def\ifstarred#1{\ifnexttok*{\firstoftwo{#1}}} \catcode`\@11 \def\teststar{\ifstarred{\teststar@bf}{\teststar@it}} \def\teststar@bf#1{{\bf#1}} \def\teststar@it#1{{\it#1\/}} \catcode`\@12 Un essai \teststar{réussi} et un essai \teststar*{étoilé} réussi aussi. ****************** Fin code ****************** ****************** Code 339 ****************** \catcode`\@11 \def\parsestop{\parsestop}% définit la macro-quark se trouvant en fin de code \newtoks\code@toks% registre contenant le code lu \def\parseadd{\addtotoks\code@toks} \def\parse{% \code@toks={}% initialise \code@toks à vide \parse@i% et passe la main à \parse@i } \def\parse@i{\futurelet\nxttok\parse@ii}% lit le prochain token et va à \parse@ii \def\parse@ii{% \ifxcase\nxttok \parsestop\parsestop@i% si la fin va être atteinte, aller à \parsestop@i \sptoken\read@space% si un espace va être lu, aller à \read@space \bgroup\read@bracearg% si une accolade ouvrante aller à \read@bracearg \elseif \testtoken% dans les autres cas, aller à \testtoken \endif } \def\parsestop@i\parsestop{% la fin est atteinte : manger \parsestop \the\code@toks% afficher le registre de tokens } \expandafter\def\expandafter\read@space\space{% manger un espace dans le code \testtoken{ }% et aller à \testtoken } \def\read@bracearg#1{% l'argument entre accolades est lu \parseadd{{#1}}% puis ajouté (entre accolades) tel quel à \code@toks \parse@i% ensuite, lire le prochain token } \catcode`@12 {\bf Exemple 1 :} \catcode`@11 \def\testtoken#1{% macro qui teste le token \ifxcase{#1} a{\parseadd{e}}% remplacer a par e e{\parseadd{i}}% e par i i{\parseadd{o}}% i par o o{\parseadd{u}}% o par u u{\parseadd{y}}% u par y y{\parseadd{a}}% y par a \elseif \parseadd{#1}% sinon, ajouter le token tel quel \endif \parse@i% aller lire le token suivant }% \catcode`@12 \parse Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es {\bf catalanes, corses ou grecques} assez inattendues. \parsestop\medbreak {\bf Exemple 2 :} \leavevmode \frboxsep=1pt \catcode`@11 \def\testtoken#1{% \ifxcase{#1}% si #1 est un espace { }{\parseadd{\hskip 0.75em }}% ajouter un espace \ {\parseadd{\hskip 0.75em }} \elseif \parseadd{\frbox{#1}}% sinon, l'encadrer \endif \parse@i}% \catcode`@12 \parse Programmer en \TeX\ est facile\parsestop\medbreak {\bf Exemple 3 :} \catcode`@11 \def\testtoken#1{% \ifx a#1\parseadd{X}\else\parseadd{#1}\fi% remplace les "a" par des "X" \parse@i } \catcode`@12 \parse a\bgroup\bf braca\egroup dabra\parsestop ****************** Fin code ****************** ****************** Code 340 ****************** \catcode`\@11 \def\ifbracefirst#1{% \ifnum\catcode\expandafter\expandafter\expandafter `\expandafter\firstto@nil\detokenize{#1W}\@nil=1 % tester si son catcode est 1 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi } \catcode`\@12 a) \ifbracefirst{123 456}{vrai}{faux}\qquad b) \ifbracefirst{\bgroup12\egroup3 456}{vrai}{faux}\qquad c) \ifbracefirst{{12}3 456}{vrai}{faux}\qquad d) \ifbracefirst{1{2}3 456}{vrai}{faux}\qquad \begingroup \catcode`[=1 \catcode`]=2 % les crochets deviennent des accolades e) \ifbracefirst[[]123 456][vrai][faux] \endgroup ****************** Fin code ****************** ****************** Code 341 ****************** \catcode`\@11 \def\ifbracefirst#1{% teste si #1 commence par un token de catcode 1 \ifspacefirst{#1}% si #1 commence par un espace {\secondoftwo}% renvoyer faux {\ifnum\catcode\expandafter\expandafter\expandafter `\expandafter\firstto@nil\detokenize{#1W}\@nil=1 % tester si son catcode est 1 \expandafter\firstoftwo \else \expandafter\secondoftwo \fi }% } \catcode`\@12 a) \ifbracefirst{}{vrai}{faux}\qquad b) \ifbracefirst{ }{vrai}{faux}\qquad c) \ifbracefirst{ {}}{vrai}{faux} ****************** Fin code ****************** ****************** Code 342 ****************** \catcode`\@11 \def\parsestop@i\parsestop{% la fin va être atteinte \detokenize\expandafter{\the\code@toks}% afficher le contenu du registre } \def\read@bracearg{% \read@bracearg@i\relax% ajoute un \relax avant de passer la main à \read@bracearg@i } \def\read@bracearg@i#1\parsestop{% #1 = tout jusqu'à \parsestop \exparg\ifbracefirst{\gobone#1}% retire le \relax et teste si #1 commence par "{" {\expandafter\read@bracearg@ii\gobone#1\parsestop}% si oui, aller à \readbrace@ii {\expandafter\testtoken\gobone#1\parsestop}% sinon, aller à \testtoken } \def\read@bracearg@ii#1{% lit l'argument entre accolades \parseadd{{#1}}% ajoute cet argument entre accolades \parse@i% aller lire le token suivant } \def\testtoken#1{% \parseadd{#1}% ajouter le token tel quel \parse@i% aller lire le token suivant } \catcode`\@12 \parse a\hbox\bgroup\bf braca\egroup {da}{}b{ra}\parsestop ****************** Fin code ****************** ****************** Code 343 ****************** \catcode`\@11 \def\parsestop{\parsestop}% définit le quark se trouvant en fin de code \newtoks\code@toks% alloue le registre contenant le code lu \def\parseadd#1{\code@toks\expandafter{\the\code@toks#1}} \newif\ifparse@group \def\parse{% \code@toks{}% initialise le collecteur de tokens \ifstarred% teste si la macro est étoilée {\parse@grouptrue\parse@i}% mettre le booléen à vrai {\parse@groupfalse\parse@i}% sinon à faux } \def\parse@i{\futurelet\nxttok\parse@ii}% lit le prochain token % et va à \parse@ii \def\parse@ii{% \ifxcase\nxttok \parsestop\parsestop@i% si la fin va être atteinte, aller à \parsestop@i \sptoken\read@space% si un espace va être lu, aller à \read@space \bgroup\read@bracearg% si une accolade ouvrante aller à \read@bracearg \elseif \testtoken% dans les autres cas, aller à \testtoken \endif } \def\parsestop@i\parsestop{% la fin est atteinte \the\code@toks% afficher le registre de tokens } \expandafter\def\expandafter\read@space\space{% \read@space mange un espace dans le code \testtoken{ }% et va à \testtoken } \def\read@bracearg{% \read@bracearg@i\relax% ajoute un \relax avant de passer la main à \read@bracearg@i } \def\read@bracearg@i#1\parsestop{% l'argument tout jusqu'à \parsestop \expsecond\ifbracefirst{\gobone#1}% retire le \relax et teste si #1 commence par "{" {\expandafter\read@bracearg@ii\gobone#1\parsestop}% lire l'argument entre accolades {\expandafter\testtoken\gobone#1\parsestop}% sinon, tester le token } \def\read@bracearg@ii#1{% l'argument entre accolades est lu \ifparse@group\expandafter\firstoftwo\else\expandafter\secondoftwo\fi% si macro étoilée {\begingroup% ouvre un groupe pour parser l'intérieur de l'accolade \def\parsestop@i\parsestop{% redéfinir localement \parsestop@i pour \expandafter\endgroup% ne fermer le groupe qu'après avoir \expandafter\parseadd% 1-développé à l'extérieur du groupe \expandafter{\expandafter{\the\code@toks}}% \parse@i% puis va lire le token suivant }% \parse*#1\parsestop% <- le \parsestop@i fermera le groupe semi-simple } {\parseadd{{#1}}% macro non étoilée, on ajoute #1 tel quel entre accolades \parse@i% puis va lire le token suivant }% } \def\testtoken#1{% macro qui teste le token \ifxcase{#1} a{\parseadd{e}} e{\parseadd{i}} i{\parseadd{o}} o{\parseadd{u}} u{\parseadd{y}} y{\parseadd{a}} \elseif \parseadd{#1}% \endif \parse@i% aller lire le token suivant } \catcode`@12 \frboxsep=1pt a) \parse Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es {\bf catalanes, \frbox{corses} ou grecques} assez inattendues. \parsestop\medbreak b) \parse* Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es {\bf catalanes, \frbox{corses} ou grecques} assez inattendues. \parsestop ****************** Fin code ****************** ****************** Code 344 ****************** \catcode`\@11 \def\parse{% \code@toks{}% initialise le collecteur de tokens \ifstarred {\parse@grouptrue \ifnexttok{ }% si un espace suit l'étoile {\afterassignment\parse@i% aller à \parse@i \let\nxttok= }% après l'avoir mangé {\parse@i}% sinon, aller à \parse@i } {\parse@groupfalse \parse@i }% } \def\testtoken#1{% macro qui teste le token \ifxcase{#1} a{\parseadd{e}}e{\parseadd{i}}i{\parseadd{o}}o{\parseadd{u}} u{\parseadd{y}}y{\parseadd{a}} \elseif \parseadd{#1}% \endif \parse@i% aller lire le token suivant } \catcode`@12 \frboxsep=1pt a) \parse Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es {\bf catalanes, \frbox{corses} ou grecques} assez inattendues. \parsestop\medbreak b) \parse* Ce texte devenu \`a peine reconnaissable montre que le r\'esultat contient des sonorit\'es {\bf catalanes, \frbox{corses} ou grecques} assez inattendues. \parsestop ****************** Fin code ****************** ****************** Code 345 ****************** \catcode`\@11 \def\grab@first#1#2{% \ifx#1\empty\expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\let#2\empty% si #1 est vide, ne rien faire et assigner à #2 }% si #1 n'est pas vide {\def\arg@b{#2}% stocke la macro #2 dans \arg@b \exparg\ifbracefirst#1% si le 1er token de #1 est "{" {\expandafter\grab@arg#1\@nil#1% aller lire l'argument avec \grab@arg } {% sinon, développer #1 avant de le regarder avec \futurelet : \expandafter\futurelet\expandafter\nxttok\expandafter\test@nxttok#1\@nil#1% % puis aller à \test@nxttok }% }% } \def\test@nxttok{% si le premier token de l'arg #1 de \grab@first est \ifx\nxttok\sptoken% un espace \expandafter\grab@spc% aller le lire avec \grab@spc \else \expandafter\grab@tok% sinon, lire le token avec \grab@tok \fi } \def\grab@arg#1{% assigne l'argument de \grab@first (mis entre accolades) \expandafter\def\arg@b{{#1}}% à #2 \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first } \expandafter\def\expandafter\grab@spc\space{% \expandafter\def\arg@b{ }% assigne un espace à #2 de \grab@first \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first } \def\grab@tok#1{%% assigne le premier token de l'arg #1 de \grab@first \expandafter\def\arg@b{#1}% à la macro #2 de \grab@first \assign@tonil\relax% puis, assigne le reste à #1 de \grab@first } % assigne tout ce qui reste à lire (moins le "\relax") à la macro % #1 de \grab@first \def\assign@tonil#1\@nil#2{\expsecond{\def#2}{\gobone#1}} \tt a) \def\foo{1 {2 3} 4}\grab@first\foo\bar "\meaning\bar"\qquad"\meaning\foo" b) \def\foo{ 1 {2 3} 4}\grab@first\foo\bar "\meaning\bar"\qquad"\meaning\foo"" c) \def\foo{{1 2} 3 4}\grab@first\foo\bar "\meaning\bar"\qquad"\meaning\foo" ****************** Fin code ****************** ****************** Code 346 ****************** \catcode`\@11 \def\ifstartwith#1#2{% #1= #2= \ifempty{#2} \firstoftwo% si est vide, renvoyer vrai {\ifempty{#1}% si est vide et non vide \secondoftwo% renvoyer faux {\def\startwith@code{#1}\def\startwith@pattern{#2}% \ifstartwith@i% dans les autres cas, aller à \ifstartwith@i }% }% } \def\ifstartwith@i{% \grab@first\startwith@code\first@code% extrait le premier "argument" de \grab@first\startwith@pattern\first@pattern% et celui de \ifx\first@code\first@pattern\expandafter\firstoftwo\else\expandafter\secondoftwo\fi {% si les deux arguments élargis sont égaux \exparg\ifempty\startwith@pattern \firstoftwo% et que ne contient plus rien => vrai {\exparg\ifempty\startwith@code \secondoftwo% si ne contient plus rien => faux \ifstartwith@i% sinon poursuivre les tests }% } \secondoftwo% si les deux argument élargis sont différents, renvoyer faux } \catcode`\@12 1) \ifstartwith{a b c}{a b}{oui}{non}\quad 2) \ifstartwith{a b}{a b c}{oui}{non}\quad 3) \ifstartwith{ 123 }{ }{oui}{non}\quad 4) \ifstartwith{ 123 }{1}{oui}{non}\quad 5) \ifstartwith{1{2} 3}{12}{oui}{non}\quad 6) \ifstartwith{1{2} 3}{1{2} }{oui}{non}\quad 7) \ifstartwith{{} {}}{ }{oui}{non}\quad 8) \ifstartwith{{} {}}{{} }{oui}{non}\quad 9) \ifstartwith{ {12} a}{ }{oui}{non}\quad10) \ifstartwith{ {12} a}{ {1}}{oui}{non} ****************** Fin code ****************** ****************** Code 347 ****************** \catcode`\@11 \def\ifcontain#1#2{% #1 contient-il #2? \def\main@arg{#1}\def\pattern@arg{#2}% stocke le et le \ifempty{#2} \firstoftwo% si #2 est vide => vrai {\ifempty{#1} \secondoftwo% si #1 est vide et pas #2 => faux \ifcontain@i% sinon, aller faire les tests }% } \def\ifcontain@i{% \exptwoargs\ifstartwith\main@arg\pattern@arg \firstoftwo% si motif est au début de code => vrai {\exparg\ifempty\main@arg \secondoftwo% sinon, si code est vide => faux {\grab@first\main@arg\aux@arg% autrement, manger le 1er "argument" de code \ifcontain@i% et recommencer }% }% } \catcode`\@12 1) \ifcontain{abc def}{c }{oui}{non}\quad 2) \ifcontain{abc def}{cd}{oui}{non}\quad 3) \ifcontain{12 34 5}{1 }{oui}{non}\quad 4) \ifcontain{12 34 5}{ }{oui}{non}\quad 5) \ifcontain{a{b c}d}{b c}{oui}{non}\quad 6) \ifcontain{a{b c}d}{{b c}}{oui}{non}\quad 7) \ifcontain{{} {}}{ }{oui}{non}\quad 8) \ifcontain{{} {}}{{}}{oui}{non} ****************** Fin code ****************** ****************** Code 348 ****************** \catcode`@11 \newif\ifin@group \def\ifcontain{% \ifstarred {\in@groupfalse\ifcontain@i\ifcontain@star}% {\ifcontain@i\ifcontain@nostar}} \def\ifcontain@i#1#2#3{% #1 = macro à appeler selon étoile ou pas. #2 = code. #3 = motif \def\main@arg{#2}\def\pattern@arg{#3}% \ifempty{#3} \firstoftwo {\ifempty{#2} \secondoftwo #1% aller à \ifcontain@star ou \ifcontain@nostar }% } \def\ifcontain@nostar{% \exptwoargs\ifstartwith\main@arg\pattern@arg \firstoftwo% si motif est au début de code => vrai {\exparg\ifempty\main@arg \secondoftwo% sinon, si code est vide => faux {\grab@first\main@arg\aux@arg% autrement, manger le 1er "argument" de code \ifcontain@nostar% et recommencer }% }% } \def\ifcontain@star{% \expandafter\ifbracefirst\expandafter{\main@arg}% si code commence par "{" {\grab@first\main@arg\aux@arg% enlever {} de main@arg \begingroup% ouvrir un groupe \in@grouptrue% mettre le booléen à vrai \expandafter\def\expandafter\main@arg\aux@arg% assigner "argument" à \main@arg \ifcontain@star% et recommencer avec ce nouveau \main@arg }% si code ne commence pas par "{" {\exptwoargs\ifstartwith\main@arg\pattern@arg% si motif est au début de code \return@true% renvoyer vrai {\expandafter\ifempty\expandafter{\main@arg}% si code est vide {\ifin@group% et que l'on est dans un groupe \endgroup \expandafter\ifcontain@star% en sortir et recommencer \else% si on n'est pas dans un groupe, le code a été parcouru \expandafter\secondoftwo% sans trouver => renvoyer faux \fi }% si code n'est pas vide {\grab@first\main@arg\startwith@code% manger le 1er "argument" de code \ifcontain@star% et recommencer }% }% }% } \def\return@true{% \ifin@group% tant qu'on est dans un groupe \endgroup \expandafter\return@true% en sortir et recommencer \else \expandafter\firstoftwo% sinon, renvoyer vrai \fi } \catcode`@12 1) \ifcontain{ab {c d}ef}{c}{oui}{non}\quad 2) \ifcontain*{ab {c d}ef}{c}{oui}{non} ****************** Fin code ****************** ****************** Code 349 ****************** \catcode`@11 \newtoks\subst@toks% registre de tokens pour stocker le modifié \def\substitute{% \def\substitute@end{\the\subst@toks}% macro exécutée à la fin \ifstarred {\let\recurse@macro\substitute@star \substitute@i}% {\let\recurse@macro\substitute@nostar\substitute@i}% } \def\substitute@i#1#2#3#4{% #1=macro à appeler selon étoile ou pas. % #2= #3= #4= \def\code@arg{#2}\def\pattern@arg{#3}\def\subst@arg{#4}% \subst@toks={}% initialiser le collecteur à vide #1% aller à \substitute@star ou \substitute@nostar } \def\substitute@nostar{% \exptwoargs\ifstartwith\code@arg\pattern@arg% si le commence par {\eaddtotoks\subst@toks\subst@arg% ajouter \grab@first\code@arg\aux@code% manger le 1er "argument" de \substitute@nostar% et recommencer } {\expandafter\ifempty\expandafter{\code@arg}% {\substitute@end% sinon, si est vide => afficher le registre de tokens } {\grab@first\code@arg\aux@arg% autrement, manger le 1er "argument" de \eaddtotoks\subst@toks\aux@arg% et l'ajouter au registre de tokens \substitute@nostar% et recommencer }% }% } \def\substitute@star{% \expandafter\ifbracefirst\expandafter{\code@arg}% si commence par "{" {\grab@first\code@arg\aux@arg% enlever {} de \code@arg \begingroup% ouvrir un groupe \def\substitute@end{% modifier localement la macro exécutée à la fin \expandafter\endgroup\expandafter% avant de fermer le groupe \addtotoks\expandafter\subst@toks\expandafter% ajouter au registre hors du groupe {\expandafter{\the\subst@toks}}% ce qui est collecté localement, mis entre {} \substitute@star% puis recommencer }% \subst@toks{}% initialiser à vide \expandafter\def\expandafter\code@arg\aux@arg% % assigner "argument" au \substitute@star% et recommencer avec ce nouveau \code@arg }% si code ne commence pas par "{" {\exptwoargs\ifstartwith\code@arg\pattern@arg% si est au début de {\eaddtotoks\subst@toks\subst@arg% ajouter \grab@first\code@arg\aux@code% manger le 1er "argument" de \substitute@star% et recommencer } {\expandafter\ifempty\expandafter{\code@arg}% si est vide {\substitute@end% aller à la macro de fin }% si n'est pas vide {\grab@first\code@arg\aux@code% manger le 1er "argument" de \eaddtotoks\subst@toks\aux@code% et l'ajouter au registre \substitute@star% et recommencer }% }% }% } \catcode`@12 1) \substitute{ab{\bf racada}bra}{a}{W}\qquad 2) \substitute*{ab{\bf racada}bra}{a}{W}\qquad 3) \substitute{12\frbox{\bf 34}56}{\bf}{\it}\qquad 4) \substitute*{12\frbox{\bf 34}56}{\bf}{\it} ****************** Fin code ****************** ****************** Code 350 ****************** \catcode`@11 \newtoks\subst@toks% registre de tokens pour stocker le modifié \def\substitute{% \def\substitute@end{\the\subst@toks}% macro exécutée à la fin \ifstarred {\let\recurse@macro\substitute@star \substitute@i}% {\let\recurse@macro\substitute@nostar\substitute@i}% } \def\substitute@i#1#2#3{% #1= #2= #3= \def\code@arg{#1}\def\pattern@arg{#2}\def\subst@arg{#3}% \subst@toks={}% initialiser à vide \recurse@macro% aller à \substitute@star ou \substitute@nostar } \def\substitute@nostar{% \exptwoargs\ifstartwith\code@arg\pattern@arg% si le commence par {\eaddtotoks\subst@toks\subst@arg% ajouter \grab@first\code@arg\aux@code% manger le 1er "argument" de \recurse@macro% et recommencer } {\expandafter\ifempty\expandafter{\code@arg}% \substitute@end% sinon, si est vide => afficher le registre de tokens {\grab@first\code@arg\aux@arg% autrement, manger le 1er "argument" de \eaddtotoks\subst@toks\aux@arg% et l'ajouter au registre de tokens \recurse@macro% et recommencer }% }% } \def\substitute@star{% \expandafter\ifbracefirst\expandafter{\code@arg}% si commence par "{" {\grab@first\code@arg\aux@arg% enlever {} de \code@arg \begingroup% ouvrir un groupe \def\substitute@end{% modifier localement la macro exécutée à la fin \expandafter\endgroup\expandafter% avant de fermer le groupe \addtotoks\expandafter\subst@toks\expandafter% ajouter au registre hors du groupe {\expandafter{\the\subst@toks}}% ce qui est collecté localement, mis entre {} \recurse@macro% puis recommencer }% \subst@toks{}% initialiser à vide \expandafter\def\expandafter\code@arg\aux@arg% % assigner "argument" au \recurse@macro% et recommencer avec ce nouveau \code@arg }% si ne commence pas par "{" : \substitute@nostar% exécuter macro non étoilée pour une seule itération } \def\substitutetocs{% \ifstarred {\let\recurse@macro\substitute@star \substitutetocs@i}% {\let\recurse@macro\substitute@nostar\substitutetocs@i}% } \def\substitutetocs@i#1#2#3#4{% #1= #2= #3= #4=\ \def\substitute@end{\edef#4{\the\subst@toks}}% macro exécutée à la fin \substitute@i{#1}{#2}{#3}% continuer comme avec \substitute } \catcode`@12 \frboxsep=1pt 1) \substitute{ab{\bf racada}bra}{a}{W}\qquad 2) \substitute*{ab{\bf racada}bra}{a}{W}\qquad 3) \substitute{12\frbox{\bf 34}56}{\bf}{\it}\qquad 4) \substitute*{12\frbox{\bf 34}56}{\bf}{\it} \medbreak 1) \substitutetocs{ab{\bf racada}bra}{a}{W}\foo \meaning\foo\par 2) \substitutetocs*{ab{\bf racada}bra}{a}{W}\foo \meaning\foo\par 3) \substitutetocs{12\frbox{\bf 34}56}{\bf}{\it}\foo \meaning\foo\par 4) \substitutetocs*{12\frbox{\bf 34}56}{\bf}{\it}\foo \meaning\foo ****************** Fin code ****************** ****************** Code 351 ****************** \catcode`@11 \def\vecteur{% \ifnexttok[% si la macro est suivie d'un crochet \vecteur@i% aller à la macro à arguments délimités {\vecteur@i[1]}% sinon, ajouter l'argument optionnel par défaut entre crochets } \def\vecteur@i[#1]#2{% #1=arg optionnel #2=arg obligatoire $(x_{#1},\ldots,x_{#2})$ } \catcode`@12 1) \vecteur{n}\qquad 2) \vecteur[0]{k}\qquad 3) \vecteur[i]j \qquad4) \vecteur[]n ****************** Fin code ****************** ****************** Code 352 ****************** \catcode`@11 \def\forcemath#1{% compose #1 en mode math, quel que soit le mode en cours \ifmmode\expandafter\firstoftwo\else\expandafter\secondoftwo\fi {#1}{$#1$}% } \def\vecteur{% \ifnexttok[% si la macro est suivie d'un crochet \vecteur@i% aller à la macro à arguments délimités {\vecteur@i[1]}% sinon, ajouter l'argument optionnel par défaut entre crochets } \def\vecteur@i[#1]{% \ifnexttok(% si une parenthèse vient ensuite {\vecteur@ii[#1]}% aller à la macro à argument délimités {\vecteur@ii[#1](x)}% sinon, rajouter "x" comme argument optionnel } \def\vecteur@ii[#1](#2)#3{% #1 et #2=arg optionnel #3=arg obligatoire \forcemath{({#2}_{#1},\ldots,{#2}_{#3})}% } \catcode`@12 1) \vecteur{n}\qquad 2) \vecteur[0]{k}\qquad 3) \vecteur[i](y)j \qquad4) \vecteur[](\alpha)n ****************** Fin code ****************** ****************** Code 353 ****************** \catcode`@11 \def\vecteur{% \ifnexttok[% si la macro est suivie d'un crochet \vecteur@bracket% lire cet argument entre crochet {\ifnexttok(% sinon, si elle est suivie d'une parenthèse \vecteur@paren % lire cet argument entre parenthèses {\vecteur@i[1](x)}% sinon, transmettre les arguments par défaut }% } \def\vecteur@bracket[#1]{% \ifnexttok(% s'il y a une parenthèse après {\vecteur@i[#1]}% lire la parenthèse {\vecteur@i[#1](x)}% sinon, donne "x" comme argument par défaut } \def\vecteur@paren(#1){% \ifnexttok[% si le caractère suivant est un crochet {\vecteur@ii(#1)}% lire l'argument entre crochets {\vecteur@ii(#1)[1]}% sinon, donner "1" comme argument par défaut } \def\vecteur@i[#1](#2)#3{\forcemath{({#2}_{#1},\ldots,{#2}_{#3})}} \def\vecteur@ii(#1)[#2]{\vecteur@i[#2](#1)}% met les arg optionnels dans l'ordre \catcode`@12 1) \vecteur{n}\qquad 2) \vecteur[i](y){j}\qquad 3) \vecteur(y)[i]{j}\qquad 4) \vecteur[0]{k}\qquad 5) \vecteur(\alpha){n} ****************** Fin code ****************** ****************** Code 354 ****************** \catcode`\@11 \def\foo#1{% #1 -> lit le premier argument obligatoire \ifnexttok[% si le token suivant est un crochet {\foo@i{#1}}% aller à \foo@i qui va lire cet argument {\foo@i{#1}[xxx]}% sinon, transmettre [xxx] à foo@i } \def\foo@i#1[#2]#3#4{% lit l'arg obligatoire + arg optionnel + 2 arg obligatoires \ifnexttok[ {\foo@ii{#1}[#2]{#3}{#4}}% {\foo@ii{#1}[#2]{#3}{#4}[y]}% } \def\foo@ii#1[#2]#3#4[#5]#6{% lit tous les arguments 1="#1" 2="#2" 3="#3" 4="#4" 5="#5" 6="#6"% } \catcode`\@12 1) \foo{arg1}{arg2}{arg3}{arg4}\par 2) \foo{arg1}[OPT\_A]{arg2}{arg3}{arg4}\par 3) \foo{arg1}{arg2}{arg3}[OPT\_B]{arg4}\par 4) \foo{arg1}[OPT\_A]{arg2}{arg3}[OPT\_B]{arg4}\medbreak ****************** Fin code ****************** ****************** Code 355 ****************** \catcode`\@11 \newcount\macro@cnt% numéro à mettre dans le nom des sous macros \newcount\arg@cnt% compte le nombre d'arguments \newtoks\param@text% texte de paramètre des macros sous forme "#x" et/ou "[#x]" \newtoks\arg@text% arguments sous forme "{#x}" et/ou "[#x]" \def\newmacro#1{% % \macro@name construit le de la macro et éventuellement "@[]" \def\macro@name##1{\expandafter\gobone\string#1\ifnum##1>0 @[\romannumeral##1]\fi}% \macro@cnt=0 \arg@cnt=0 % initialise les compteurs \param@text{}\arg@text{}% vide les registres de texte de paramètre et d'argument \newmacro@i% va voir le prochain token } \def\newmacro@i{\futurelet\nxttok\newmacro@ii}% met le prochain token dans \nxttok... % ...puis va à la macro : \def\newmacro@ii{% \ifxcase\nxttok [\newmacro@optarg% si le prochain token est un crochet aller à \newmacro@optarg \bgroup% si c'est un accolade ouvrante % le texte de paramètre est fini et il faut définir la macro {\defname{\macro@name\macro@cnt\expandafter}% \the\param@text}% <- le {} est juste après, il n'est pas encore lu \elseif% sinon, c'est donc un chiffre \newmacro@arg% aller à \newmacro@arg \endif } \def\newmacro@optarg[#1]{% lit la valeur par défaut de l'argument optionnel % Définit la macro \@[] qui lit tous les arguments (optionnels ou pas) % jusqu'alors définis à l'aide de \param@text. Puis, cette macro testera si le prochain % token est un crochet \expandafter\edef\csname\macro@name\macro@cnt\expandafter\endcsname\the\param@text{% \noexpand\ifnexttok[% % si oui : la macro \@ le lira {\expandafter\noexpand\csname\macro@name{\numexpr\macro@cnt+1}\expandafter\endcsname \the\arg@text}% % si non : transmettre à \@ l'argument optionnel par défaut lu {\expandafter\noexpand\csname\macro@name{\numexpr\macro@cnt+1}\expandafter\endcsname \the\arg@text[\unexpanded{#1}]}% }% \advance\arg@cnt 1 % incrémenter le numéro d'argument % pour ajouter "[#]" à \param@text et à \arg@text \eaddtotoks\param@text{\expandafter[\expandafter##\number\arg@cnt]}% \eaddtotoks\arg@text {\expandafter[\expandafter##\number\arg@cnt]}% \advance\macro@cnt 1 % incrémenter le numéro de nom de macro \newmacro@i% va voir le token suivant } \def\newmacro@arg#1{% #1=nombre d'arguments obligatoires à ajouter % boucle qui ajoute "##etc" dans \param@text % et "{#}{#}etc" dans \arg@text \ifnum#1>\z@ % tant qu'on n'a pas ajouté le nombre de #x nécessaire \advance\arg@cnt 1 % incrémenter le numéro d'argument % pour ajouter #x à \param@text et {#x} à \arg@text \eaddtotoks\param@text{\expandafter##\number\arg@cnt}% \eaddtotoks\arg@text {\expandafter{\expandafter##\number\arg@cnt}}% \expandafter\newmacro@arg\expandafter{\number\numexpr#1-1\expandafter}% boucler \else% si les arguments sont tous ajoutés \expandafter\newmacro@i% lire le token suivant \fi } \catcode`\@12 \newmacro\foo 1[xxx]2[y]1{1="#1" 2="#2" 3="#3" 4="#4" 5="#5" 6="#6"} a) \foo{arg1}{arg2}{arg3}{arg4}\par b) \foo{arg1}[OPT\_A]{arg2}{arg3}{arg4}\par c) \foo{arg1}{arg2}{arg3}[OPT\_B]{arg4}\par d) \foo{arg1}[OPT\_A]{arg2}{arg3}[OPT\_B]{arg4}\medbreak \meaning\foo\par \expandafter\meaning\csname foo@[i]\endcsname\par \expandafter\meaning\csname foo@[ii]\endcsname ****************** Fin code ****************** ****************** Code 356 ****************** \newmacro\framebox[ULRD]1{% #1 = ULRD (Up, Down, Right, Left) % ne pas changer le mode H ou V en cours \hbox{% enferme dans une \hbox \uppercase{\ifin{#1}L}{\vrule width\frboxrule}{}% réglure gauche \vtop{% \vbox{% 1er élément de la \vtop \uppercase{\ifin{#1}U}{% si la réglure sup doit être tracée \hrule height\frboxrule% réglure supérieure \kern\frboxsep% espace haut } {}% \hbox{% \uppercase{\ifin{#1}L}{\kern\frboxsep}{}% espace gauche #2% contenu \uppercase{\ifin{#1}R}{\kern\frboxsep}{}% espace droite }% }% puis autres éléments de la \vtop, sous la ligne de base \uppercase{\ifin{#1}D}{% \kern\frboxsep% espace bas \hrule height\frboxrule% réglure inférieure }% {}% }% \uppercase{\ifin{#1}R}{\vrule width\frboxrule}{}% réglure droite }% } \frboxsep=1pt Boite \framebox{entière}, \framebox[ud]{Up down}, \framebox[LR]{Left Right}, \framebox[LU]{Left Up} et \framebox[rd]{Right Down}. ****************** Fin code ****************** ****************** Code 357 ****************** \catcode`\@11 \newtoks\eargs@toks \newtoks\eargs@temptoks \def\eargs[#1]#2{% #1=liste des développements #2=macro \eargs@toks{#2}% mettre la macro dans le collecteur de tokens \expandafter\eargs@i\detokenize{#1}\@nil% appeler \eargs@i avec % la liste des developpements } \def\eargs@i#1\@nil{% #1=liste des n-développements restant \ifempty{#1}% s'il n' y plus de n-développements {\the\eargs@toks}% exécuter la macro et ses arguments développés {\eargs@ii#1\@nil}% sinon appeller la macro qui lit un argument } % #1=n-développement actuel #2=liste des n-développements restants #3=argument lu \def\eargs@ii#1#2\@nil#3{% \if+#1% si #1="+", un \edef est demandé pour cet argument \edef\eargs@tempmacro{{#3}}% le stocker dans une macro temporaire \else% sinon \eargs@temptoks={#3}% stocker l'argument dans un registre temporaire \for\eargs@loop = 1 to #1\do 1 % faire #1 fois : {\eargs@temptoks=% 1-développer le 1er token du registre temporaire \expandafter\expandafter\expandafter{\the\eargs@temptoks}% }% puis le stocker dans la macro temporaire \edef\eargs@tempmacro{{\the\eargs@temptoks}}% \fi \eaddtotoks\eargs@toks\eargs@tempmacro% ajouter le contenu de la macro au collecteur \eargs@i#2\@nil% appeler \eargs@i avec les n-développements restants } \catcode`\@12 \def\foo#1#2#3#4#5{\detokenize{1="#1" 2="#2" 3="#3" 4="#4" 5="#5"}} \def\aaa{\bbb}\def\bbb{\ccc}\def\ccc{Bonjour} \eargs[0123+]\foo{\aaa\bbb}{\aaa\bbb}{\aaa\bbb}{\aaa\bbb}{\aaa\bbb}. ****************** Fin code ****************** ****************** Code 358 ****************** \catcode`\@11 \def\detectmark#1{% #1 est le marqueur \begingroup \catcode`#1=13 % rendra #1 actif après la macro \begingroup% pour les besoins du \lccode \lccode`\~=`#1 % transforme "~" en " #1 actif" \lowercase{\endgroup\def~##1~}{\markeffect{##1}}% \detectmark@i } \def\detectmark@i#1{% #1% exécute le code \endgroup% ferme le groupe, le marqueur perd son catcode actif } \catcode`\@12 a) \def\markeffect#1{{\bf #1}}% met en gras \detectmark+{Un +argument+ où les +marqueurs+ sont détectés} \medskip b) \def\markeffect#1{% met dans une boite \begingroup \frboxsep=1pt % modifie l'espacement entre texte et encadrement \frbox{\strut#1}% encadre \endgroup } \detectmark|{Un |argument| où les |marqueurs| sont détectés} \medskip c) \def\markeffect#1{$\vcenter{\hbox{#1}\hbox{#1}}$}% superpose 2 fois \detectmark`{Un `argument` où les `marqueurs` sont détectés} ****************** Fin code ****************** ****************** Code 359 ****************** \catcode`\@11 \def\detectmark#1{% \begingroup \catcode`#1=13 % rendra #1 actif après la macro \begingroup% pour les besoins du \lccode \lccode`\~=`#1 % transforme "~" en " #1 actif" \lowercase{\endgroup\def~##1~}{\markeffect{##1}}%% \detectmark@i } \def\detectmark@i#1{% #1% lit le code \endgroup% ferme le groupe, le marqueur perd son catcode actif } \catcode`\@12 \def\markeffect#1{{\bf #1}}% met en gras \frbox{\detectmark+{Un +argument+ où les +marqueurs+ sont détectés}} ****************** Fin code ****************** ****************** Code 360 ****************** \catcode`\@11 \newtoks\alter@toks% collecteur de tokens \def\alter#1#2{% #1= délimiteur #2 = macro à altérer \let\alter@macro#2% sauvegarde la macro \edef\alter@restorecatcode{% restaurera le catcode de #1 \catcode`\noexpand#1=\the\catcode`#1 }% \edef\alter@tmp{\let\noexpand\alter@markertoks= \string#1}% \alter@tmp% et sauvegarder le délimiteur après avoir mis son catcode à 12 \edef\alter@tmp{\def\noexpand\alter@readlitterate@i\string#1####1\string#1}% % développe les \string#1 pour que les arguments délimités aient % des délimiteurs de catcode 12 \alter@tmp{% <- comme si on écrivait "\def\alter@readlitterate@i#1##1#1" \endgroup% après avoir lu ##1 (tokens rendus inoffensifs), fermer le groupe \addtotoks\alter@toks{{\tt##1}}% ajouter ces tokens \alter@i% et aller lire le prochain token }% \alter@toks{}% initialise le collecteur de tokens \afterassignment\alter@i% aller lire le premier token après avoir \let\alter@tmptok= % mangé l'accolade ouvrante de l' qui suit } \def\alter@i{% lit le prochain token et va à \alter@ii \futurelet\alter@nxttok\alter@ii}% \def\alter@ii{% teste le token qui doit être lu \ifxcase\alter@nxttok% si le token à lire est \egroup \alter@stop% "}" : aller à \alterstop@i \sptoken \alter@readspc% " " : aller à \alter@readspc \bgroup \alter@readarg% "{" : aller à \alter@readarg \alter@markertoks \alter@readlitterate% "" : aller à \alter@readlitterate \elseif \alter@readtok% dans les autres cas, aller à \alter@readtok \endif } \def\alter@readlitterate{% le prochain token est le délimiteur \begingroup% ouvrir un groupe \for\alter@tmp=0to255\do{\catcode\alter@tmp=12 }% % mettre tous les catcodes à 12 \defactive{ }{\ }% sauf l'espace rendu actif \doforeach\alter@tmp\in{<,>,-,`,{,},'}% pour chaque motif de ligature {\unless\if\alter@tmp\alter@markertoks% s'il est différent du délimiteur % le rendre actif pour éviter la ligature \expandafter\alter@defligchar\alter@tmp \fi }% \alter@readlitterate@i% puis aller à \alter@readlitterate@i... % ...qui a été définie dans \alter } \def\alter@defligchar#1{% définit le caractère pour ne pas provoquer de ligature \defactive#1{\string#1{}}% } \expandafter\def\expandafter\alter@readspc\space{% mange un espace dans le code \addtotoks\alter@toks{ }% ajoute l'espace \alter@i% puis lire le token suivant } \def\alter@readarg{% le token qui suit est "{" \begingroup% ouvrir un groupe \def\alter@stop@ii{% et modifier localement la macro appelée à la toute fin, % après que l'accolade fermante ait été mangée (par \alterstop@i) \expandafter\endgroup% retarder la fermeture de groupe ouvert ci-dessus \expandafter\addtotoks\expandafter\alter@toks\expandafter {\expandafter{\the\alter@toks}}% % pour ajouter hors du groupe ce qui a été collecté à l'intérieur, % le tout mis entre accolades \alter@i% puis, lire le token suivant }% \alter@toks{}% au début du groupe, initialiser le collecteur \afterassignment\alter@i% aller lire le prochain token après \let\alter@tmptok= % avoir mangé l'accolade ouvrante } \def\alter@readtok#1{% le prochain token ne demande pas une action spéciale \addtotoks\alter@toks{#1}% l'ajouter au collecteur \alter@i% et aller lire le token suivant } \def\alter@stop{% le token à lire est "}" \afterassignment\alter@stop@ii% aller à \alter@stop@ii après \let\alter@tmptok= % avoir mangé l'accolade fermante } \def\alter@stop@ii{% donner à la \ tout ce qui a été récolté \expandafter\alter@macro\expandafter{\the\alter@toks}% \alter@restorecatcode% puis restaure le catcode du délimiteur } \catcode`@12 \frboxsep=1pt \alter|\frbox{Texte normal - |#& }| - texte normal - |_^ ##| - texte normal} \alter=\frbox{La macro =\alter= autorise du verbatim dans des commandes imbriquées \frbox{comme ici =\alter=}.} ****************** Fin code ****************** ****************** Code 361 ****************** \def\hello#1#2{Bonjour #1 et #2 !} \hello{foo}{bar}\par \alter|\identity{\hello{macro |{{{\foo|}{macro |\bar}|}} ****************** Fin code ****************** ****************** Code 362 ****************** \def\retokenize#1{% \immediate\openout\wtest=retokenize.tex % ouvre le fichier \immediate\write\wtest{\unexpanded{#1}}% y écrit l'argument \immediate\closeout\wtest% ferme le fichier \input retokenize.tex % lit le fichier selon les catcodes en vigueur \unskip% mange l'espace précédemment ajouté qui provient de la fin du fichier } \frboxsep=1pt 1) \frbox{Programmer en \catcode`\~=12 \TeX{} est~facile et~utile.}\par 2) \frbox{Programmer en \retokenize{\catcode`\~=12 \TeX{} est~facile} et~utile.}\par 3) \frbox{Programmer en \catcode`\~=12 \retokenize{\TeX{} est~facile} et~utile.} ****************** Fin code ****************** ****************** Code 363 ****************** \frboxsep=1pt 1) \frbox{Programmer \retokenize{\litterate|en \TeX {} est |}facile et utile.}\par 2) \frbox{Programmer \retokenize{\litterate|en \TeX{} est |}facile et utile.} ****************** Fin code ****************** ****************** Code 364 ****************** \frboxsep=1pt 1) \frbox{Programmer en \catcode`\~=12 \TeX{} est~facile et~utile.}\par 2) \frbox{Programmer en \scantokens{\catcode`\~=12 \TeX{} est~facile} et~utile.}\par 3) \frbox{Programmer en \catcode`\~=12 \scantokens{\TeX{} est~facile} et~utile.}\par 4) \frbox{Programmer \scantokens{\litterate|en \TeX {} est facile|} et utile.}\par 5) \frbox{Programmer \scantokens{\litterate|en \TeX{} est facile|} et utile.} ****************** Fin code ****************** ****************** Code 365 ****************** \scantokens{a}b ****************** Fin code ****************** ****************** Code 366 ****************** \begingroup\endlinechar=-1 \scantokens{a}b\endgroup ****************** Fin code ****************** ****************** Code 367 ****************** \edef\foo{\scantokens{Bonjour le monde}}% produit une erreur ****************** Fin code ****************** ****************** Code 368 ****************** \expandafter\def\expandafter\foo\expandafter {\scantokens{Bonjour le monde}}% produit une erreur ****************** Fin code ****************** ****************** Code 369 ****************** Voici la macro \string\foo\ : \foo. ****************** Fin code ****************** ****************** Code 370 ****************** \catcode`\@11 \def\scandef#1#2{% #1=\ #2= \begingroup \endlinechar=-1 % pas de caractère de fin de ligne \everyeof{\@nil#1\noexpand}% ajoute "\@nil\\noexpand" avant la fin du fichier \expandafter\scandef@i\expandafter\relax\scantokens{#2}% } \def\scandef@i#1\@nil#2{% "\@nil#2" ont été ajouté par \everyeof \endgroup% ferme le groupe \expandafter\def\expandafter#2\expandafter{\gobone#1}% et définit la \ } \catcode`@12 \def\foo{% Dans tout l'argument de \string\foo, <<~>> est actif sauf \catcode`\~12 \scandef\bar{dans celui de \string\bar : <<~>>}% \catcode`~13 \bar } \foo ****************** Fin code ****************** ****************** Code 371 ****************** {% \endlinechar=`\d% insère la lettre "d" à chaque fin de ligne % le \noexpan(d) est incomplet Voici la macro \string\foo\ : \foo.% fins de ligne... }% ...commentées pour éviter le "d" ****************** Fin code ****************** ****************** Code 372 ****************** \catcode`\@11 \def\cprotect#1{% #1 est la \ \def\cprotect@i##1{% ##1 est l' \endgroup% ferme le groupe précédemment ouvert #1{\scantokens{##1\noexpand}}% }% \begingroup% rend tous les octets de catcode12 \for\cprotect@temp=0to255\do{\catcode\cprotect@temp=12 }% \catcode`\{=1 \catcode`\}=2 % sauf "{" et "}" \cprotect@i% puis, lit l'argument } \catcode`@12 \frboxsep=1.5pt 1) \cprotect\frbox{foo \litterate-&# #^ ^_%- bar}\par 2) \cprotect\frbox{\catcode`\~=12 a~b~c~d}\par 3) \cprotect\frbox{foo \litterate-\bar- \cprotect\frbox{\litterate-&# #-} fin} ****************** Fin code ****************** ****************** Code 373 ****************** \catcode`@11 \def\Cprotect#1{% #1 est la \ \def\Cprotect@i##1{% ##1 est la liste des arguments \endgroup% ferme le groupe précédemment ouvert \toks0={#1}% met la \ dans le registre de tokens \Cprotect@ii##1\quark% \quark est mis à la fin des arguments \the\toks0 % exécute le registre }% \begingroup% rend tous les octets de catcode12 \for\temp@arg= 0 to 255 \do{% changer à 12 tous les catcodes \unless\ifnum\catcode\temp@arg=1 % sauf si catcode=1 \unless\ifnum\catcode\temp@arg=2 % ou 2 \catcode\temp@arg=12 \fi \fi}% \Cprotect@i% puis, lit l'argument } \def\Cprotect@ii#1{% #1 est l'argument courant (dépouillé de ses accolades) \def\temp@arg{#1}% stocke l'argument pour le tester ci dessous : \unless\ifx\quark\temp@arg% si la fin n'est pas atteinte % ajouter "{\scantokens{#1\noexpand}}" au registre \addtotoks{\toks0}{{\scantokens{#1\noexpand}}}% \expandafter\Cprotect@ii% et recommencer \fi } \catcode`@12 \def\test#1#2{Bonjour #1 et #2} \Cprotect\test{{argument 1 : \litterate-\foo-}{argument 2 : \litterate-\bar-}} ****************** Fin code ****************** ****************** Code 374 ****************** \newdimen\pixelsize \newdimen\pixelsep \def\pixel{\vrule height\pixelsize width\pixelsize depth0pt } \def\vblankpixel{\vrule height\pixelsize width0pt depth0pt } \def\blankpixel{\vblankpixel \vrule height0pt width\pixelsize depth0pt } \def\gap{\kern\pixelsep} \pixelsize=3pt \pixelsep=1pt Essai : \vbox{% aligne verticalement \offinterlineskip% annule le ressort d'interligne \lineskip=\pixelsep\relax% pour le mettre à \pixelsep \hbox{\pixel\gap\blankpixel\gap\blankpixel\gap\blankpixel\gap\pixel}% 1re ligne \hbox{\pixel\gap\pixel \gap\blankpixel\gap\pixel \gap\pixel}% 2e ligne } ****************** Fin code ****************** ****************** Code 375 ****************** \pixelsize=3pt \pixelsep=1pt Essai : \vtop{% \offinterlineskip \lineskip=\pixelsep\relax \vbox{% \hbox{\blankpixel\gap\pixel \gap\pixel \gap\pixel}% *** \hbox{\pixel \gap\blankpixel\gap\blankpixel\gap\pixel}% * * \hbox{\pixel \gap\blankpixel\gap\blankpixel\gap\pixel}% * * \hbox{\pixel \gap\blankpixel\gap\blankpixel\gap\pixel}% * * \hbox{\blankpixel\gap\pixel \gap\pixel \gap\pixel}% *** }% ----ligne de base \hbox {\blankpixel\gap\blankpixel\gap\blankpixel\gap\pixel}% * \hbox {\blankpixel\gap\pixel \gap\pixel }% ** } ****************** Fin code ****************** ****************** Code 376 ****************** \catcode`\@11 \begingroup% dans ce groupe : \catcode`\ =12\relax% l'espace devient un "caractère autre" \catcode`\^^M=13\relax% le retour à la ligne est actif \edef^^M{\string,}% et se développe en une virgule (de catcode 12) \global\deftok\otherspc{ }% définit un espace de catcode 12 \xdef\letter@code{% macro contenant le dessin de la lettre "e" ** * * *** * ***}% \endgroup \def\makecar@i#1{% #1 = dessin de la lettre avec les caractères "," "*" et " " \doforeach\current@line\in{#1}% pour chaque ligne dans #1 : {\ifx\empty\current@line% si la ligne est vide \addtomacro\pixabove{\hbox{\vblankpixel}}% ajouter une fausse ligne \else% sinon \let\pix@line\empty% initialiser le code de la ligne à vide \expandafter\makecar@ii\current@line\quark% et la construire \fi }% } \def\makecar@ii#1{% #1=caractère de dessin de la ligne en cours \ifxcase#1% si le caractère est * {\addtomacro\pix@line\pixel}% \otherspc{\addtomacro\pix@line\blankpixel}% \endif \ifx#1\quark% si la fin est atteinte \addtomacro\pix@line\unkern% annuler le dernier espace interpixel \eaddtomacro\pixabove{% et encapsuler \pix@line dans une \hbox \expandafter\hbox\expandafter{\pix@line}}% \else% si la fin n'est pas atteinte, ajouter l'espace interpixel \addtomacro\pix@line\gap \expandafter\makecar@ii% recommencer avec le caractère suivant \fi } \pixelsize=3pt \pixelsep=1pt \let\pixabove\empty% initialisation de la macro finale à vide \exparg\makecar@i\letter@code% appelle la macro qui construit \pixabove La lettre \vtop{% enferme le tout dans une \vtop : \offinterlineskip\lineskip=\pixelsep% ajuste l'espace interligne \vbox{\pixabove}% affiche le caractère créé }. ****************** Fin code ****************** ****************** Code 377 ****************** \catcode`\@11 \begingroup% dans ce groupe : \catcode`\ =12\relax% l'espace devient un "caractère autre" \catcode`\^^M=13\relax% le retour à la ligne est actif \edef^^M{\string,}% et se développe en une virgule (de catcode 12) \global\deftok\otherspc{ }% définit un espace de catcode 12 \xdef\letter@code{% macro contenant le dessin de la lettre "g" *** * * * * * * ***_ * **}% \endgroup \def\makecar#1#2{% #1=nom recevant le code final #2=macro contenant le dessin \let\pixabove\empty \let\pixbelow\empty \let\pix@line\empty% initialise à vide \exparg\ifin{#2}_% si le code contient _ {\expandafter\makecar@iii#2\@nil}% aller à \makecar@iii {\exparg\makecar@i{#2}}% sinon, à \makecar@i \edef#1{% définit la macro #1 comme \vtop{% une \vtop contenant : \unexpanded{\offinterlineskip\lineskip\pixelsep}% réglage d'espace inter ligne \vbox{\unexpanded\expandafter{\pixabove}}% \vbox des pixels au-dessus % de la ligne de base \unless\ifx\pixbelow\empty% s'il y a des pixels au-dessous de la baseline \unexpanded\expandafter{\pixbelow}% les ajouter dans la \vtop \fi }% }% } \def\makecar@iii#1_,#2\@nil{% \makecar@i{#2}% construit la partie au-dessous de la baseline \let\pixbelow\pixabove% et affecte le code à \pixbelow \let\pixabove\empty \let\pix@line\empty% ré-initialise \makecar@i{#1}% construit la partie au-dessus de la baseline } \makecar\lettreg\letter@code \catcode`\@12 \pixelsize=3pt \pixelsep=1pt Essai : \lettreg ****************** Fin code ****************** ****************** Code 378 ****************** \catcode`\@11 \begingroup \expandafter\gdef\csname impact@" "\endcsname{% définit la lettre "espace" \hskip 4\pixelsize plus.5\pixelsize minus.5\pixelsize\relax}% \catcode`\^^M=13\relax% le retour à la ligne est actif \edef^^M{\string,}% et se développe en une virgule (de catcode 12) \catcode`\ =12\relax% l'espace devient un "caractère autre" \gdef\impact@alphabet{ a/ *** * *** * * ****, b/ * * *** * * * * * * ***, % beaucoup de caractères omis 9/ *** * * * * **** * * ***, 0/ *** * * * * * * * * * * * ***, error/ * * * * * * * * * * * * * * * * * *}% <- commenter la fin de ligne \endgroup% % On parcourt le texte de remplacement de \impact@alphabet \edef\saved@crcatcode{\catcode13=\the\catcode13\relax}% \catcode`\^^M=13\relax% le retour à la ligne est actif \edef^^M{\string,}% et se développe en une virgule (de catcode 12) \expsecond{\doforeach\letter@name/\letter@code\in}\impact@alphabet% {\edef\letter@name{\letter@name}% développe la lettre (^^M devient ",") \edef\letter@code{\letter@code}% développe le code (^^M devient ",") \exparg\ifstart\letter@name,% si la lettre commence par "," {\edef\letter@name{\expandafter\gobone\letter@name}}% la retirer {}% \exparg\ifstart\letter@code,% si le code commence par "," {\edef\letter@code{\expandafter\gobone\letter@code}}% la retirer {}% \expandafter\makecar\csname impact@"\letter@name"\endcsname\letter@code% }% \saved@crcatcode% redonne le catcode de ^^M % % définit l'espace \pixelsize=3pt \pixelsep=1pt % puis, on affiche ce qui a été défini : a = \csname impact@"a"\endcsname\qquad b = \csname impact@"b"\endcsname\qquad 9 = \csname impact@"9"\endcsname \qquad 0 = \csname impact@"0"\endcsname\qquad error = \csname impact@"error"\endcsname ****************** Fin code ****************** ****************** Code 379 ****************** \catcode`\@11 \newskip\letter@skip% ressort mis entre chaque lettre \def\impactend{\impactend}% définit le quark de fin \newmacro\impact[0.2ex][0pt]{% définit la macro à arguments optionnels \leavevmode% passer en mode horizontal \begingroup% dans un groupe semi-simple : \pixelsize=#1\relax \pixelsep=#2\relax% définir ces deux dimensions \letter@skip=#1 plus.1\pixelsize minus.1\pixelsize\relax% définir espace inter-lettre \baselineskip=% définir la distance entre les lignes de base \dimexpr12\pixelsize+7\pixelsep\relax% à 12\pixelsize+7\pixelsep \lineskiplimit=0pt % si lignes trop serrées \lineskip=2\pixelsize\relax% les espacer de 2\pixelsize \impact@i% puis aller voir le prochain token } % va voir le prochain token puis va le tester à \impact@ii \def\impact@i{\futurelet\nxtletter\impact@ii} \def\impact@ii{% \ifx\nxtletter\impactend% si le token est \let égal à \impactend \let\donext\impact@endprocess% aller à al macro de fin \else \ifx\nxtletter\sptoken% si c'est un espace \let\donext\impact@spc% aller à \impact@spc \else \let\donext\impact@arg% sinon, aller à \impact@arg \fi \fi \donext% faire l'action décidée ci-dessus } % mange un espace (argument délimité) et affiche "\letter@" \expandafter\def\expandafter\impact@spc\space{% \csname impact@" "\endcsname \impact@i% puis va voir le prochain token } % % lit l'argument suivant \def\impact@arg#1{% \csname impact@% affiche \ifcsname impact@"#1"\endcsname "#1"% le caractère \impact@"#1" s'il est défini \else "error"% sinon \impact@"error" \fi \endcsname \hskip\letter@skip% insérer le ressort inter-lettre \impact@i% puis, aller voir le prochain token } \def\impact@endprocess\impactend{% macro exécuté lorsqque le quark \impactend va être lu \unskip% annuler le dernier ressort \par% composer le paragraphe pour prendre en compte % \baselineskip, \lineskip et \lineskiplimit \endgroup% et fermer le groupe } \catcode`\!=12 % rend le point d'exclamation "gentil" \impact Programmer en {TEX} est facile et tr{e`}s utile !\impactend\par \impact[2pt][0.5pt]Programmer en {TEX} est facile et tr{e`}s utile !\impactend ****************** Fin code ****************** ****************** Code 380 ****************** \newdimen\maingraddim \maingraddim=4pt % hauteur des graduations principales \newdimen\maingradwd \maingradwd=0.5pt % épaisseur des graduations principales \def\maingradx#1{% \lower1.5ex\clap{$\scriptscriptstyle#1$}% afficher l'argument au dessous \clap{\vrule height\maingraddim width\maingradwd depth0pt }% et la réglure } Début\maingradx{1}suite\maingradx{2}conclusion\maingradx{3}fin. ****************** Fin code ****************** ****************** Code 381 ****************** \maingraddim=4pt \maingradwd=0.5pt \newdimen\axiswd \axiswd=0.5pt \newmacro\xaxis[1cm]1[1]1[4]{%% #1= dist #2=xmin #3=inc #4=xmax #5=subdiv \hbox{% mettre le tout dans une \hbox \rlap{% en débordement à droite : \FOR\xx = #2 to #4 \do #3{% pour chaque graduation principale \maingradx{\xx}% tracer la graduation et écrire l'abscisse \kern#1\relax% puis se déplacer vers la droite }% }% \vrule% tracer l'axe des abscisses height\axiswd% d'epaisseur \axiswd, de longueur #1*(#4-#2)/#3 width\dimexpr#1*\decdiv{\dimtodec\dimexpr#4pt-#2pt\relax}{#3}\relax depth 0pt\relax % et de profondeur nulle }% } a) \xaxis{-2}{5}suite b) \frboxsep=0pt\frbox{\xaxis[1.25cm]{-1}[0.25]{1}}suite ****************** Fin code ****************** ****************** Code 382 ****************** \maingraddim=4pt \maingradwd=0.5pt \axiswd=0.5pt \newdimen\subgraddim \subgraddim=2.5pt \newdimen\subgradwd \subgradwd=0.2pt % trace un trait de subdivision \def\subgradx{\clap{\vrule height\subgraddim width\subgradwd depth0pt }} \newmacro\xaxis[1cm]1[1]1[4]{% #1= dist #2=xmin #3=inc #4=xmax #5=subdiv \hbox{% tout mettre dans une \hbox \setbox0=\hbox{% stocke dans une \hbox les grad secondaires entre 2 unités \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions \for\xx=1 to #5-1 \do{% insérer #5-1 fois \kern\dimsubgrad% une espace secondaire \subgradx% une graduation secondaire }% }% \rlap{% en débordement à droite : \FOR\xx = #2 to #4 \do #3{% pour chaque graduation principale \maingradx{\xx}% imprimer l'abscisse \ifdim\xx pt<#4pt % et en débordement à droite, \rlap{\copy0 }% les réglures secondaires, sauf pour la dernière \fi \kern#1\relax% se déplacer vers la droite }% }% \vrule% tracer l'axe des abscisses height\axiswd% d'epaisseur \axiswd, de longueur #1*(#4-#2)/#3 width\dimexpr#1*\decdiv{\dimtodec\dimexpr#4pt-#2pt\relax}{#3}\relax depth 0pt\relax % et de profondeur nulle }% } a) \xaxis{-2}{5}[2] )b \xaxis[1.25cm]{-1}[0.25]{1}[5] ****************** Fin code ****************** ****************** Code 383 ****************** \maingraddim=4pt \maingradwd=0.5pt \axiswd=0.5pt \subgraddim=2.5pt \subgradwd=0.2pt \def\maingrady#1{% affiche... \vlap{\llap{$\scriptscriptstyle#1$\kern2pt }}% l'ordonnée... \vbox to0pt{\vss\hrule height\maingradwd width\maingraddim depth0pt }% et la réglure } % affiche une subdiv \def\subgrady{\vlap{\hrule height\subgradwd width\subgraddim depth0pt }} \newmacro\yaxis[1cm]1[1]1[4]{% % #1= dist #2=ymin #3=inc #4=ymax #5=subdiv \vbox{% \offinterlineskip% désactiver le ressort d'interligne \setbox0=\vbox{% stocke dans une \hbox les grad secondaires entre 2 unités \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions \for\xx=1 to #5-1 \do{% insérer #5-1 fois \kern\dimsubgrad% une espace secondaire \subgrady% une graduation secondaire }% }% \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% distance entre 2 subdivisions \vbox to 0pt{% en débordement vers le bas \FOR\xx = #4to#2\do-#3{% \maingrady{\xx}% imprimer l'abscisse \ifdim\xx pt>#2pt % et en débordement à droite, \vbox to 0pt{\copy0 \vss}% les réglures secondaires, sauf pour la dernière \fi \kern#1\relax% se déplacer vers la droite }% \vss% assure le débordement vers le bas }% \clap{\vrule% tracer l'axe des ordonnées width\axiswd% d'épaisseur \axiwd, et de hauteur (#4-#2)/#3*#1 height\decdiv{\dimtodec\dimexpr(#4pt-#2pt)\relax}{#3}\dimexpr#1\relax depth 0pt\relax % profondeur nulle }% }% } Essai : \yaxis{-2}{5}[2]\qquad \frboxsep=0pt \frbox{\yaxis[0.75cm]{-1}[0.5]{3}[5]} ****************** Fin code ****************** ****************** Code 384 ****************** \newdimen\xunit \xunit=1cm \newdimen\yunit \yunit=0.75cm \maingraddim=4pt \maingradwd=0.5pt \axiswd=0.5pt \subgraddim=2.5pt \subgradwd=0.2pt \catcode`@11 \newmacro\graphzone1[1]1[4]1[1]1[4]{% \leavevmode% quitter le mode vertical \begingroup% travailler dans un groupe semi-simple \def\graphxmin{#1}\def\graphxmax{#3}% sauvegarder \def\graphymin{#5}\def\graphymax{#7}% les \def\xincrement{#2}\def\yincrement{#6}% arguments \setbox0\hbox{\yaxis[\yunit]{#5}[#6]{#7}[#8]}% axe "y" dans boite 0 \setbox1\hbox{\xaxis[\xunit]{#1}[#2]{#3}[#4]}% axe "x" dans boite 1 \edef\graphboxht{\the\ht0 }% \graphboxh est la hauteur de la zone \edef\graphboxwd{\the\wd1 }% \graphboxw est la largeur de la zone \rlap{% annuler la dimension horizontale, et... \box1 % ...afficher l'axe (Ox) puis % le trait vertical à l'extrême droite de la zone \clap{\vrule height\dimexpr\graphboxht+\axiswd\relax width\axiswd depth0pt }% }% \rlap{\box0 }% afficher l'axe (Oy) en annulant sa dimension horizontale \raise\graphboxht% puis monter tout en haut de la zone % pour tracer le trait horizontal en annulant sa longueur \rlap{\kern-0.5\axiswd% et en rattrapant le centrage de l'axe des ordonnées \vrule height\axiswd width\dimexpr\graphboxwd+\axiswd}% \begingroup% pour lire l'argument "" : \catcode`\^^M=9\relax % ignorer les retours à la ligne \graphzone@i% appeler \graphzone@i } \def\graphzone@i#1{% lire le ... \endgroup% et fermer le groupe précédemment ouvert \setbox0\hbox{#1}% mettre les instructions graphique dans la boite 0 \wd0=\dimexpr\graphboxwd+\axiswd\relax% forcer sa dim horizontale \ht0=\dimexpr\graphboxht+\axiswd\relax% et verticale % pour correspondre aux dimension de la zone graphique \box0 % l'afficher \endgroup% sortir du groupe initial } \catcode`@12 essai\graphzone{-1}[0.5]{1.5}[5]{-20}[10]{10}[2]{\relax}fin ****************** Fin code ****************** ****************** Code 385 ****************** \def\putat#1#2#3{% \leavevmode\rlap{\kern#1\vbox to0pt{\vss\hbox{#3}\kern#2}}% } Essai\putat{0.5cm}{0.3cm}{A}\putat{-0.2cm}{-0.1cm}{B}\putat{0pt}{0.2cm}{C}suite ****************** Fin code ****************** ****************** Code 386 ****************** \def\ifinside#1[#2,#3]{% \ifnum\sgn{\dimexpr#1pt-#2pt\relax}\sgn{\dimexpr#1pt-#3pt}1=1 \expandafter\secondoftwo \else \expandafter\firstoftwo \fi } 1) \ifinside3.5[0.5,4]{vrai}{faux}\qquad 2) \ifinside-0.2[-0.4,-0.3]{vrai}{faux}\qquad 3) \ifinside-0.999[-1,-0.5]{vrai}{faux} ****************** Fin code ****************** ****************** Code 387 ****************** \def\fonction#1{\dimtodec\dimexpr \decmul{#1}{\decmul{#1}{#1}}pt% x^3 -\decmul{#1}{#1}pt% -x^2 -#1pt*3% -3x +2pt\relax}% +2 1) \fonction{-4}\qquad% doit afficher -66 2) \fonction{-1.7}\qquad % doit afficher -0.703 3) \fonction{1}\qquad% doit afficher -1 4) \fonction{1.35}% doit afficher -1.412125 ****************** Fin code ****************** ****************** Code 388 ****************** \newmacro\cross[2pt][0.2pt]{% % #1=dimensions de traits depuis le centre de la croix % #2=épaisseur des traits \leavevmode \vlap{% \clap{% \vrule height#2 depth0pt width#1 % 1/2 trait horizontal gauche \vrule height#1 depth#1 width#2 % trait vertical \vrule height#2 depth0pt width#1 % 1/2 trait horizontal droit }% }% } Une croix : \cross{}puis une autre\cross[8pt][0.8pt] ****************** Fin code ****************** ****************** Code 389 ****************** \maingraddim=4pt \maingradwd=0.5pt \axiswd=0.5pt \subgraddim=2.5pt \subgradwd=0.2pt \xunit=1.25cm \yunit=0.75cm \def\maingradx#1{% \lower1.5ex\clap{$\scriptscriptstyle#1$}% afficher l'argument au dessous \clap{\vrule height\maingraddim width\maingradwd depth0pt }% et la réglure } \def\subgradx{\clap{\vrule height\subgraddim width\subgradwd depth0pt }} \newmacro\xaxis[1cm]1[1]1[4]{% \hbox{% tout mettre dans une \hbox \setbox0=\hbox{% stocke dans une \hbox les grad secondaires entre 2 unités \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions \for\xx=1 to #5-1 \do{% insérer #5-1 fois \kern\dimsubgrad% une espace secondaire \subgradx% une graduation secondaire }% }% \rlap{% en débordement à droite : \FOR\xx = #2 to #4 \do #3{% pour chaque graduation principale \maingradx{\xx}% imprimer l'abscisse \ifdim\xx pt<#4pt % et en débordement à droite, \rlap{\copy0 }% les réglures secondaires, sauf pour la dernière \fi \kern#1\relax% se déplacer vers la droite }% }% \vrule% tracer l'axe des abscisses height\axiswd% d'epaisseur \axiswd, de longueur #1*(#4-#2)/#3 width\dimexpr#1*\decdiv{\dimtodec\dimexpr#4pt-#2pt\relax}{#3}\relax depth 0pt\relax % et de profondeur nulle }% } \def\maingrady#1{% affiche... \vlap{\llap{$\scriptscriptstyle#1$\kern2pt }}% l'ordonnée... \vbox to0pt{\vss\hrule height\maingradwd width\maingraddim depth0pt }% et la réglure } % affiche une subdiv \def\subgrady{\vlap{\hrule height\subgradwd width\subgraddim depth0pt }} % #1= dim entre 2 grad principales #2=abscisse départ #3=incrément % #4=abscisse arrivée #5=nb intervalles secondaires \newmacro\yaxis[1cm]1[1]1[4]{% \vbox{% \offinterlineskip% désactiver le ressort d'interligne \setbox0=\vbox{% stocke dans une \hbox les grad secondaires entre 2 unités \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% dimension entre 2 subdivisions \for\xx=1 to #5-1 \do{% insérer #5-1 fois \kern\dimsubgrad% une espace secondaire \subgrady% une graduation secondaire }% }% \edef\dimsubgrad{\the\dimexpr#1/#5\relax}% distance entre 2 subdivisions \vbox to 0pt{% en débordement vers le bas \FOR\xx = #4to#2\do-#3{% \maingrady{\xx}% imprimer l'abscisse \ifdim\xx pt>#2pt % et en débordement à droite, \vbox to 0pt{\copy0 \vss}% les réglures secondaires, sauf pour la dernière \fi \kern#1\relax% se déplacer vers la droite }% \vss% assure le débordement vers le bas }% \clap{\vrule% tracer l'axe des ordonnées width\axiswd% d'épaisseur \axiwd, et de hauteur (#4-#2)/#3*#1 height\decdiv{\dimtodec\dimexpr(#4pt-#2pt)\relax}{#3}\dimexpr#1\relax depth 0pt\relax % profondeur nulle }% }% } \catcode`@11 \newmacro\graphzone1[1]1[4]1[1]1[4]{% \leavevmode% quitter le mode vertical \begingroup% travailler dans un groupe semi-simple \def\graphxmin{#1}\def\graphxmax{#3}% sauvegarder \def\graphymin{#5}\def\graphymax{#7}% les \def\xincrement{#2}\def\yincrement{#6}% arguments \setbox0\hbox{\yaxis[\yunit]{#5}[#6]{#7}[#8]}% axe "y" dans boite 0 \setbox1\hbox{\xaxis[\xunit]{#1}[#2]{#3}[#4]}% axe "x" dans boite 1 \edef\graphboxht{\the\ht0 }% \graphboxh est la hauteur de la zone \edef\graphboxwd{\the\wd1 }% \graphboxw est la largeur de la zone \rlap{% annuler la dimension horizontale, et... \box1 % ...afficher l'axe (Ox) puis % le trait vertical à l'extrême droite de la zone \clap{\vrule height\dimexpr\graphboxht+\axiswd\relax width\axiswd depth0pt }% }% \rlap{\box0 }% afficher l'axe (Oy) en annulant sa dimension horizontale \raise\graphboxht% puis monter tout en haut de la zone % pour tracer le trait horizontal en annulant sa longueur \rlap{\kern-0.5\axiswd% et en rattrapant le centrage de l'axe des ordonnées \vrule height\axiswd width\dimexpr\graphboxwd+\axiswd}% \begingroup% pour lire l'argument "" : \catcode`\^^M=9\relax % ignorer les retours à la ligne \graphzone@i% appeler \graphzone@i } \def\graphzone@i#1{% lire le ... \endgroup% et fermer le groupe précédemment ouvert \xunit=\decdiv1\xincrement\xunit% divise les unités par l'incrément \yunit=\decdiv1\yincrement\yunit \setbox0\hbox{#1}% mettre les instructions graphique dans la boite 0 \wd0=\dimexpr\graphboxwd+\axiswd\relax% forcer sa dim horizontale \ht0=\dimexpr\graphboxht+\axiswd\relax% et verticale % pour correspondre aux dimension de la zone graphique \box0 % l'afficher \endgroup% sortir du groupe initial } \def\putat#1#2#3{% \leavevmode\rlap{\kern#1\vbox to0pt{\vss\hbox{#3}\kern#2}}% } \newmacro\cross[2pt][0.2pt]{% \leavevmode \vlap{% \clap{% \vrule height#2 depth0pt width#1 % 1/2 terait horizontal gauche \vrule height#1 depth#1 width#2 % trait vertical \vrule height#2 depth0pt width#1 % 1/2 trait horizontal droit }% }% } \def\plot(#1,#2){% place "\plotstuff" aux coordonnées #1,#2 \edef\x@plot{#1}\edef\y@plot{#2}% développer au cas où \ifinside\x@plot[\graphxmin,\graphxmax]% si #1 est dans les limites {\ifinside\y@plot[\graphymin,\graphymax]% et si #2 l'est aussi {\putat% placer aux coordonnées {\dimexpr\x@plot\xunit-\graphxmin\xunit\relax}% X=(x-xmin)/xinc {\dimexpr\y@plot\yunit-\graphymin\yunit\relax}% Y=(y-ymin)/yinc \plotstuff% le contenu de \plotstuff }% \relax } \relax } \def\showxaxis{% affiche l'axe (Ox) \ifinside0[\graphymin,\graphymax]% {\putat\z@{-\graphymin\yunit}{\vlap{\vrule width\graphboxwd height\axiswd}}}% \relax } \def\showyaxis{% affiche l'axe (Oy) \ifinside0[\graphxmin,\graphxmax]% {\putat{-\graphxmin\xunit}\z@{\clap{\vrule width\axiswd height\graphboxht}}}% \relax } \def\showaxis{\showxaxis\showyaxis}% affiche les deux axes \catcode`@12 %%%%%%%%%%%%%%% 1er exemple : \xunit=1cm \yunit=0.5cm \def\fonction#1{\dimtodec\dimexpr\decmul{#1}{\decmul{#1}{#1}}pt-% \decmul{#1}{#1}pt-#1pt*3+2pt\relax} Exemple 1 :\qquad \graphzone{-2}[0.5]{2.5}[5]{-5}{5}[2]{% \let\plotstuff\cross% définit ce qu'il faut afficher comme point \FOR\xx=-2 to 2.5 \do 0.1{% \plot(\xx,\fonction{\xx})% afficher les points de coordonnées (x ; f(x)) }% \putat{5pt}{\dimexpr\graphboxht-10pt\relax}% afficher {$f(x)=x^3-x^2-3x+2$}% la fonction tracée \showaxis% et les axes }\par\medskip %%%%%%%%%%%%% 2e exemple \xunit=0.7cm \yunit=\xunit \def\foncx#1{\dimtodec\dimexpr\decmul{#1}{\decmul{#1}{#1}}pt-% \decmul{4}{\decmul{#1}{#1}}pt-#1pt+4pt} \def\foncy#1{\dimtodec\dimexpr\decmul{#1}{#1}pt-#1pt*2-6pt} Exemple 2 :\qquad \graphzone{-12}[2]{12}[2]{-8}[2]{8}[2]{% \def\plotstuff{\cross[1.25pt]} \FOR\tt = -5 to 5 \do 0.1 {% \plot(\foncx\tt,\foncy\tt)% }% \putat{5pt}{\dimexpr\graphboxht-20pt\relax}% afficher la fonction paramétrique tracée {$\left\{\vcenter{\hbox{$x(t)=t^3-4t^2-t-4$}\hbox{$y(t)=t^2-2t-6$}}\right.$}% \showaxis } ****************** Fin code ****************** ****************** Code 390 ****************** \def\mandeltest#1#2{% \def\zx{0}\def\zy{0}% zn=0 + i*0 \def\zxx{0}\def\zyy{0}% carrés de \zx et \zy \def\mandelresult{1}% le point appartient à M a priori \for\ii=1to\maxiter\do1{% \advance\count255 by1 \edef\zy{\dimtodec\dimexpr\decmul{\decmul2\zx}\zy pt+#2pt\relax}% \edef\zx{\dimtodec\dimexpr\zxx pt-\zyy pt+#1pt\relax}%% \edef\zxx{\decmul\zx\zx}% \edef\zyy{\decmul\zy\zy}% \ifdim\dimexpr\zxx pt+\zyy pt\relax>4pt \def\mandelresult{0}% \exitfor\ii \fi }% } \def\mandel#1#2{% #1=points par unité #2=nombre maximal d'itérations \graphzone{-2}[1]{1}[2]{-1}[1]{1}[2]{% \def\maxiter{#2}% \edef\plotstuff{\the\dimexpr\xunit/#1\relax}% taille d'un pixel \edef\plotstuff{\vrule height\plotstuff width\plotstuff}% \edef\increment{\decdiv{1}{#1}}% incrément \count255=0 % compteur des itérations \FOR\xxx = -2 to 1 \do \increment{% pour chaque \FOR\yyy = 0 to 1 \do \increment{% pixel du domaine \mandeltest\xxx\yyy% tester s'il est dans M \ifnum\mandelresult=1 % si oui, \plot(\xxx,\yyy)\plot(\xxx,-\yyy)% afficher les 2 points \fi }% }% \edef\plotstuff{$\scriptstyle\number\count255 $}% affiche la valeur du compteur \plot(-1.99,0.92)% aux coordonnées (-1.99 ; 0.92) }% } \xunit=3cm \yunit=3cm \mandel{400}{500} ****************** Fin code ****************** ****************** Code 391 ****************** \catcode`\@11 \protected\def\numsep{\kern0.2em }% \numsep est le séparateur mis tous les 3 chiffres \def\formatdecpart#1{% #1=série de chiffres \ifempty{#1}% si la partie décimale est vide {}% ne rien afficher {{,}\formatdecpart@i 1.#1..}% sinon, afficher la virgule et mettre en forme } % #1=compteur de caractères #2= chiffre courant % #3= chiffres restants #4 = chiffres déjà traités \def\formatdecpart@i#1.#2#3.#4.{% \ifempty{#3}% si #2 est le dernier chiffre {#4#2}% le mettre en dernière position et tout afficher, sinon {\ifnum#1=3 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi % si 3 chiffres sont atteint, rendre #1 égal à 1 et {\formatdecpart@i 1.#3.#4#2\numsep.}% mettre #2\numsep en dernier puis recommencer % sinon, mettre #2 en dernière position et recommencer % tout en incrémentant #1 de 1 {\expandafter\formatdecpart@i \number\numexpr#1+1.#3.#4#2.}% }% } a) \formatdecpart{1234567}\qquad b) \formatdecpart{987125}\qquad c) \formatdecpart{56}\qquad d) \edef\foo{\formatdecpart{2014}}\meaning\foo ****************** Fin code ****************** ****************** Code 392 ****************** \catcode`\@11 \protected\def\numsep{\kern0.2em }% \numsep est le séparateur mis tous les 3 chiffres \def\formatintpart#1{% #1=série de chiffres \formatintpart@i 1.#1..% appelle la macro récursive } % #1=compteur de caractères #2= chiffre courant % #3= chiffres restants #4 = chiffres déjà traités \def\formatintpart@i#1.#2#3.#4.{% \ifempty{#3}% si #2 est le dernier chiffre {#2#4}% le mettre en première position et tout afficher, sinon {\ifnum#1=3 \expandafter\firstoftwo\else\expandafter\secondoftwo \fi% si 3 chiffres sont atteint, rendre #1 égal à 1 et {\formatintpart@i 1.#3.\numsep#2#4.}% mettre "\numsep#2" en premier et recommencer % sinon, mettre #2 en première position et recommencer % tout en incrémentant #1 de 1 {\expandafter\formatintpart@i \number\numexpr#1+1.#3.#2#4.}% }% } \catcode`\@12 a) \formatintpart{1234567}\qquad b) \formatintpart{987125}\qquad c) \formatintpart{56}\qquad d) \edef\foo{\formatintpart{2014}}\meaning\foo ****************** Fin code ****************** ****************** Code 393 ****************** \catcode`\@11 \def\formatintpart#1{% #1=série de chiffres \expandafter\formatintpart@i\expandafter1\expandafter.\romannumeral\reverse{#1\z@}..% } \catcode`\@12 a) \formatintpart{1234567}\qquad b) \formatintpart{987125}\qquad c) \formatintpart{56}\qquad d) \edef\foo{\formatintpart{2014}}\meaning\foo ****************** Fin code ****************** ****************** Code 394 ****************** \catcode`\@11 \def\ifnodecpart#1{\if@nodecpart#1.\@nil}% teste si #1 est un entier \def\if@nodecpart#1.#2\@nil{\ifempty{#2}} \def\formatnum#1{% \ifnodecpart{#1}% s'il n'y a pas de partie décimale {\formatintpart{#1}}% formatter la partie entière {\formatnum@i#1\@nil}% sinon, formatter les deux parties } \def\formatnum@i#1.#2\@nil{% \formatintpart{#1}% formatte la partie entière \formatdecpart{#2}% et la partie décimale } \catcode`\@12 a) \formatnum{3.1415926}\qquad b) \formatnum{1987654.12301}\qquad c) \edef\foo{\formatnum{0987654.12300}}$\foo$ ****************** Fin code ****************** ****************** Code 395 ****************** \catcode`\@11 \def\removefirstzeros#1{% \removefirstzeros@i#1\quark% ajoute "\quark" en dernier } \def\removefirstzeros@i#1{% #1=chiffre courant \ifx\quark#1% fin atteinte donc nombre = 0 \expandafter0% laisser un zéro \else \ifx0#1% si le chiffre lu est un 0 \expandafter\expandafter\expandafter\removefirstzeros@i% recommencer \else% sinon remettre le chiffre #1 et tout afficher jusqu'à \removefirstzeros@i \expandafter\expandafter\expandafter\removefirstzeros@ii \expandafter\expandafter\expandafter#1% \fi \fi } \def\removefirstzeros@ii#1\quark{#1} \catcode`\@12 a) \removefirstzeros{000325478}\qquad b) \removefirstzeros{00000}\qquad c) \edef\foo{\removefirstzeros{001000}}\meaning\foo\qquad d) \long\def\>#1<{\detokenize{#1}} \expandafter\>\romannumeral\removefirstzeros{0123}< ****************** Fin code ****************** ****************** Code 396 ****************** \long\def\>#1<{\detokenize{#1}} \expandafter\>\romannumeral-`\@\removefirstzeros{000123}< ****************** Fin code ****************** ****************** Code 397 ****************** \catcode`\@11 \def\removelastzeros#1{% \exparg\reverse% inverser après {\romannumeral-`\.% tout développer \expandafter\removelastzeros@i% enlever les 0 de gauche après \romannumeral\reverse{#1\z@}\quark% avoir inversé #1 }% } \def\removelastzeros@i#1{% enlève tous les 0 de gauche \unless\ifx\quark#1% si la fin n'est pas atteinte \ifx0#1% si le chiffre lu est un 0 \expandafter\expandafter\expandafter\removelastzeros@i% recommencer \else% sinon remettre le chiffre et tout afficher jusqu'à \removefirstzeros@i \expandafter\expandafter\expandafter\removelastzeros@ii \expandafter\expandafter\expandafter#1% \fi \fi } \def\removelastzeros@ii#1\quark{#1} \catcode`\@12 a) \removelastzeros{0003254780}\qquad b) \removelastzeros{00000}\qquad c) \edef\foo{\removelastzeros{001000}}\foo\qquad \long\def\>#1<{\detokenize{#1}} d) \expandafter\>\romannumeral-`\.\removelastzeros{012300}< ****************** Fin code ****************** ****************** Code 398 ****************** \catcode`\@11 \protected\def\numsep{\kern0.2em }% \numsep est le séparateur mis tous les 3 chiffres \def\formatdecpart#1{% #1=série de chiffres \ifempty{#1}% si la partie décimale est vide {}% ne rien afficher {{,}\formatdecpart@i 1.#1..}% sinon, afficher la virgule et mettre en forme } % #1=compteur de caractères #2= chiffre courant % #3= chiffres restants #4 = chiffres déjà traités \def\formatdecpart@i#1.#2#3.#4.{% \ifempty{#3}% si #2 est le dernier chiffre {#4#2}% le mettre en dernière position et tout afficher, sinon {\ifnum#1=3 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi % si 3 chiffres sont atteint, rendre #1 égal à 1 et {\formatdecpart@i 1.#3.#4#2\numsep.}% mettre #2\numsep en dernier puis recommencer % sinon, mettre #2 en dernière position et recommencer % tout en incrémentant #1 de 1 {\expandafter\formatdecpart@i \number\numexpr#1+1.#3.#4#2.}% }% } \def\formatintpart#1{% #1=série de chiffres \expandafter\formatintpart@i\expandafter1\expandafter.% \romannumeral\reverse{#1\z@}..% appelle la macro récursive } % #1=compteur de caractères #2= chiffre à déplacer % #3= chiffres restants #4 = chiffres déjà traités \def\formatintpart@i#1.#2#3.#4.{% \ifempty{#3}% si #2 est le dernier chiffre à traiter {#2#4}% le mettre en première position et tout afficher, sinon {\ifnum#1=3 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi % si 3 chiffres sont atteint, rendre #1 égal à 1 et {\formatintpart@i 1.#3.\numsep#2#4.}% mettre \numsep#2 en premier puis recommencer % sinon, mettre #2 en dernière position et recommencer % tout en incrémentant #1 de 1 {\expandafter\formatintpart@i\number\numexpr#1+1.#3.#2#4.}% }% } \def\removefirstzeros#1{% \removefirstzeros@i#1\quark% ajoute "\quark" en dernier } \def\removefirstzeros@i#1{% #1=chiffre courant \ifx\quark#1% fin atteinte donc nombre = 0 \expandafter0% laisser un zéro \else \ifx0#1% si le chiffre lu est un 0 \expandafter\expandafter\expandafter\removefirstzeros@i% recommencer \else% sinon remettre le chiffre #1 et tout afficher jusqu'à \removefirstzeros@i \expandafter\expandafter\expandafter\removefirstzeros@ii \expandafter\expandafter\expandafter#1% \fi \fi } \def\removefirstzeros@ii#1\quark{#1} \def\removelastzeros#1{% \exparg\reverse% inverser après {\romannumeral-`\.% tout développer \expandafter\removelastzeros@i% enlever les 0 de gauche après \romannumeral\reverse{#1\z@}\quark% avoir inversé #1 }% } \def\removelastzeros@i#1{% enlève tous les 0 de gauche \unless\ifx\quark#1% si la fin n'est pas atteinte \ifx0#1% si le chiffre lu est un 0 \expandafter\expandafter\expandafter\removelastzeros@i% recommencer \else% sinon remettre le chiffre et tout afficher jusqu'à \removefirstzeros@i \expandafter\expandafter\expandafter\removelastzeros@ii \expandafter\expandafter\expandafter#1% \fi \fi } \def\removelastzeros@ii#1\quark{#1} % renvoie vrai s'il n'y a pas de partie décimale \def\if@nodecpart#1.#2\@nil{\ifempty{#2}} \def\formatnum#1{\formatnum@i1!#1!} % #1 = suivie de "1" % #2 = caractère courant % #3 = caractères non traités \def\formatnum@i#1!#2#3!{% \ifx+#2\expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\ifempty{#3}% si #2=+ et #3 est vide {\number#1 }% afficher "+1" ou "-1" (il n'y avait que des signes) {\formatnum@i#2#1!#3!}% sinon, mettre le signe #2 devant #1 } {\ifx-#2\expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\ifempty{#3}% si #2=- et #3 est vide {\number#1 }% afficher "+1" ou "-1" (il n'y avait que des signes) {\formatnum@i#2#1!#3!}% sinon, mettre le signe #2 devant #1 }% #2 est le dernier caractère qui n'est pas un signe + ou - {\ifnum#1<0 -\fi% si "1" est <0 afficher un "-" \formatnum@ii{#2#3}% formatter le nombre formé avec "#2#3"" }% }% } \def\formatnum@ii#1{% \if@comma{#1}% si #1 comporte une virgule {\formatnum@iii#1\@nil}% la remplacer par un "." et recommencer {\if@nodecpart#1.\@nil% puis si les chiffres restants sont un entier {\formatintpart{#1}}% formatter l'entier {\formatnum@iv#1\@nil}% sinon, formatter le nombre }% } \def\formatnum@iii#1,#2\@nil{\formatnum@ii{#1.#2}} \def\formatnum@iv#1.#2\@nil{% formatte le nombre décimal #1.#2 \exparg\formatintpart{\romannumeral-`\.\removefirstzeros{#1}}% \exparg\formatdecpart{\romannumeral-`\.\removelastzeros{#2}}% } \def\if@comma#1{\if@comma@i#1,\@nil}% teste la présence d'un virgule dans #1 \def\if@comma@i#1,#2\@nil{\ifempty{#2}\secondoftwo\firstoftwo} \catcode`\@12 a) \formatnum{3,141592653589793238462643383279502884197169399375105820974944592}\par b) \formatnum{---+69874}\qquad c) \formatnum{0032100,98000}\qquad d) \formatnum{+++010.01100}\qquad e) \formatnum{-+--+}\qquad f) \formatnum{---00.0000}\qquad g) \formatnum{+99,0000}\qquad h) \formatnum{.123456}\par i) \edef\foo{\formatnum{-+-+-010500,090900}}\meaning\foo ****************** Fin code ****************** ****************** Code 399 ****************** \setbox0=\vbox{% \hbox{Première ligne} \hbox{Deuxième ligne} \hbox{Avant-dernière ligne} \hbox{Dernière ligne} } \frboxsep=0pt % aucun espace entre le contenu et l'encadrement Boite initiale de hauteur \the\ht0 {} : \frbox{\copy0 } \splittopskip0pt % ne rajouter aucun espace au sommet de la boite restante \setbox1=\vsplit0 to 22pt % couper la boite à 22pt de hauteur Boite 1 de hauteur \the\ht1 {} : \frbox{\box1 } Boite 0 de hauteur \the\ht0 {} : \frbox{\box0 } ****************** Fin code ****************** ****************** Code 400 ****************** \setbox0=\vbox{% \hbox{Première ligne} \hbox{Deuxième ligne} \hbox{Avant-dernière ligne} \hbox{Dernière ligne} } \edef\restorevbadness{\vbadness=\the\vbadness\relax}% restaurera le \vbadness \vbadness=10000 % plus d'avertissement pour boite verticale \frboxsep=0pt % aucun espace entre le contenu et l'encadrement Boite initiale de hauteur \the\ht0 {} : \frbox{\copy0 } \splittopskip0pt % ne rajouter aucun espace au sommet de la boite restante \setbox1=\vsplit0 to 22pt % couper la boite à 22pt de hauteur \setbox1=\vbox{\unvbox1 }% la boite 1 prend la hauteur naturelle \restorevbadness\relax% restaure le \vbadness Boite 1 de hauteur \the\ht1 {} : \frbox{\box1 } Boite 0 de hauteur \the\ht0 {} : \frbox{\box0 } ****************** Fin code ****************** ****************** Code 401 ****************** \catcode`@11 \newbox\remainbox \newbox\partialbox \newdimen\cut@ht \def\breakpar{% \par\nointerlineskip% termine le paragraphe précédent \vskip\frboxsep\relax% et saute une petite espace verticale \begingroup \splittopskip\topskip% \topskip en haut des boites coupées \topskip=0pt % neutraliser le \topskip % nbre de réglures horizontales contribuant à l'encadrement restant (2 au début) \def\coeff@rule{2}% \setbox\remainbox=\vbox\bgroup% compose la boite après avoir... \advance\hsize by -2\dimexpr\frboxrule+\frboxsep\relax% ajusté sa largeur } \def\endbreakpar{% \egroup% fin de la composition de la boite \def\rule@arg{ULR}% prendre l\cut@htes réglures d'encadrement haute, gauche et droite \splitbox% puis, aller à l'algorithme de coupure \endgroup% une fois fini, sortir du groupe semi-simple } \def\splitbox{% \ifvoid\remainbox% si la boite est vide, c'est la fin du processus \par\nointerlineskip% termine le paragraphe précédent \vskip\frboxsep\relax% et saute un petit espace vertical \else% sinon \expandafter\splitbox@i% aller à \splitbox@i \fi } \def\splitbox@i{% \hbox{}% composer un noeud en mode vertical \nointerlineskip% pas de ressort d'interligne % calculer la dimension verticale disponible dans la page pour le texte de la boite \cut@ht=\dimexpr\pagegoal-\pagetotal-(\frboxsep+\frboxrule)*\coeff@rule\relax % si dimension totale du texte > dimension disponible pour le texte \ifdim\dimexpr\ht\remainbox+\dp\remainbox>\cut@ht% si une coupure doit être faite \advance\cut@ht\dimexpr% augmenter Dv de l'espace verticale libérée +\frboxsep+\frboxrule% par la réglure inférieure qui n'est pas sur cette page \relax \edef\old@vbadness{\the\vbadness}% sauvegarder \badness \vbadness=10000 % désactive les avertissement lors de la coupure \def\coeff@rule{1}% ne prendre en compte que réglure D + espace D \setbox\partialbox=\vsplit\remainbox to\cut@ht% coupe à la hauteur calculée % \partialbox retrouve sa hauteur naturelle \setbox\partialbox=\vbox{\unvbox\partialbox}% \vbadness=\old@vbadness\relax% restaure \vbadness \printpartialbox% imprime la boite partielle \vfill\eject% et compose la page en cours \else% si une coupure n'est pas nécessaire : \setbox\remainbox\vbox{\unvbox\remainbox}% reprendre la hauteur naturelle \setbox\partialbox=\box\remainbox% \partialbox devient \remainbox % et cette dernière devient vide \cut@ht=\dimexpr\ht\partialbox+\dp\partialbox\relax% hauteur à encadrer \edef\rule@arg{\rule@arg D}% ajouter "D" aux réglures à tracer \printpartialbox% afficher la boite restante \fi \splitbox } \def\printpartialbox{% imprime \partialbox \expandafter\framebox\expandafter[\rule@arg]{% \vbox to\cut@ht{\unvbox\partialbox\vss}}% \def\rule@arg{LR}% ne mettre que les réglures d et g } \def\dummytext#1{% \for\xx=1to#1\do% composer #1 fois la phrase suivante : {Ceci est un texte sans aucun int\'er\^et dont le seul but est de meubler la page artificiellement. }% } \catcode`@12 \dummytext{5} \frboxsep=5pt \breakpar \dummytext{70} \endbreakpar \dummytext{5} ****************** Fin code ****************** ****************** Code 402 ****************** \setbox0=\vbox{% \hsize=5cm Ceci est un texte sans aucun intérêt dont le seul but est de meubler la page de façon artificielle. } Boite 0 : \copy0 % affiche la boite totale \medbreak \edef\restorevfuzz{\vfuzz=\the\vfuzz\relax}% appelée après la coupure \vfuzz=\maxdimen% annule les avertissements pour débordement \splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante \setbox1=\vsplit0 to 0pt % couper la boite à 0pt de hauteur \restorevfuzz% restaurer \vfuzz \setbox1=\vbox{\unvbox1}% redonner à la boite sa hauteur d'origine Boites 1+0 : \vbox{% \offinterlineskip% annule le ressort d'interligne \box1 % affiche la première ligne \box0 %affiche les lignes restantes } ****************** Fin code ****************** ****************** Code 403 ****************** \def\vdim#1{\dimexpr\ht#1+\dp#1\relax}% hauteur totale de la boite #1 \setbox0=\vbox{% \hsize=5cm Ceci est un texte sans aucun intérêt dont le seul but est de meubler la page de façon artificielle. } \edef\htbefore{\the\vdim0}% hauteur de la boite 0 Boite 0 : \copy0 % affiche la boite totale \medbreak \edef\restoreparam{% \vfuzz=\the\vfuzz\relax% sauvegarde le \vfuzz \splittopskip=\the\splittopskip% et \splittopskip }% \vfuzz=\maxdimen% annule les avertissements pour débordement \splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante \setbox1=\vsplit0 to 0pt % couper la boite à 0pt de hauteur \restoreparam \setbox1=\vbox{\unvbox1}% redonner à la boite sa hauteur d'origine \edef\intersplitspace{\the\dimexpr\htbefore-(\vdim0+\vdim1)\relax}% Boites 1+0 : \vbox{% \offinterlineskip% annule le ressort d'interligne \box1 % affiche la première ligne \vskip\intersplitspace\relax% ajoute le ressort perdu à la coupure \box0 % affiche les lignes restantes } ****************** Fin code ****************** ****************** Code 404 ****************** \setbox0=\vbox{% \hsize=5cm Ceci est un texte sans aucun intérêt dont le seul but est de meubler la page de façon artificielle. } Boite 0 : \copy0 % affiche la boite totale \medbreak \edef\restoreparam{% \vfuzz=\the\vfuzz\relax% sauvegarde le \vfuzz \splittopskip=\the\splittopskip\relax% , le \splittopskip \savingvdiscards=\the\savingvdiscards\relax% et le \savingvdiscards }% \vfuzz=\maxdimen% annule les avertissements pour débordement \splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante \savingvdiscards=1 % autorise la sauvagarde des éléments supprimés \setbox1=\vsplit0 to 0pt % couper la boite à 0pt de hauteur \setbox1=\vbox{\unvbox1}% redonner à la boite sa hauteur d'origine \restoreparam Boites 1+0 : \vbox{% \offinterlineskip% annule le ressort d'interligne \box1 % affiche la première ligne \splitdiscards% affiche les éléments ignorés \box0 % affiche les lignes restantes } ****************** Fin code ****************** ****************** Code 405 ****************** \catcode`@11 \newbox\remainbox% boite contenant le texte total \newbox\currentline% boite contenant le ligne en cours \newcount\linecnt% compteur pour numéroter les lignes \def\vdim#1{\dimexpr\ht#1+\dp#1\relax}% hauteur totale de la boite #1 \newmacro\leftline[0pt]{% définit ce qui se trouve à gauche de chaque ligne \def\wd@left{#1}% \def\stuff@left } \newmacro\rightline[0pt]{% définit ce qui se trouve à droite de chaque ligne \def\wd@right{#1}% \def\stuff@right } \let\formatline=\identity% par défaut, afficher chaque ligne telle quelle % Par défaut : \leftline[11pt]{$\scriptscriptstyle\number\linecnt$\kern3pt }% numérotation à gauche \rightline{}% rien à droite \def\numlines{% \par\smallskip \begingroup% dans un groupe semi-simple \splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante \linecnt=0 % initialiser le compteur de lignes \savingvdiscards=1 % autorise la sauvagarde des éléments supprimés \setbox\remainbox=\vbox\bgroup% compose la boite... \advance\hsize by% diminuer la \hsize -\dimexpr\wd@left+\wd@right\relax% de la largeur des contenus } \def\endnumlines{% \egroup \offinterlineskip \split@line } \def\split@line{% \ifvoid\remainbox% si la boite est vide \par% fin du processus \endgroup% fermer le groupe ouvert au début \else% sinon \advance\linecnt 1 % incrémente le compteur de lignes \edef\htbefore{\the\vdim\remainbox}% \edef\restorevfuzz{\vfuzz=\the\vfuzz\relax}% sauvegarde le \vfuzz \vfuzz=\maxdimen% annule les avertissements pour débordement \setbox\currentline=\vsplit\remainbox to 0pt % couper la boite à 0pt de hauteur \restorevfuzz \setbox\currentline=\vbox{\unvbox\currentline}% redonner à la boite sa hauteur \edef\intersplitspace{% calcul de l'espace vertical perdu à la coupure \the\dimexpr\htbefore-(\vdim\remainbox+\vdim\currentline)\relax }% \hbox{% en mode vertical et dans une hbox, afficher : \hbox to\wd@left{\hss\stuff@left}% 1) ce qui est à gauche \formatline{\box\currentline}% 2) la ligne courante \hbox to\wd@right{\stuff@right\hss}% 3) ce qui est à droite }% \splitdiscards% affiche ce qui a été ignoré à la coupure \expandafter\split@line% recommencer \fi } \def\dummytext#1{% \for\xx=1to#1\do% composer #1 fois la phrase suivante : {Ceci est un texte sans aucun intérêt dont le seul but est de meubler la page de façon artificielle. }% } \catcode`@12 \parindent=2em ESSAI 1 : \numlines \dummytext{2}\par% 2 phrases $$1+1=2$$\par% des maths \dummytext{1}\par% une phrase \hrulefill\par% un leaders \dummytext{2}% 2 phrases \hrule height2pt depth 2pt %une \hrule \vskip10pt % saute 10pt verticalement \dummytext{1}% une phrase \endnumlines\medbreak ESSAI 2 : \leftline{}\rightline{}% rien à gauche et rien à droite \frboxsep=-\frboxrule% encadrer vers "l'intérieur" \let\formatline\frbox% lignes encadrées \numlines \dummytext{4} \endnumlines\medbreak ESSAI 3 : \let\formatline\identity \leftline[7pt]{% \for\xx= 1 to 2 \do{% insérer 2 fois \setbox0=\hbox{% % mettre dans une \hbox une \vrule de "bonnes dimensions" \vrule height\ht\currentline depth\dimexpr\dp\currentline+\intersplitspace width0.5pt }% \dp0=\dp\currentline% réajuster la profondeur (c-à-d enlever \intersplitspace) \box0 % afficher le boite \kern2pt % et insérer un espace horizontal de 2pt après chaque réglure verticale }% \kern2pt % ajouter 2pt de plus entre les lignes et le texte } \rightline[10pt]{\kern5pt $\scriptscriptstyle\number\linecnt$}% numéroter à droite \numlines \dummytext{2} $$a^2+b^2=c^2$$ \dummytext{2} \endnumlines ****************** Fin code ****************** ****************** Code 406 ****************** % définition des ressorts "inter-lettre" et "inter-mot" \newskip\interletterskip \interletterskip=0.25em plus0.05em minus0.05em \newskip\interwordskip \interwordskip=3\interletterskip\catcode`\@11 \catcode`@11 \def\spreadtxt@testtoken#1{% macro qui teste le token \ifcat\noexpand#1\sptoken% si le token est un espace \parseadd{% \unskip% retirer le précédent ressort \hskip\interwordskip}% et ajouter le ressort inter-mot \else \ifcat\noexpand#1a% si le token est une lettre \parseadd{#1\hskip\interletterskip}% ajouter le ressort inter-lettre \else \ifcat\noexpand#1.% si le token est "autre", comme le "." \parseadd{#1\hskip\interletterskip}% ajouter le ressort inter-lettre \else% sinon \parseadd{#1}% ajouter le token lu \fi \fi \fi \parse@i } \def\spreadtxt{% \ifstarred% si étoilée {\spreadtxt@i{\parse*}}% appeler \parse* {\spreadtxt@i{\parse}}% sinon, appeler \parse } \def\spreadtxt@i#1#2{% #1= appel "\parse*" ou "\parse" #2 = texte à espacer \begingroup% dans un groupe \let\testtoken=\spreadtxt@testtoken% modifier \testtoken #1#2\parsestop% et appeler \parse \endgroup } \catcode`@12 \spreadtxt{Comme on le voit sur cet exemple, une espace est insérée après {\it chaque} caractère de catcode 10, 11 ou 12.} \medbreak \spreadtxt*{Comme on le voit sur cet exemple, une espace est insérée après {\it chaque} caractère de catcode 10, 11 ou 12.} ****************** Fin code ****************** ****************** Code 407 ****************** \newskip\interletterskip \newskip\interwordskip \catcode`\@11 \newtoks\spacetxt@toks% le registre qui contient le texte final \def\spacetxt{% \let\spacetxt@endprocess\spacetxt@endnormal % définit la macro appelée en fin de processus -> a priori : fin normale \ifstarred% si la macro est étoilée {\let\spacetxt@recurse\spacetxt@star% définir la macro récursive \spacetxt@i% et aller à \spacetxt@i }% sinon {\let\spacetxt@recurse\spacetxt@nostar% définir la macro récursive \spacetxt@i% et aller à \spacetxt@i }% } \newmacro\spacetxt@i[0.3em plus0.07em minus.07em][3\interletterskip]1{% % arg optionnel #1 et #2 = ressorts inter-lettre et inter--mot % #3 = texte à espacer \interletterskip=#1\relax \interwordskip=#2\relax \def\spacetxt@code{#3}% met le texte à espacer dans \spacetxt@code \spacetxt@toks{}% initialiser le registre contenant le texte final \spacetxt@recurse% aller à la macro récursive précédemment définie } \newif\if@indivifound% booléen qui sera vrai si un motif spécial est rencontré \def\rightofsc#1#2{% \exparg\ifin{#1}{#2}% si #1 contient le #2 {\def\right@of##1#2##2\@nil{\def#1{##2}}% \expandafter\right@of#1\@nil% appelle la macro auxiliaire }% {\let#1=\empty}% sinon, #1 est vide } \def\spacetxt@nostar{% \exparg\ifempty{\spacetxt@code}% si texte restant est vide \spacetxt@endprocess% aller à la fin du processus {\@indivifoundfalse% sinon, a priori, les motifs non réguliers ne sont pas trouvés % pour chaque \indivi@tmp dans \indivilist \expsecond{\doforeach\indivi@tmp\in}{\indivilist}% pour chaque motif indivisible {% si le code commence par le motif courant \exptwoargs\ifstartwith\spacetxt@code\indivi@tmp {% l'ajouter dans le registre ainsi que l'espace inter-lettre \eaddtotoks\spacetxt@toks{\indivi@tmp\hskip\interletterskip}% % et enlever le motif du texte restant à lire \expsecond{\rightofsc\spacetxt@code}{\indivi@tmp}% \@indivifoundtrue% marquer qu'un motif a été trouvé \doforeachexit% et sortir prématurément de la boucle }% \relax% si le code ne commence pas le motif courant -> ne rien faire }% \unless\if@indivifound% si aucun motif n'a été trouvé \grab@first\spacetxt@code\spacetxt@temp% retirer le 1er caractère du texte \ifx\spacetxt@temp\space% si le 1er caractère est un espace \addtotoks\spacetxt@toks{% \unskip% annuler le précédent ressort \hskip\interwordskip}% ajouter l'espace inter-mot au registre de token \else% si le 1er caractère n'est pas un espace % ajouter ce caractère et l'espace inter-lettre au registre de token \eaddtotoks\spacetxt@toks{\spacetxt@temp\hskip\interletterskip}% \fi \fi \spacetxt@recurse% enfin, continuer le processus }% } \def\spacetxt@star{% \exparg\ifempty{\spacetxt@code}% si texte restant est vide \spacetxt@endprocess% aller à la fin du processus {% sinon, si texte commence par "{" \exparg\ifbracefirst{\spacetxt@code}% {\grab@first\spacetxt@code\spacetxt@temp % mettre {>} \spacetxt[4pt plus.7pt minus.7pt][12pt plus2pt minus2pt]{Ici, les motifs <> et <> restent indivisibles et la ligature des guillemets devient possible en déclarant "<<" et ">>" comme motifs.} ****************** Fin code ****************** ****************** Code 408 ****************** \catcode`\@11 \def\ttcode#1{% lit #1, le de début \def\ttcode@i##1#1{% ##1 = entre délimiteurs \tt% passe en fonte à chasse fixe \setbox0=\hbox{ }% \edef\spc@wd{\the\wd0 }% longueur d'un espace \spacetxt [.1pt plus0pt minus.1pt]% espace inter-lettre [\glueexpr\wd0+.3pt plus.1pt minus.1pt\relax]% espace inter-mot {##1}% le est composé par \spacetxt \endgroup }% \begingroup \def\indivilist{<<,>>,{,,},--}% (rajouter à, é, è, etc. en codage UTF8) \def\do##1{\catcode`##1=12 }% \dospecials% rend inoffensifs tous les tokens spéciaux \letactive\ =\space % rend l'espace actif \ttcode@i% va lire le et le délimiteur de fin } \catcode`\@12 \hfill\vrule\vbox{% \hsize=.75\hsize Avec \ttcode/\ttcode/, on peut insérer une adresse internet << \ttcode-http://www.gutenberg.eu.org/Typographie- >> puis repasser en fonte normale puis \ttcode+même composer un court passage en fonte à chasse fixe -- même si les coupures de mots se font n'importe où -- et aussi afficher tous les caractères spéciaux <<{$^ _$#}&+>>, et finir en fonte normale\ldots }\vrule\hfill\null ****************** Fin code ****************** ****************** Code 409 ****************** \def\ttwide{0.7}% coefficient pour la largeur de composition \def\ttindent{5}% nombre de caractères d'indentation \hfill\vrule \vbox{% \ttfamily% en TeX, on écrirait "\tt" \setbox0\hbox{0}% \wd0 est donc la largeur d'un caractère \parindent=\ttindent\wd0 % réglage de l'indentation \hsize=\ttwide\hsize % compose sur 70% de la largeur Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauches et droites du texte ont été tracées, on constate que les caractères sont exactement les uns au-dessous d'une ligne à l'autre. Des débordements dans la marge deviennent alors inévitables, car la largeur de composition (ici \the\hsize) n'est pas un multiple de la largeur d'un caractère (\the\wd0 ), le quotient des deux valant environ \xdef\ttratio{\decdiv{\dimtodec\hsize}{\dimtodec\wd0 }}\ttratio. }% \vrule\hfill\null ****************** Fin code ****************** ****************** Code 410 ****************** Fonte normale : \fontname\font\par {\bf Fonte grasse : \fontname\font\par} {\it Fonte italique : \fontname\font\par} {\sc Fonte petites majuscules : \fontname\font} ****************** Fin code ****************** ****************** Code 411 ****************** \font\gras=LinLibertineTB-tlf-t1 at 8pt \font\ital= LinLibertineTI-tlf-t1 at 8pt \font\itgras=LinLibertineTBI-tlf-t1 at 8pt Du texte normal {\gras puis en gras, \ital en italique, \itgras en italique gras} et retour à la normale. ****************** Fin code ****************** ****************** Code 412 ****************** Code du caractère de coupure = \number\hyphenchar\font\par Caractère de coupure : "\char\hyphenchar\font" ****************** Fin code ****************** ****************** Code 413 ****************** \def\longtext{Voici une phrase écrite avec des mots insignifiants mais terriblement, épouvantablement, horriblement et indéniablement longs.} % créér une macro restaurant le \hyphenchar \edef\restorehyphenchar{\hyphenchar\font=\number\hyphenchar\font}% %comportement normal, caractère de coupure "-" 1) \vrule\vbox{\hsize=5cm \longtext}\vrule\hfill % modification du caractère de coupure "W" 2) \vrule\vbox{\hsize=5cm \hyphenchar\font=`W \longtext}\vrule \medbreak % interdiction des coupures de mots 3) \vrule\vbox{\hsize=5cm \hyphenchar\font=-1 \longtext}\vrule \restorehyphenchar ****************** Fin code ****************** ****************** Code 414 ****************** \def\longtext{Voici une phrase écrite avec des mots insignifiants mais terriblement, épouvantablement, horriblement et indéniablement longs.} \edef\restorehyphenchar{\hyphenchar\font=\number\hyphenchar\font}% \vrule\vbox{\hsize=5cm \hyphenchar\font=-1 \sloppy \longtext}\vrule \restorehyphenchar ****************** Fin code ****************** ****************** Code 415 ****************** a) \texttt{\number\hyphenchar\font}\qquad b) {\ttfamily\number\hyphenchar\font} ****************** Fin code ****************** ****************** Code 416 ****************** \leavevmode \vbox{% \hsize=.4\hsize \Souligne{Police à chasse variable}% \vskip5pt espace inter-mot = \the\fontdimen2\font\par étrirement inter-mot = \the\fontdimen3\font\par compression inter-mot = \the\fontdimen4\font }\hfill \vbox{% \tt \hsize=.4\hsize \Souligne{Police à chasse fixe}% \vskip5pt espace inter-mot = \the\fontdimen2\font\par étrirement inter-mot = \the\fontdimen3\font\par compression inter-mot = \the\fontdimen4\font } ****************** Fin code ****************** ****************** Code 417 ****************** \def\ttwide{0.7}\def\ttindent{5}% \hfill\vrule \vbox{% \ttfamily \hyphenchar\font=`\- % change le caractère de coupure de la fonte en cours \setbox0\hbox{0}\parindent=\ttindent\wd0 \hsize=\ttwide\hsize % compose sur 70% de la largeur Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche et droite du texte ont été tracées, on constate que les caractères sont exactement les uns au-dessous des autres d'une ligne à l'autre. Des débordements dans la marge deviennent inévitables même si les coupures des mots sont à nouveau rendues possibles.% }% \vrule\hfill\null ****************** Fin code ****************** ****************** Code 418 ****************** \frboxsep=0.5pt \def\printallchars{% \leavevmode \for\xx=0 to255\do{% \vbox{% empiler verticalement \offinterlineskip% en désactivant le ressort d'interligne \setbox0\hbox{\frbox{\char\xx}}% \copy0 % la boite contenant le caractère encadré \kern1pt% saute 1pt vertical \hbox to\wd0{\hss$\scriptscriptstyle\xx$\hss}% le numéro }\hskip0.5em plus1pt minus1pt % saute 0.5em horizontalement }% } \tt Nom de la fonte = \fontname\font\par \printallchars ****************** Fin code ****************** ****************** Code 419 ****************** \setbox0=\hbox{\tt\char23} Largeur = \the\wd0 \qquad Hauteur = \the\ht0 \qquad Profondeur = \the\dp0 ****************** Fin code ****************** ****************** Code 420 ****************** {\tt\xdef\nfont{\the\font}} Largeur = \the\fontcharwd\nfont23 \qquad Hauteur = \the\fontcharht\nfont23 \qquad Profondeur = \the\fontchardp\nfont23 ****************** Fin code ****************** ****************** Code 421 ****************** \newmacro\ttstart[5]{% \begingroup \tt \edef\restorefontsettings{% stocke les paramètres de fonte \hyphenchar\font=\the\hyphenchar\font\relax% le \hyphenchar \fontdimen2\font=\the\fontdimen2\font\relax% et les paramètres d'espacement \fontdimen3\font=\the\fontdimen3\font\relax \fontdimen4\font=\the\fontdimen4\font\relax }% \fontdimen3\font=0.30\fontdimen2\font% composante + = 30% de la dimension naturelle \fontdimen4\font=0.20\fontdimen2\font% composante - = 20% de la dimension naturelle \hyphenchar\font=`\- % on autorise la coupure des mots (au cas où on utilise latex) \setbox0\hbox{0}% largeur d'un caractère \parindent=#1\wd0 % indentation (en nombre de caractères) \ignorespaces } \def\ttstop{% \restorefontsettings% restaure les paramètres de fonte \endgroup% et ferme le groupe } \hfill\vrule \def\ttwide{0.70}% \vbox{% \hsize=\ttwide\hsize % compose sur 70% de la largeur \ttstart[5] Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche et droite du texte ont été tracées, on constate que les caractères ne sont pas exactement les uns au-dessous des autres entre les lignes du paragraphe. Puisque les espaces sont redevenus étirables, les débordements dans la marge (même s'ils restent possibles), sont bien plus rares et ce d'autant plus que le nombre d'espaces dans une ligne est grand.% \ttstop }% \vrule\hfill\null ****************** Fin code ****************** ****************** Code 422 ****************** \catcode`\@11 \newcount\brktt@cnt \newdimen\brktt@interletter \newif\ifline@start% booléen vrai lorsqu'aucun caractère n'est encore affiché \newif\if@indivifound% booléen qui sera vrai si un motif spécial est rencontré \def\insert@blankchar{% \ifstarred\insert@blankchar@ii\insert@blankchar@i } \def\insert@blankchar@i#1{% insère une espace de largeur #1 caractères complets \ifnum\numexpr#1\relax>0 \kern\numexpr#1\relax\dimexpr\ttchar@width+\brktt@interletter\relax \fi } \def\insert@blankchar@ii#1{% insère #1-1 caractères complets + 1 largeur de caractère \ifnum\numexpr#1\relax>0 \insert@blankchar@i{#1-1}\kern\ttchar@width \fi } \def\restart@hbox#1{% \egroup% feerme la \hbox précédente \hbox\bgroup% ouvre la suivante \expsecond{\def\tt@remaintext} {\romannumeral\removefirstspaces@i{#1}}% initialiser le code à composer \let\previous@char\space% initialise le caractère précédent \line@starttrue% aucun caractère n'a encore été imprimé \brktt@cnt=0\relax% remettre le compteur à 0 } \newmacro\breaktt[0.3em][\hsize]1{% % arg optionnel #1 et #2 = ressorts inter-lettre et dimension horizontale texte % #3 = texte à espacer \begingroup% ouvrir un groupe et le fermer à la toute fin \par% commencer un nouveau paragraphe -> passage en mode vertical \parindent=0pt% empêche l'indentation \tt% passer en fonte à chasse fixe \setbox0 = \hbox{M}% la boite 0 contient un caractère \edef\ttchar@width{\the\wd0 }% largeur de chaque caractère en fonte \tt \edef\text@width{\the\dimexpr#2\relax}% largeur de composition % les 2 lignes suivantes rendent le compteur égal à E((L-l)/(l+Delta)) \brktt@cnt=\numexpr\dimexpr#2-\wd0 \relax\relax% largeur diminuée du 1er caractère \divide\brktt@cnt by \numexpr\dimexpr\wd0 + #1 \relax\relax % le nombre de caractère par ligne est égal à 1 de plus : \edef\maxchar@num{\number\numexpr\brktt@cnt+1\relax}% % calcul de la dimension inter-lettre \brktt@interletter=\dimexpr(\text@width-\ttchar@width*\maxchar@num)/\brktt@cnt\relax % stocke le texte après avoir enlevé les éventuels espaces extremes : \expsecond{\expsecond{\def\tt@remaintext}}{\removetrailspaces{#3}}% \unless\ifx\tt@remaintext\empty% si le texte à composer n'est pas vide \hbox\bgroup% démarrer la boite horizontale contenant la première ligne \insert@blankchar\ttindent% insérer une espace d'indentation \brktt@cnt=\ttindent\relax% tenir compte du nombre de caractères indentés \line@starttrue% il s'agit du début d'une ligne \expandafter\breaktt@i% aller à la macro récursive \fi } \def\breaktt@i{% \print@nchar\maxchar@num% afficher \maxchar@num caractères \ifx\tt@remaintext\empty% si texte restant est vide \egroup% fermer la hbox \par% aller à la ligne \endgroup% fermer le groupe ouvert au début \else \unless\ifnum\brktt@cnt<\maxchar@num\relax% si la ligne est remplie \exparg\restart@hbox{\tt@remaintext}% ferme la hbox et en ré-ouvre une \fi \expandafter\breaktt@i% enfin, continuer le processus \fi } \def\print@nchar#1{% affiche #1 caractères pris dans \tt@remaintext \for\xxx= 1 to #1 \do 1{% \ifx\tt@remaintext\empty% si le code restant à composer est vide \exitfor\xxx%sortir de la boucle prématurément \else \@indivifoundfalse% sinon, a priori, les motifs de ligature ne sont pas trouvés % pour chaque \indivi@tmp dans la liste de ligatures \expsecond{\doforeach\indivi@tmp\in}{\liglist}% {% si le code commence par la ligature courante \exptwoargs\ifstartwith\tt@remaintext\indivi@tmp {\let\previous@char\indivi@tmp% prendre le motif pour caractère courant, \expsecond{\rightofsc\tt@remaintext}{\indivi@tmp}% l'enlever du texte restant \@indivifoundtrue% marquer qu'un motif a été trouvé \doforeachexit% et sortir prématurément de la boucle }% {}% si le code ne commence pas le motif courant -> ne rien faire }% \unless\if@indivifound% si aucun motif trouvé, \grab@first\tt@remaintext\previous@char% lire le premier caractère \fi \advance\brktt@cnt by 1 % incrémenter le compteur de caractères \hbox to\ttchar@width{\hss\previous@char\hss}% afficher le caractère lu \line@startfalse% nous ne sommes plus au début d'une ligne \ifnum\brktt@cnt<\maxchar@num\relax% si la ligne n'est pas encore remplie \kern\brktt@interletter% insérer le ressort inter-lettre \else \exitfor\xxx% sinon, sortir de la boucle prématurément \fi \fi }% } \catcode`\@12 \def\liglist{<<,>>}% liste des motifs de ligature % ajouter à, é, etc si codage UTF8 + moteur 8 bits \def\ttindent{3}% valeur de l'indentation' \vrule\vbox{\breaktt[4pt][.7\hsize]{% Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche et droite du texte ont été tracées, on constate que les caractères sont exactement les uns au-dessous des autres d'une ligne à l'autre. Plus aucun débordement n'a lieu, car une espace correctement calculée est insérée entre chaque caractère. Les mots, en revanche, sont toujours coupés <>.% }% }\vrule\smallbreak \vrule\vbox{\breaktt[1pt][6cm]{% Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche et droite du texte ont été tracées, on constate que les caractères sont exactement les uns au-dessous des autres d'une ligne à l'autre. Plus aucun débordement n'a lieu, car une espace correctement calculée est insérée entre chaque caractère. Les mots, en revanche, sont toujours coupés <>.% }% }\vrule ****************** Fin code ****************** ****************** Code 423 ****************** \catcode`\@11 \newcount\brktt@cnt \newdimen\brktt@interletter \newif\ifline@start% booléen vrai lorsqu'aucun caractère n'est encore affiché \newif\if@indivifound% booléen qui sera vrai si un motif spécial est rencontré \newmacro\breakttA[0.3em][\hsize]1{% % arg optionnel #1 et #2 = ressorts inter-lettre et dimension horizontale texte % #3 = texte à espacer \begingroup% ouvrir un groupe et le fermer à la toute fin \par% commencer un nouveau paragraphe -> passage en mode vertical \parindent=0pt% empêche l'indentation \tt% passer en fonte à chasse fixe \setbox0 = \hbox{M}% la boite 0 contient un caractère \edef\ttchar@width{\the\wd0 }% largeur de chaque caractère en fonte \tt \edef\text@width{\the\dimexpr#2\relax}% largeur de composition % les 2 lignes suivantes rendent le compteur égal à E((L-l)/(l+Delta)) \brktt@cnt=\numexpr\dimexpr#2-\wd0 \relax\relax% largeur diminuée du 1er caractère \divide\brktt@cnt by \numexpr\dimexpr\wd0 + #1 \relax\relax % le nombre de caractères par ligne est égal à 1 de plus : \edef\maxchar@num{\number\numexpr\brktt@cnt+1\relax}% % calcul de la dimension inter-lettre \brktt@interletter=\dimexpr(\text@width-\ttchar@width*\maxchar@num)/\brktt@cnt\relax % stocke le texte après avoir enlevé les éventuels espaces extremes : \expsecond{\expsecond{\def\tt@remaintext}}{\removetrailspaces{#3}}% \unless\ifx\tt@remaintext\empty% si le texte à composer n'est pas vide \hbox\bgroup% démarrer la boite horizontale contenant la première ligne \insert@blankchar\ttindent% insérer une espace d'indentation \brktt@cnt=\ttindent\relax% tenir compte du nombre de caractères indentés \line@starttrue% il s'agit du début d'une ligne \expandafter\breakttA@i% aller à la macro récursive \fi } \def\breakttA@i{% \edef\remaining@chars{% calculer le nombre de caractères restant à placer sur la ligne \numexpr\maxchar@num-\brktt@cnt\relax}% \len@tonextword% \next@len contient le nombre de caractères du prochain mot % si le mot + l'eventuel "-" qui le suit ne peut pas loger sur la ligne en cours \ifnum\numexpr\next@len+\extra@char\relax>\remaining@chars\relax \ifline@start% et si c'est le début d'une ligne % avertir l'utilisateur \message{Largeur de composition trop faible pour \unexpanded\expandafter{\next@word}^^J}% % et composer tout de même "à la sauvage" jusqu'à la fin de la ligne \exparg\print@nchar{\number\numexpr\maxchar@num-\brktt@cnt}% \else% si la ligne en cours n'est pas au début \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne d'espaces \fi \exparg\restart@hbox{\tt@remaintext}% commencer une nouvelle ligne \expandafter\breakttA@i% et poursuivre le processus % s'il y a assez de place pour accueillir ce qui est avant la prochaine coupure \else \print@nchar\next@len% afficher le mot \ifx\tt@remaintext\empty% si texte restant est vide \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne \egroup% fermer la hbox en cours \par% aller à la ligne \endgroup% fermer le groupe ouvert au début. Fin du processus \else% si le texte restant n'est pas vide \ifnum\brktt@cnt<\maxchar@num\relax% si la ligne n'est pas remplie \print@nchar{1}% afficher le caractère qui suit le mot (" " ou "-") \else% si la ligne est remplie \exparg\restart@hbox{\tt@remaintext}% ferme la hbox et en ré-ouvre une \fi \expandafter\expandafter\expandafter\breakttA@i% enfin, continuer le processus \fi \fi } \def\leftofsc#1#2{% dans la macro #1, garde ce qui est à gauche de #2 \def\leftofsc@i##1#2##2\@nil{\def#1{##1}}% \expandafter\leftofsc@i#1#2\@nil } \def\len@tonextword{% stocke dans \next@len le nombre de caractères avant % le prochain point de coupure dans \tt@remaintext \let\next@word\tt@remaintext% copie \tt@remaintext dans la macro temporaire \next@word \leftofsc\next@word{ }% ne prend que ce qui est avant le prochain espace \exparg\ifin\next@word{-}% si le mot contient un tiret {\leftofsc\next@word{-}% prendre ce qui est à gauche de ce tiret \def\extra@char{1}% il y a un caractère de plus à loger après le mot }% sinon, le caractère après le mot est un espace {\def\extra@char{0}% qu'il ne faut pas compter }% \setbox0=\hbox{\next@word}% enfermer le mot dans une boite % et en calculer le nombre de caractères = dim(boite)/dim(caractère) \edef\next@len{\number\numexpr\dimexpr\wd0 \relax/\dimexpr\ttchar@width\relax\relax}% } \catcode`\@12 \def\liglist{<<,>>}% liste des motifs de ligature (mettre à, é, etc si codage UTF8) \def\ttindent{3} \vrule\vbox{\breakttA[3pt][8cm]{% Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche et droite du texte ont été tracées, on constate que les caractères sont exactement les uns au-dessous d'une ligne à l'autre. Plus aucun débordement n'a lieu, car une espace correctement calculée est insérée entre chaque caractère. Dorénavant, les mots ne sont plus coupés <<~sauvagement~>>. }}\vrule\medbreak \leavevmode\vrule\vbox{\breakttA[1pt][5cm]{% Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche et droite du texte ont été tracées, on constate que les caractères sont exactement les uns au-dessous d'une ligne à l'autre. Plus aucun débordement n'a lieu, car une espace correctement calculée est insérée entre chaque caractère. Dorénavant, les mots ne sont plus coupés <<~sauvagement~>>. }}\vrule\qquad\def\ttindent{0}% \vrule\vbox{\breakttA[0.6pt][2cm]{mot-composé mot-clé passe-droit au-dessus là-bas remonte-pente vingt-huit Notre-Dame-de-Lourdes Mont-Blanc Saint-Jean-de-Luz}}\vrule ****************** Fin code ****************** ****************** Code 424 ****************** \newmacro\zerocompose[]2{% % #1=code à exécuter avant la composition % #2=registre de boite recevant le résultat % #3= texte à composer en largeur 0pt \setbox#2=\vbox{% #1% code a exécuter (changement de fonte par exemple) \hfuzz=\maxdimen% annule les avertissements pour débordement horizontaux \hbadness=10000 % annule les avertissements pour mauvaise boite horizontale \pretolerance=-1 % désactive la première passe (celle sans coupures) \tolerance=10000 % passe avec coupures acceptée \hyphenpenalty=-10000 % favorise fortement les copures de mots \lefthyphenmin=2 \righthyphenmin=3 % longueur mini des fragments de début et fin \clubpenalty=0 % pas de pénalité supplémentaire après la première ligne \interlinepenalty=0 % pas de pénalité inter-ligne \widowpenalty=0 % pas de pénalité supplémentaire avant la dernière ligne \exhyphenpenalty=0 % ne pas pénaliser une coupure explicite \leftskip=0pt \rightskip=0pt % désactive les éventuels ressorts latéraux \everypar={}% désactive l'éventuel \everypar \parfillskip=0pt plus1fil % règle le \parfillskip par défaut \hsize=0pt % largeur de composition = 0pt \edef\restorehyphenchar{\hyphenchar\font=\number\hyphenchar\font}% \hyphenchar\font=`\- % impose "-" comme caractère de coupure \noindent % pas d'indentation + passage en mode horizontal \hskip0pt \relax% premier noeud horizontal pour permettre la coupure de la suite #3\par% compose #3 \restorehyphenchar% restaure le caractère de coupure }% } \zerocompose{0}{Programmation}% \leavevmode\box0 \hskip 5cm \zerocompose{0}{Composer en largeur nulle}% \box0 ****************** Fin code ****************** ****************** Code 425 ****************** \def\dummytext{Ceci est un texte sans aucun intérêt dont le seul but est de meubler la page de façon artificielle. } \setbox0=\vtop{% définit la boite 0 \hsize=8cm \dummytext\dummytext } a) Boite pleine : \copy0 b) \setbox0=\vtop{% \unvbox0 % boite précédemment composée \global\setbox1=\lastbox% et capture la dernière ligne dans la boite 1 }% Dernière ligne = |\hbox{\unhbox1 }|\par% redonne à box1 sa largeur naturelle Boite restante = \box0 ****************** Fin code ****************** ****************** Code 426 ****************** \def\dummytext{Ceci est un texte sans aucun intérêt dont le seul but est de meubler la page de façon artificielle. } \setbox0=\vtop{% définit la boite 0 \hsize=8cm \dummytext\dummytext } \setbox0=\vtop{\unvbox0 \global\setbox1=\lastbox} \setbox1=\hbox{\unhbox1 } 1) |\box1| \setbox0=\vtop{\unvbox0 \global\setbox1=\lastbox} \setbox1=\hbox{\unhbox1 } 2) |\box1| \setbox0=\vtop{\unvbox0 \global\setbox1=\lastbox} \setbox1=\hbox{\unhbox1 } 3) |\box1| ****************** Fin code ****************** ****************** Code 427 ****************** \setbox0=\vbox{\hsize=2.5cm Programmer en TeX est facile}% \showboxbreadth=5 \showboxdepth=3 \tracingonline=1 \showbox0 ****************** Fin code ****************** ****************** Code 428 ****************** \newbox\tempbox \def\reversebox#1{% affiche le contenu de la boite verticale #1 \setbox#1=\vbox{% \unvbox#1% composer la boite #1 \unskip\unpenalty% retirer ce qui n'est pas capturable par \lastbox \global\setbox0=\lastbox% la boite 1 devient la dernière ligne }% |\hbox{\unhbox0 }|\par% afficher la boite 1 dans sa largeur d'origine \ifvoidorempty{#1}% si le registre #1 contient une boite vide \relax% ne rien faire {\reversebox{#1}}% sinon, rencommencer } \def\dummytext{Ceci est un texte sans aucun intérêt dont le seul but est de meubler la page de façon artificielle. } \setbox\tempbox=\vbox{% définit la boite 0 \hsize=8cm \dummytext\dummytext } \reversebox\tempbox ****************** Fin code ****************** ****************** Code 429 ****************** % compose "Programmation" en largeur 0 dans la boite 0 : \zerocompose{0}{Programmation}% Boite initiale : \copy0 \par% compose la boite résultante \vfuzz=\maxdimen% annule les avertissements pour débordements \splittopskip=0pt % ne rajouter aucun espace au sommet de la boite restante \setbox1 =\vsplit0 to 0pt % coupe la boite 0 à 0pt {% dans un groupe (où la boite 0 sert de brouillon) \setbox0 =\vbox{% affecter à la boite brouillon \unvbox1 % la boite 1 composée dans sa largeur naturelle \unskip\unpenalty% annule ce qui n'est pas une boite \global\setbox2 =\lastbox% affecte globalement la dernière (et unique) ligne % à la boite 1 }% }% ferme le groupe \setbox2=\hbox{\unhbox2}% rend à la boite 2 sa largeur naturelle Première ligne = "\box2 "% compose la boite 2 ****************** Fin code ****************** ****************** Code 430 ****************** \catcode`@11 \def\hyphlengths#1#2{%#2 = macro contenant les longueurs de coupures du mot #1 \begingroup \zerocompose [\tt% passer en fonte à chasse fixe \setbox\z@\hbox{M}\xdef\ttwidth{\the\wd\z@}% mesurer la largeur des caractères ]\z@{#1}% compose en 0pt dans la boite 0 \let#2 = \empty% initialise la macro #2 \def\cumul@length{0}% le cumul des longueurs initialisé à 0 \vfuzz=\maxdimen% annule les avertissements pour débordement \splittopskip=\z@ % ne rajouter aucun espace au sommet de la boite restante \loop \setbox1=\vsplit\z@ to \z@% couper la boite à 0pt de hauteur {\setbox\z@=\vbox{\unvbox1 \unskip\unpenalty\global\setbox1=\lastbox}}% \setbox1=\hbox{\unhbox1 }% \unless\ifvoid\z@% si la boite 0 n'est pas encore vide \edef\cumul@length{% mettre à jour \cumul@length \number\numexpr \cumul@length +% ajouter le quotient "largeur syllabe/largeur d'1 caractère" \wd1/\dimexpr\ttwidth\relax -1% et soustraire 1 (le caractère "-") \relax }% % ajouter à #2 la virgule et le cumul actuel +1 \edef#2{% définir la macro #2 : #2% reprendre le contenu de #2 \ifx#2\empty\else,\fi% ajouter "," si #2 non vide \number\numexpr\cumul@length+1\relax% et le cumul }% \repeat% et recommencer \expsecond{% avant de fermer le groupe \endgroup \def#2}{#2}% définit #2 hors du groupe } \catcode`\@12 a) \hyphlengths{programmation}\foo liste = "\foo"\par b) \hyphlengths{typographie}\foo liste ="\foo"\par c) \hyphlengths{j'entendrai}\foo liste ="\foo"\par d) \hyphlengths{vite}\foo liste ="\foo" ****************** Fin code ****************** ****************** Code 431 ****************** \catcode`\@11 \newcount\brktt@cnt \newdimen\brktt@interletter \newif\ifline@start% booléen vrai lorsqu'aucun caractère n'est encore affiché \newif\if@indivifound% booléen qui sera vrai si un motif spécial est rencontré \let\breakttB=\breakttA \def\breakttA@i{% \edef\remaining@chars{% calculer le nombre de caractères restant à placer sur la ligne \numexpr\maxchar@num-\brktt@cnt\relax}% \len@tonextword% calculer dans \next@len le nombre de caractères avant le prochain mot \def\next@hyphlen{0}% initialiser la longueur de coupure possible du mot % si le mot + l'eventuel "-" qui le suit ne rentre pas sur ce qui reste de la ligne \ifnum\numexpr\next@len+\extra@char\relax>\remaining@chars\relax \hyphlengths\next@word\list@hyphlengths% bâtir la liste des longueurs de coupures \unless\ifx\list@hyphlengths\empty% si une coupure est possible \expsecond{\doforeach\xx\in}{\list@hyphlengths}% les examiner toutes {% si la coupure examinée peut loger sur la ligne \unless\ifnum\xx>\remaining@chars\relax% \let\next@hyphlen\xx% la stocker \fi% pour que \next@hyphlen soit maximal }% \fi \fi \ifnum\next@hyphlen>0 % si une coupure est nécessaire et a été trouvée \let\next@len\next@hyphlen% mettre à jour la longueur du mot à placer \fi % si le mot + l'eventuel "-" qui le suit ne peut pas loger sur la ligne en cours \ifnum\numexpr\next@len+\extra@char\relax>\remaining@chars\relax \ifline@start% si c'est le début d'une ligne % avertir l'utilisateur \message{Largeur de composition trop faible pour \unexpanded\expandafter{\next@word}^^J}% % et composer tout de même jusqu'à la fin de la ligne \exparg\print@nchar{\number\numexpr\maxchar@num-\brktt@cnt}% \else% sinon \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne \fi \exparg\restart@hbox{\tt@remaintext}% ré-ouvrir une boite \expandafter\breakttA@i% et poursuivre le processus % s'il y a la place pour accueillir ce qui est avant la prochaine coupure \else \ifnum\next@hyphlen>0 % si une coupure de mot doit être faite \exparg\print@nchar{\number\numexpr\next@len-1}% afficher les lettres de la syllabe \print@hyphchar% et le caractère de coupure \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne \exparg\restart@hbox{\tt@remaintext}% ré-ouvrir une boite \expandafter\expandafter\expandafter\breakttA@i% et continuer le processus \else% si pas de coupure dans le mot \print@nchar\next@len% afficher le mot \ifx\tt@remaintext\tt@emptytext% si texte restant est vide \insert@blankchar*{\maxchar@num-\brktt@cnt}% remplir la ligne \egroup% fermer la hbox \par% aller à la ligne \endgroup% fermer le groupe ouvert au début. Fin du processus \else% si le texte restant n'est pas vide \ifnum\brktt@cnt<\maxchar@num\relax% si la ligne n'est pas remplie \print@nchar{1}% afficher le caractère qui suit le mot \else% si la ligne est remplie \exparg\restart@hbox{\tt@remaintext}% ferme la hbox et en ré-ouvre une \fi \expandafter\expandafter\expandafter\breakttA@i% enfin, continuer le processus \fi \fi \fi } \def\print@hyphchar{% \advance\brktt@cnt by 1 % augmenter le compteur de caractères \hbox to\ttchar@width{\hss-\hss}% afficher "-" \ifnum\brktt@cnt<\maxchar@num\relax% si la ligne n'est pas encore remplie \kern\brktt@interletter\relax% insérer le ressort inter-lettre \fi } \catcode`\@12 \def\liglist{<<,>>}% liste des motifs de ligature (mettre à, é, etc si codage UTF8) \def\ttindent{3} \vrule\vbox{\breakttB[3pt][8cm]{% Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche et droite du texte ont été tracées, on constate que les caractères sont exactement les uns au-dessous d'une ligne à l'autre. Plus aucun débordement n'a lieu, car une espace correctement calculée est insérée entre chaque caractère. Dorénavant, les mots ne sont plus coupés <>. }% }\vrule \vrule\vbox{\breakttB[1pt][5cm]{% Dans ce paragraphe, composé en fonte à chasse fixe et où les limites gauche et droite du texte ont été tracées, on constate que les caractères sont exactement les uns au-dessous d'une ligne à l'autre. Plus aucun débordement n'a lieu, car une espace correctement calculée est insérée entre chaque caractère. Dorénavant, les mots ne sont plus coupés <>. }% }\vrule ****************** Fin code ****************** ****************** Code 432 ****************** Voici un algorithme élémentaire : \algorithm[\#]{Algorithme {\bf MinMax}}| macro ~Min_Max~ (#1) % afficher la valeur max et min d'une liste de valeurs #1 $V_{max}:=-\infty$ % $-\infty$ ou la plus petite valeur possible $V_{min}:=+\infty$ % $+\infty$ ou la plus grande valeur possible ~Pour~ chaque $x$ dans (#1) ~Si~ $x>V_{max}$ ~alors~ $V_{max}:=x$ ~FinSi~ ~Si~ $xV_{max}$ WWW~alors~ $V_{max}:=x$ WW~FinSi~ WW~Si~ $x1 bar\else toi\fi}\par Suite \tracingcommands=0 ****************** Fin code ****************** ****************** Code 444 ****************** \def\i{\advance\count255 1 } \tracingrestores=1 \count255=2 {\i\i\i} \tracingrestores=0 ****************** Fin code ****************** ****************** Code 445 ****************** \def\i{\advance\count255 1 } \def\gi{\global\i} \tracingrestores=1 \count255=2 {\i\i\gi\i\gi\i} \tracingrestores=0 ****************** Fin code ****************** ****************** Code 446 ****************** \catcode`@11 \def\ifnodecpart#1{\ifnodecpart@i#1.\@nil} \def\ifnodecpart@i#1.#2\@nil{\ifempty{#2}} \def\decadd#1#2{% #1 et #2=nombre à additionner \ifnodecpart{#1}% si #1 est un entier {\ifnodecpart{#2}% et #2 aussi, les additionner avec \numexpr {\number\numexpr#1+#2\relax}% {\decadd@i#1.0\@nil#2\@nil}% sinon, ajouter ".0" après #1 } {\ifnodecpart{#2}% si #1 a une partie entière mais pas #2 {\decadd@i#1\@nil#2.0\@nil}% ajouter ".0" à #2 {\decadd@i#1\@nil#2\@nil}% sinon, les 2 parties entières sont présentes }% } \def\decadd@i#1.#2\@nil#3.#4\@nil{% affiche les parties entières et décimales reçues $x_a=#1\quad y_a=#2\qquad x_b=#3\quad y_b=#4$ } \catcode`@12 a) \decadd{5}{9.4}\par b) \decadd{-3.198}{-6.02}\par c) \decadd{0.123}{123} ****************** Fin code ****************** ****************** Code 447 ****************** \def\addzeros#1#2/#3.#4#5/#6.#7/{% Arguments reçus : #1#2/#3.#4#5/#6.#7/\par% afficher ce que la macro reçoit \ifempty{#2}% si #1 est le dernier chiffre de y {\ifempty{#5}% et si #4 est le dernier chiffre de y2 {Résultat final : \detokenize{{#3#1}{#6#4}{#70}}}% afficher le résultat final {\addzeros0/#3#1.#5/#6#4.#70/}% sinon alimenter #1 avec un 0 } {\ifempty{#5}% si #4 est le dernier chiffre de y2 {\addzeros#2/#3#1.0/#6#4.#70/}% alimenter #4 avec un 0 {\addzeros#2/#3#1.#5/#6#4.#70/}% #2 et #5 non vides }% } \addzeros457/.689714/.1/ ****************** Fin code ****************** ****************** Code 448 ****************** \catcode`\@11 \def\addzeros#1#2/#3.#4#5/#6.#7/{% \ifempty{#2}% si #1 est le dernier chiffre de y1 {\ifempty{#5}% et si #4 est le dernier chiffre de y2 {{#3#1}{#6#4}{#70}}% redonner les 3 arguments {\addzeros0/#3#1.#5/#6#4.#70/}% sinon alimenter #1 avec un 0 } {\ifempty{#5}% si #4 est le dernier chiffre de y2 {\addzeros#2/#3#1.0/#6#4.#70/}% alimenter #4 avec un 0 {\addzeros#2/#3#1.#5/#6#4.#70/}% #2 et #5 non vides }% } \def\decadd#1#2{% #1 et #2=nombre à additionner \ifnodecpart{#1} {\ifnodecpart{#2}{\number\numexpr#1+#2\relax}{\decadd@i#1.0\@nil#2\@nil}} {\ifnodecpart{#2}{\decadd@i#1\@nil#2.0\@nil}{\decadd@i#1\@nil#2\@nil}}% } \def\decadd@i#1.#2\@nil#3.#4\@nil{% \expandafter\decadd@ii \romannumeral-`\0\addzeros#2/.#4/.1/% se développe en 3 arguments {#1} {#3}% } \def\decadd@ii#1#2#3#4#5{% % #1 et #2=parties décimales (mêmes longueurs); % #3=seuil de retenue; #4 et #5=parties entières \exptwoargs{\decadd@iii{#3}}% envoyer le seuil de retenue tel quel % sommer les parties décimales signées {\number\numexpr\true@sgn{#4}#1+\true@sgn{#5}#2\relax}% % et les parties entières {\number\numexpr#4+#5\relax}% } \def\decadd@iii#1#2#3{% seuil de retenue = #1 \qquad nombre = "#3"."#2"% } \catcode`\@12 a) \decadd{6.7}{3.498}\par b) \decadd{1.67}{-4.9}\par c) \decadd{3.95}{2.0005}\par d) \decadd{1.007}{2.008}\par e) \decadd{-7.123}{3.523} ****************** Fin code ****************** ****************** Code 449 ****************** \catcode`\@11 \def\format@dec#1#2{% #1=partie décimale #2=seuil de retenue \expandafter\gobone% le \gobone agira en dernier, \romannumeral-`\0% mais avant, tout développer \expandafter\reverse\expandafter% et retarder le \reverse de fin % pour développer son argument qui {\number% développe tout et évalue avec \number \expandafter\reverse\expandafter% l'inversion de {\number\numexpr\abs{#1}+#2\relax}% |#1|+#2 }% } a) \format@dec{710}{100000}\par b) \format@dec{6}{100}\par c) \format@dec{-12300}{1000000} \catcode`\@12 ****************** Fin code ****************** ****************** Code 450 ****************** \catcode`@11 \def\true@sgn#1{\ifnum#11<\z@-\fi} \def\decadd#1#2{% #1 et #2=nombre à additionner \romannumeral-`\.% tout développer jusqu'à l'affichage du nombre (\decadd@iv) \ifnodecpart{#1}% si #1 est un entier {\ifnodecpart{#2}% et #2 aussi, les additionner avec \numexpr {\numexpr#1+#2\relax}% {\decadd@i#1.0\@nil#2\@nil}% sinon, ajouter ".0" après #1 } {\ifnodecpart{#2}% si #1 a une partie entière mais pas #2 {\decadd@i#1\@nil#2.0\@nil}% ajouter ".0" à #2 {\decadd@i#1\@nil#2\@nil}% sinon, les 2 parties entières sont présentes }% } \def\decadd@i#1.#2\@nil#3.#4\@nil{% \expandafter\decadd@ii \romannumeral-`\0\addzeros#2/.#4/.10/% se développe en 3 arguments {#1} {#3}% } \def\decadd@ii#1#2#3#4#5{% % #1 et #2=parties décimales (mêmes longueurs) % #3=seuil de retenue; #4 et #5=parties entières \exptwoargs{\decadd@iii{#3}}% envoyer le seuil de retenue tel quel % sommer les parties décimales signées {\number\numexpr\true@sgn{#4}#1+\true@sgn{#5}#2\relax}% % et les parties entières {\number\numexpr#4+#5\relax}% } \def\decadd@iii#1#2#3{% #1=seuil de retenue #2=partie décimale #3= partie entière \ifnum\true@sgn{#2}\true@sgn{\ifnum#3=\z@#2\else#3\fi}1=-1 % si les signes sont % différents \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\exptwoargs\decadd@iv% transmettre les arguments modifiés : {\number\numexpr#3-\true@sgn{#3}1}% #3:=#3-sgn(#3)1 {\number\numexpr#2-\true@sgn{#2}#1}% #2:=#2-sgn(#2)10^n {#1}% }% si les signes sont égaux {\ifnum\abs{#2}<#1 % et si abs(y)<10^n \expandafter\firstoftwo\else\expandafter\secondoftwo\fi {\decadd@iv{#3}{#2}{#1}% tranmettre les arguments tels quels } {\exptwoargs\decadd@iv% sinon {\number\numexpr#3+\true@sgn{#3}1}% #3:=#3+sgn(#3)1 {\number\numexpr#2-\true@sgn{#2}#1}% #2:=#2-sgn(#2)10^n {#1}% }% }% } \def\decadd@iv#1#2#3{% affiche le décimal "#1.#2" % le développement maximal initié par le \romannumeral de \decadd est actif \ifnum#1=\z@\ifnum#2<\z@% si #1=0 et #2<0 \expandafter\expandafter\expandafter% transmettre le développement à \number -% puis afficher le signe "-" \fi\fi \number#1% affiche #1 qui est la somme des parties entières % poursuivre le développement initié par \number \unless\ifnum#2=\z@% si la partie décimale est différente de 0 \antefi% se débarrasser de \fi \expandafter.% afficher le "." décimal après avoir \romannumeral-`\0\format@dec{#2}{#3}% correctement géré les 0 de #2 \fi } \def\addzeros#1#2/#3.#4#5/#6.#7/{% \ifempty{#2}% si #1 est le dernier chiffre de y1 {\ifempty{#5}% et si #4 est le dernier chiffre de y2 {{#3#1}{#6#4}{#7}}% redonner les 3 arguments {\addzeros0/#3#1.#5/#6#4.#70/}% sinon alimenter #1 avec un 0 } {\ifempty{#5}% si #4 est le dernier chiffre de y2 {\addzeros#2/#3#1.0/#6#4.#70/}% alimenter #4 avec un 0 {\addzeros#2/#3#1.#5/#6#4.#70/}% #2 et #5 non vides }% } \def\format@dec#1#2{% #1=partie décimale #2=seuil de retenue \expandafter\gobone% le \gobone agira en dernier, \romannumeral-`\0% mais avant, tout développer \expandafter\reverse\expandafter% et retarder le \reverse de fin % pour développer son argument qui {\number% développe tout et évalue avec \number \expandafter\reverse\expandafter% l'inversion de {\number\numexpr\abs{#1}+#2\relax}% abs(#1)+#2 }% } \catcode`@12 a) $-3.78+1.6987=\decadd{-3.78}{1.6987}$\par b) $3.56-3.06=\decadd{3.56}{-3.06}$\par c) $4.125+13.49=\decadd{4.125}{13.49}$\par d) $-0.99+1.005=\decadd{-0.99}{1.005}$\par e) $16.6-19.879=\decadd{16.6}{-19.879}$\par f) $5.789-0.698=\decadd{5.789}{-0.698}$\par g) $0.123-0.123=\decadd{0.123}{-0.123}$\par h) $3.14-16.4912=\decadd{3.14}{-16.4912}$\par i) $0.1-0.98=\decadd{0.1}{-0.98}$\par j) $2.43+7.57=\decadd{2.43}{7.57}$\par h) \edef\foo{\decadd{1.23}{9.78}}\meaning\foo\par j) \detokenize\expandafter\expandafter\expandafter{\decadd{3.14}{-8.544}} ****************** Fin code ****************** ****************** Code 451 ****************** a) \pdfstrcmp{foo}{bar}\qquad b) \pdfstrcmp{bar}{foo}\qquad c) \def\foo{ABC}\pdfstrcmp{\foo}{ABC}\qquad d) \edef\foo{\string_}\pdfstrcmp{1_2}{1\foo2}\qquad e) \pdfstrcmp{\string\relax}{\relax} ****************** Fin code ****************** ****************** Code 452 ****************** \edef\tempcompil{\number\pdfelapsedtime}% Depuis le début de la compilation, il s'est écoulé \tempcompil{} secondes d'échelle, soit \convertunit{\tempcompil sp}{pt} secondes. ****************** Fin code ****************** ****************** Code 453 ****************** %%%%%%%%%% definition de \loop...\repeat comme plain-TeX %%%%%%%%%% \def\loop#1\repeat{\def\body{#1}\iterate} \def\iterate{\body \let\next\iterate \else\let\next\relax\fi \next} \let\repeat=\fi %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \newcount\testcnt \pdfresettimer% remet le compteur à 0 \testcnt=0 \loop % Test no 1 \ifnum\testcnt<100000 \advance\testcnt 1 \repeat Temps 1 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle loop de \TeX)\par %%%%%%%%%%%% definition de \loop...\repeat comme LaTeX %%%%%%%%%%%% \def\loop#1\repeat{\def\iterate{#1\relax\expandafter\iterate\fi}% \iterate \let\iterate\relax} \let\repeat\fi \pdfresettimer% remet le compteur à 0 \testcnt=0 \loop % Test no 2 \ifnum\testcnt<100000 \advance\testcnt 1 \repeat Temps 2 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle loop de \LaTeX)\par %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \pdfresettimer% remet le compteur à 0 \for\ii=1 to 100000\do{}% Test no 3 Temps 3 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle for) ****************** Fin code ****************** ****************** Code 454 ****************** %%%%%%%%%%%% definition de \loop...\repeat comme LaTeX %%%%%%%%%%%% \def\loop#1\repeat{\def\iterate{#1\relax\expandafter\iterate\fi}% \iterate \let\iterate\relax} \let\repeat\fi \pdfresettimer% remet le compteur à 0 \def\ii{0}% \loop % Test no 1 \ifnum\ii<100000 \edef\ii{\number\numexpr\ii+1\relax}% \repeat Temps 1 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle loop de \LaTeX)\par %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \pdfresettimer% remet le compteur à 0 \for\ii=1 to 100000\do{}% Test no 2 Temps 2 = \convertunit{\pdfelapsedtime sp}{pt} s (boucle for) ****************** Fin code ****************** ****************** Code 455 ****************** \newcount\testcnt \pdfresettimer \testcnt=0 \for\ii=1 to 100000\do{\advance\testcnt1 } Temps 1 : \convertunit{\pdfelapsedtime sp}{pt} s (incrémentation compteur) \pdfresettimer \def\foo{0}% \for\ii=1 to 100000\do{\edef\foo{\number\numexpr\foo+1\relax}}% Temps 2 : \convertunit{\pdfelapsedtime sp}{pt} s (incrémentation du texte de remplacement) ****************** Fin code ******************