18

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?

0

2 Answers 2

11

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.

4
  • I thought I convinced myself last century that \number didn't catch everything. will have to look again:-) Commented 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 difference Commented Dec 6, 2014 at 22:51
  • 2
    yes but try \test{0} once you add \number, the space goes in \number so \ifcase sees 077 Commented 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. Commented Dec 7, 2014 at 8:23
15

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}
5
  • +1 for numexpr, yes I think you are right that there is no way to make a "safe" termination in classic tex Commented 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. Commented 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 with 77. When two spaces are used then one space is printed when #1 is not literal number. Commented 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. Commented 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 Commented Dec 7, 2014 at 1:16

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.