In the LaTeX kernel (latex.ltx
) we see some examples of
\ifcase#1\or a\or b\or ...
(an \ifcase
without an added \relax
),
but when we look at some packages most of them add this statement
after the \ifcase#1
. So my question is: when is this required?
2 Answers
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.
-
I thought I convinced myself last century that
\number
didn't catch everything. will have to look again:-)David Carlisle– David Carlisle2014-12-06 22:39:35 +00:00Commented Dec 6, 2014 at 22:39 -
@DavidCarlisle surprise, isn't it? but just add it to \def\test#1{\ifcase#1 77zero\or one\fi} and see the differenceFrank Mittelbach– Frank Mittelbach2014-12-06 22:51:07 +00:00Commented Dec 6, 2014 at 22:51
-
2yes but try
\test{0}
once you add \number, the space goes in\number
so\ifcase
sees077
David Carlisle– David Carlisle2014-12-06 23:10:22 +00:00Commented Dec 6, 2014 at 23:10 -
@DavidCarlisle ouch. But that means \hexnumber@ in the kernel probably has a bug too (at least conceptually) as it will lose the 0 if given a literal 0.Frank Mittelbach– Frank Mittelbach2014-12-07 08:23:48 +00:00Commented Dec 7, 2014 at 8:23
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}
-
+1 for numexpr, yes I think you are right that there is no way to make a "safe" termination in classic texDavid Carlisle– David Carlisle2014-12-06 22:20:23 +00:00Commented Dec 6, 2014 at 22:20
-
@DavidCarlisle I thought so first too, but the "absolutely unnecessary" \number primitive is your friend here (see my answer). So there is a way in TeX too.Frank Mittelbach– Frank Mittelbach2014-12-06 22:32:58 +00:00Commented Dec 6, 2014 at 22:32
-
@FrankMittelbach I can't imagine how to use
\number
and I am unable to find your mentioned answer. When only one space is used:\ifcase\number#1 77zero...
then\ifcase
joins the expansion of the\number
with77
. When two spaces are used then one space is printed when#1
is not literal number.wipet– wipet2014-12-06 23:04:49 +00:00Commented Dec 6, 2014 at 23:04 -
@FrankMittelbach The
\number
primitive isn't "absolutely unnecessary" IMHO. I am using it for conversion the dimen to the number of sp units.wipet– wipet2014-12-06 23:17:58 +00:00Commented Dec 6, 2014 at 23:17 -
I think without etex you can probably do
\def\zaprelax#1\relax{} \def\testd#1{\ifcase\expandafter\zaprelax\number#1\relax
David Carlisle– David Carlisle2014-12-07 01:16:37 +00:00Commented Dec 7, 2014 at 1:16