When is \relax necessary after a \ifcase#1 statement

The \ifcase has the syntax

\ifcase number zero case\or one\or two\or ...\fi

The "number" have to be separated from "zero case". The natural separator is space, but there are formats of the "number" where the "number" is compact (for example from \countdef of \chardef and the additional space gets the part of "zero case". You can try:

\def\test#1{\ifcase#1 77zero\or one\fi}  % note the space before "77zero"

literal:\test{0}  % the space is a separator of the number, it disappears

\newcount\tmpnum  \tmpnum=0
countdef:\test{\tmpnum}  % the space is a part of the zero case, it is printed 

\chardef\tmp=0
chardef:\test{\tmp}  % the space is a part of the zero case, it is printed

\end

If you need to have no space in zero case for every types of the number you can use \relax as the separator of the number. This prints nothing. But there is another problem: the \relax stays as a part of the result if the macro is used for expansion purposes only and zero case is evaluated.

Edit: How to solve the problem of various number format in #1 in order to no space no \relax is expanded? IMHO it is possible only by eTeX primitive \numexpr:

\def\test#1{\ifcase\numexpr#1\relax 77zero\or 2one\fi}

Edit2: Thanks to Frank and David we have found (in the discussion below) the robust solution of the number separator in the classic TeX too:

\def\zaprelax#1\relax{#1 }
\def\test#1{\ifcase\expandafter\zaprelax\number#1\relax 77zero\or 2one\or two\fi}

The answer given by @wipet explains the need to separate the evaluation of the number after \ifcase from the material that is used if that result is zero and it explains why both "space" or \relax can be wrong depending on the input.

As far as LaTeX is concerned: when LaTeX was initially written every byte did count so we dropped any explicit separation whenever it wasn't necessary, e.g.,

  \ifcase#1\or

here the zero case is empty and the \or tells TeX that the number to evaluate has finished. In cases like

\ifcase\@eqcnt

we know that \@eqcnt is a counter so no space or \relax is needed (or even correct)

Finally, if you actually don't know what the input data is, then you have to resort to the solution, which @wipet found together with David Carlisle, i.e.,

\def\zaprelax#1\relax{#1 }

 \ifcase\expandafter\zaprelax\number#1\relax

(in latex.ltx there is one case of \ifcase\number#1 but that doesn't solve the general situation either (as I initially thought); it only works there as the input as used is actually not fully general. In other words \hexnumber@ is fully correctly defined in the kernel. It wouldn't work if the input is a plain 0.)

anyway, just ending the evaluation with \relax is wrong if you don't know what the input is as @wipet explained: it correctly stops the evaluation but you don't know if the \relax is gone or becomes part of the zero case!

So if you do

\def\foo#1{\ifcase#1\relax 7\or 8\else9\fi}

the replacement text for \foo could be just a number (7, 8 or 9) or \relax 7 and the latter would act quite differently from the other cases.

So in short, the use of \relax in packages may be just unnecessary but it could be plain wrong.

Tags:

Tex Core