Tester si une commande est définie en (La)TeX
Reprise d’un de mes articles récents sur fctt, qui fait le point sur les
mille-et-une (en fait, un peu moins) façons de savoir si une séquence de
contrôle est définie ou non : en TeX pur, en LaTeX, en e-TeX, en distinguant ou
pas \relax
de undefined
…
Pour commencer, précisons que « non défini » et \relax
c’est pas pareil en général :
This is TeX, Version 3.1415926 (Web2C 7.5.7) **\show\relax > \relax=\relax. <*> \show\relax ? *\show\cettecommandenexistepas > \cettecommandenexistepas=undefined. <*> \show\cettecommandenexistepas ? x No pages of output. Transcript written on texput.log. |
\ifx
compare toujours les significations (« meaning ») des trucs : deux
trucs sont \ifx
-égaux si et seulement si il ont la même signification. La
signification est à peu près ce qui est affiché par \show
, sauf qu’avec
\show
on perd les catcodes alors que bien sûr en vrai ils comptent.
Deuxième point : quand on forme avec \csname
une séquence de contrôle dont
la signification était précedemment undefined
, la séquence est
immédiatement rendue \let
-égale à \relax
. La porté de cette opération est
limité comme celle de toute assignation. Exemple :
\begingroup \show\undefined % \undefined=undefined \csname undefined\endcsname \show\undefined % \undefined=\relax \endgroup \show\undefined % \undefined=undefined |
Application, extraite de latex.ltx (ou de source2e) :
\def\@ifundefined#1{% \expandafter\ifx\csname#1\endcsname\relax \expandafter\@firstoftwo \else \expandafter\@secondoftwo \fi} |
Usage :
\@ifundefined{newcommand}{Pas LaTeX}{LaTeX} |
Particularité : \@ifundefined{relax}{Non}{Oui}
répond non. En LaTeX, on met
donc dans le même sac \relax
et undefined
, mais c’est un choix propre à
LaTeX. (Autre) inconvénient : dans le cas où on exécute la ligne ci-dessus
en Plain, c’est comme si on avait aussi fait \let\newcommand\relax
.
Autres tests qui ne présentent pas ces inconvénients :
\ifx\truc\undefined % par convention, on suppose que \undefined est non % définie, mais rien ne le garantit (on utilise aussi \@undefined) \ifx\truc\undefined non def \else \ifx\truc\relax non def % au sens de LaTeX \else def \fi \fi |
Le deuxième est vachement préférable dans un milieu plus ou moins LaTeXien,
car il y a vraiment un risque que quelqu’un ait fait un \@ifundefined
avant… Ces deux tests ne marchent que si on peut écrire la séquence de
contrôle sans problème (pas de caractères zarbis dans le nom). Sinon, on
peut utiliser :
\begingroup\expandafter\expandafter\expandafter\endgroup \expandfater\ifx\csname nom+bien/chelou\endcsname\relax |
qui a l’avantage de ne pas faire passer à \relax
un truc qui était
undefined, mais l’inconvénient de ne plus être développable. En TeX
Knuthien, on ne peut pas faire mieux. En e-TeX, si.
On a d’abord \ifdefined\truc
qui est l’exact équivalent de
\ifx\truc\undefined
sauf qu’on a plus besoin de supposer que \undefined
est vraiment non définie. Ensuite, on a \ifcsname truc+che/lou\endcsname
qui fonctionne pareil et ne présente pas l’inconvénient de rendre \truc
égal à \relax
s’il était précédemment non défini. Bref, le pied. (Le tout
était bien sûr développable, cela va sans dire.)
Comme souvent, la solution e-TeX est la meilleure techniquement, mais elle n’etre pas très vite dans les mœurs, en particulier elle n’entrera pas dans le noyau LaTeX avant la sortie de LaTeX3 (c’est-à-dire pas tout de suite). Pour une interface LaTeXienne aux fonctionnalités d’e-TeX (et bien d’autres choses), je recommande le paquet etoolbox. Voir par exemple la section 3.5.1 de son manuel, concernant des points discutés ici.