6

I'm having a trouble understanding the expansion order of TeX macros:

\newcount\x

\def\dec#1{%
  \ifnum#1=0
    .%
  \else
    #1%
    \x=#1
    \advance\x by-1
    \dec{\number\x}%
    #1%
  \fi%
}
\dec{5}

\bye

The above non-tail recursion results in 54321.00005, instead of 54321.12345. I understand that what I want can be achieved by restoring \x after the recursive \dec expansion. However, I do not quite get why this results in 00005.

Can someone enlighten me? Thank you.

2
  • 1
    please post code in a form that can be run. Presumably x is defined by \newcount\x ?? Commented Dec 13, 2022 at 10:58
  • Yes indeed, sorry for the inconvenience
    – Jay Lee
    Commented Dec 13, 2022 at 10:59

2 Answers 2

7
\newcount\x

\def\dec#1{%
  \ifnum#1=0
    .%
  \else
    #1%
    \x=#1
    \advance\x by-1
    \dec{\number\x}%
    #1%
  \fi%
}

{\tracingmacros1 \tracingonline1
\dec{5}
}
\bye

produces

\dec #1->\ifnum #1=0 .\else #1\x =#1 \advance \x by-1 \dec {\number \x }#1\fi 
#1<-5

\dec #1->\ifnum #1=0 .\else #1\x =#1 \advance \x by-1 \dec {\number \x }#1\fi 
#1<-\number \x 

\dec #1->\ifnum #1=0 .\else #1\x =#1 \advance \x by-1 \dec {\number \x }#1\fi 
#1<-\number \x 

\dec #1->\ifnum #1=0 .\else #1\x =#1 \advance \x by-1 \dec {\number \x }#1\fi 
#1<-\number \x 

\dec #1->\ifnum #1=0 .\else #1\x =#1 \advance \x by-1 \dec {\number \x }#1\fi 
#1<-\number \x 

\dec #1->\ifnum #1=0 .\else #1\x =#1 \advance \x by-1 \dec {\number \x }#1\fi 
#1<-\number \x 

so you see that you are not passing in different values each time, every argument is \number \x so at the end you have multiple \number\x so they all print 0


You can expand \number before recursing

\newcount\x

\def\dec#1{%
  \ifnum#1=0
    .%
  \else
    #1%
    \x=#1
    \advance\x by-1
    \expandafter\dec\expandafter{\number\x}%
    #1%
  \fi%
}

{\tracingmacros1 \tracingonline1
\dec{5}
}
\bye

Produces

\dec #1->\ifnum #1=0 .\else #1\x =#1 \advance \x by-1 \expandafter \dec \expand
after {\number \x }#1\fi 
#1<-5

\dec #1->\ifnum #1=0 .\else #1\x =#1 \advance \x by-1 \expandafter \dec \expand
after {\number \x }#1\fi 
#1<-4

\dec #1->\ifnum #1=0 .\else #1\x =#1 \advance \x by-1 \expandafter \dec \expand
after {\number \x }#1\fi 
#1<-3

\dec #1->\ifnum #1=0 .\else #1\x =#1 \advance \x by-1 \expandafter \dec \expand
after {\number \x }#1\fi 
#1<-2

\dec #1->\ifnum #1=0 .\else #1\x =#1 \advance \x by-1 \expandafter \dec \expand
after {\number \x }#1\fi 
#1<-1

\dec #1->\ifnum #1=0 .\else #1\x =#1 \advance \x by-1 \expandafter \dec \expand
after {\number \x }#1\fi 
#1<-0

enter image description here


With etex (including pdftex, luatex, xetex, ...) You do not need \x


\def\dec#1{%
  \ifnum#1=0
    .%
  \else
    #1%
    \expandafter\dec\expandafter{\number\numexpr#1-1\relax}%
    #1%
  \fi%
}

{\tracingmacros1 \tracingonline1
\dec{5}
}
\bye
5
  • Thank you for your excellent description!
    – Jay Lee
    Commented Dec 13, 2022 at 11:18
  • @JayLee I just added a version without \x (so is expandable as has no assignments) Commented Dec 13, 2022 at 11:22
  • @DavidCharlisle Can you please elaborate on the "as has no assignments" part? I can understand the code, thank you.
    – Jay Lee
    Commented Dec 13, 2022 at 11:26
  • 1
    @JayLee \message{\dec{6}} works in the last version as it only uses expansion. It fails in the versions with \x=#1 as assignments do not happen in such a context Commented Dec 13, 2022 at 11:30
  • @DavidCharlisle Ahha, I now get it. Thank you again :)
    – Jay Lee
    Commented Dec 13, 2022 at 11:34
1

Via the last #1 in the \else-branch of \dec TeX in the memory accumulates the tokens \number\x rather than results of evaluating \number\x (which in each iteration would give another result). When all the accumulated \number\x get evaluated after the last iteration, the value of \x is 0.

In order to make more obvious what is happening, let's evaluate the argument via \expandafter and \number before via \exchange placing the evaluation-result at the end of the \else-branch/right before the closing \fi:

\newcount\x

\long\def\exchange#1#2{#2#1}

\def\dec#1{%
  \ifnum#1=0
    .%
  \else
    #1%
    \expandafter\exchange\expandafter{\number#1}{%
      \x=#1
      \advance\x by-1
      \dec{\number\x}%
    }%
  \fi%
}
\dec{5}

\bye

This code is only to exhibit what the problen with the code of the question is.

For practical use the code provided by David Carlisle in his answer is much better.

You must log in to answer this question.

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