4

I am not able to reset a counter to zero using a macro in Plain TeX.

This code uses \ifcase and the counter animalnum to output one of a list of animal names, incrementing the counter each time it is called so that next call will produce the subsequent name in the list. If it has been called more than a maximum number, it outputs an error message.

The command \resetanimalnum is supposed to set the counter back to zero and start the list over, but for some reason it doesn't work as expected.

Why does the counter behave this way and I how can I fix it?

\newcount\animalnum

\def\nextanimal{%                                                                                                                                                                              
  \ifcase\animalnum%                                                                                                                                                                           
    \animalI\or
    \animalII\or
    \animalIII%                                                                                                                                                                                
  \fi%                                                                                                                                                                                         
  \ifnum\animalnum > 2
    \exclaim%                                                                                                                                                                                  
  \fi%                                                                                                                                                                                         
  \advance\animalnum by 1%                                                                                                                                                                     
}

\def\resetanimalnum{\animalnum=0}

\def\animalI   {Lions}
\def\animalII  {Tigers}
\def\animalIII {Bears}
\def\exclaim   {Oh my!}

\nextanimal\ \nextanimal\ \nextanimal\ \nextanimal

\resetanimalnum\nextanimal\ \nextanimal

\bye

enter image description here

14
  • I think, \resetanimalnum is the culprit. If you put \nextanimal into the next line, it works
    – user31729
    Commented Jan 14, 2016 at 19:13
  • 1
    try \def\resetanimalnum{\animalnum=0\relax}
    – touhami
    Commented Jan 14, 2016 at 19:13
  • 1
    you can replace \fi% \ifnum\animalnum > 2 with \else
    – touhami
    Commented Jan 14, 2016 at 19:14
  • 4
    if tex is trying to determine the value of a counter, it keeps going until it definitively finds something non-numeric. so a space or \relax will stop the parsing of \animalnum=0. Commented Jan 14, 2016 at 19:26
  • 1
    There are a number of references to this on the site. Here's one which includes a reference elsewhere as well: Use of \relax after \ifnum\fi construction
    – Werner
    Commented Jan 14, 2016 at 19:38

2 Answers 2

5

the main culprits are the incorrect use of % after the 1 which prevents the 1 being terminated, and the lack of a space after 0. In some cases you can terminate a number with \relax but that leaves a \relax in the token stream which is not always desirable, a space is absorbed as part of the number.

\newcount\animalnum

\def\nextanimal{%
  \ifcase\animalnum
    \animalI\or
    \animalII\or
    \animalIII
  \fi
  \ifnum\animalnum > 2
    \exclaim
  \fi
  \advance\animalnum by 1
}

\def\resetanimalnum{\animalnum=0 }

\def\animalI   {Lions}
\def\animalII  {Tigers}
\def\animalIII {Bears}
\def\exclaim   {Oh my!}

\nextanimal\ \nextanimal\ \nextanimal\ \nextanimal

\resetanimalnum\nextanimal\ \nextanimal

\bye
2
  • Thank you -- this explains it completely. Is it also true that I should more properly use \else instead of the \ifnum construction? Commented Jan 14, 2016 at 20:56
  • @AndrewCashner I saw that comment on the other answer but they are not equivalent so it depends what you want if you go ifcase or, or, or ...else fiexclaim then you either get an animal or exclaim. if you go ifcase of or or fi \ifnum exclaim fi then you get an animal and then exclaim if the test number is bigger than 2 so you may get an animal and an exclaim Commented Jan 14, 2016 at 21:13
2

It's stated (somehow) in the TeX by Topic book (page 66)

TeX can expand too far in case of counter evaluation and will try find something non-numerical, i.e. it might absorb and expand the next statement \nextanimal which is a fault here.

If \relax is inserted, this can be stopped. (The \relax is not mentioned on that page, but the expansion can be stopped with \relax then.

A one-time solution would be to place a newline between \resetanimalnum and the next call to \nextanimal, but this might get tedious.

\newcount\animalnum

\def\nextanimal{%
    \ifcase\animalnum%                                                                                                                                      
    \animalI\or
    \animalII\or
    \animalIII%                                                                                                                                             
    \fi%
    \ifnum\animalnum > 2
    \exclaim%
    \fi%
    \advance\animalnum by 1%
}

\def\resetanimalnum{\animalnum=0\relax}%

\def\animalI   {Lions}
\def\animalII  {Tigers}
\def\animalIII {Bears}
\def\exclaim   {Oh my!}

\nextanimal\ \nextanimal\ \nextanimal\ \nextanimal

\resetanimalnum\nextanimal\ \nextanimal

\bye

enter image description here

1
  • \ifcase\animalnum<0>\or<1>\or<2>\else\exclaim\fi is “more correct”.
    – Manuel
    Commented Jan 14, 2016 at 19:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .