# Non-tail recursion and argument expansion order

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

\newcount\x

\def\dec#1{%
\ifnum#1=0
.%
\else
#1%
\x=#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.

• 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 Commented Dec 13, 2022 at 10:59

\newcount\x

\def\dec#1{%
\ifnum#1=0
.%
\else
#1%
\x=#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
\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


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

• Thank you for your excellent description! 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. Commented Dec 13, 2022 at 11:26
• @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 :) Commented Dec 13, 2022 at 11:34

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