3

I would like to store and recursively update mathematical expressions in a macro \temp using the following code:

\documentclass{article}
\begin{document}
\def\temp{}
\def\update#1_#2{
    \xdef\temptemp{\temp}
    \gdef\temp{\temptemp\frac{#1}{#2}}}

\update{a}_{b} [\temp]

\update{c}_{d} [\temp] \end{document}

This code works well and gives a/b and a/b c/d.

However, once I change \frac{#1}{#2} to \underbrace{#1}_{#2}, it returns an error "! Illegal parameter number in definition of \temptemp."

What's the problem and how can I solve this? Thanks in advance.

4 Answers4

4

You are doing full expansion (\xdef) of previous macro declated by \update but \underbrace is more complicated macro which uses \halign with # in its preamble and this cannot be fully expanded without immediately processing.

You can update the list of math formulae without expansion:

\def\update#1_#2{\addto\temp{\underbrace{#1}_{#2}}}

where \addto is defined by:

\long\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
wipet
  • 74,238
  • Can you explain the meaning of long? – projetmbc Jun 27 '22 at 06:55
  • 2
    @projetmbc Generally-speaking the TeXbook/TeX by topic contains info on such "low-level" commands. – user202729 Jun 27 '22 at 07:07
  • 2
    @projetmbc The \long is not needed in you case, but I copied it from my set of usable macos, where \addto is ready to add the \par to the existing macro too. Normally \par raises error "paragraph ended before macro is complete". The TeX in a Nutshell http://petr.olsak.net/ftp/olsak/optex/tex-nutshell.pdf describes such "low-level" commands too. – wipet Jun 27 '22 at 07:24
4

Your code seems to work. Let's add some tracing in order to better see what happens:

\documentclass{article}

\begin{document}

\def\temp{} \def\update#1_#2{ \xdef\temptemp{\temp} \gdef\temp{\temptemp\frac{#1}{#2}}}

\update{a}_{b} \texttt{\meaning\temp} [\temp]

\update{c}_{d} \texttt{\meaning\temp} [\temp]

\update{e}_{f} \texttt{\meaning\temp} [\temp]

\texttt{\meaning\temptemp}

\end{document}

enter image description here

As you see, you're not updating \temp with the new part; you're updating \temptemp actually. And the last part in the image shows that you're not really getting \frac{a}{b}, but what \xdef does with it, which is somewhat unpredictable due to the fact that the vast majority of typesetting commands are not supposed to be used in \edef or \xdef. And it goes much worse with \underbrace.

You don't really need anything special: the LaTeX kernel has \g@addto@macro

\documentclass{article}

\newcommand{\container}{} \newcommand\update{} \makeatletter \def\update#1_#2{% \g@addto@macro\container{\underbrace{#1}_{#2}}% } \makeatother

\setlength{\parindent}{0pt}% just for this example

\begin{document}

\update{a}_{b} \texttt{\meaning\container} [\container]

\update{c}_{d} \texttt{\meaning\container} [\container]

\update{e}_{f} \texttt{\meaning\container} [\container]

\end{document}

enter image description here

egreg
  • 1,121,712
3

underbrace is a robust command protected by LaTeX's \protect mechanism. For the mechanism to work properly you need the protected@ functions.

\documentclass{article}
\begin{document}
\def\temp{}

\makeatletter

\def\update#1_#2{% \protected@xdef\temptemp{\temp}% \gdef\temp{\temptemp\underbrace{#1}_{#2}}}

\update{a}_{b} [\temp]

\update{c}_{d} [\temp] \end{document}

Expansion control in TeX is complex. Also, in this case you don't need x-expansion at all. There are dedicated macros for this purpose.

\documentclass{article}
\usepackage{etoolbox}
\begin{document}
\def\temp{}

\makeatletter

\def\update#1_#2{% \appto\temp{\underbrace{#1}_{#2}}}

\update{a}_{b} [\temp]

\update{c}_{d} [\temp] \end{document}

Or see \tl_put_right:Nn in expl3 but there are some "complications" with the catcode of the underscore here.

user202729
  • 7,143
3

\temp with many LaTeX-packages is a scratch-macro. So code not stemming from you might override (re)definitions of \temp stemming from code written by you.

In order to prevent such clashes in the following \Mytemp is used instead of \temp.

Donald E. Knuth in his TeXbook describes TeX analogously to a beast which has eyes, mouth and digestive tract.

I tried to outline the different stages of the processing of a .tex-file, using that analogy, in my answers to

With \def/\gdef expansion is suppressed while tokens forming the definition text go down TeX's gullet. Thus with \def/\gdef the tokens forming the definition text are not expanded when reaching the stomach where performing the assignment takes place.

With \edef/\xdef expansion is not suppressed while tokens forming the definition text go down TeX's gullet. Thus with \edef/\xdef the tokens forming the definition text in a "process of regurgitation" are "fully" expanded when reaching the stomach where performing the assignment takes place.

The crucial point is: \underbrace itself is a macro which at some stage of expansion yields some #. At the time when expansion is done and in TeX's stomach the attempt of performing the assignment underlying \xdef takes place, these # erroneously are taken for reference to some parameter of the macro that is to be defined/redefined.

Besides this you might like to cope with the fact that definitions can be nested inside definitions whereby which each "nesting-level" another level of doubling hashes for denoting parameters of the definition in that definition-level is needed and with the fact that expanding a macro reduces two consecutive hashes occurring in that macro's definition text the a single hash.

E.g., if you do

\long\def\hashcarrier{################A}
\long\xdef\hashcarrier{\hashcarrier B}
\show\hashcarrier
\long\xdef\hashcarrier{\hashcarrier C}
\show\hashcarrier
\long\xdef\hashcarrier{\hashcarrier D}
\show\hashcarrier
%% If you activate the following lines you get error-message about 
%% illegal parameter number in definition of \hashcarrier because 
%% things were reduced to single hashes that are taken for reference 
%% to parameters of the current definition-level while the parameter-text
%% of the current definition-level doesn't denote any parameter at all
%% and parameters must be numbered consecutively from 1 to 9 instead of
%% a thing like A:
%\long\xdef\hashcarrier{\hashcarrier E}
%\show\hashcarrier
\csname bye\endcsname
\stop

you get the following output on terminal / in .log-file

> \hashcarrier=\long macro:
->########AB.
l.3 \show\hashcarrier

? > \hashcarrier=\long macro: ->####ABC. l.5 \show\hashcarrier

? > \hashcarrier=\long macro: ->##ABCD. l.7 \show\hashcarrier

?

(\long means that in case the macro in question processes arguments each of these arguments may contain the control-word-token \par.)

You can prevent both unwanted expansion and reducing amount of hashes via \xdef and \unexpanded:

\documentclass{article}

\newcommand\Mytemp{}% \long\def\updateMytemp#1_#2{% \long\xdef\Mytemp{\unexpanded\expandafter{\Mytemp\underbrace{#1}_{#2}}}% }%

\begin{document}

\updateMytemp{a}_{b} \show\Mytemp [\Mytemp]

\updateMytemp{c}_{d} \show\Mytemp [\Mytemp] \end{document}

Or --legacy style-- via \xdef and \the-expansion of a token-register:

\documentclass{article}

\newtoks\MyScratchtoks \newcommand\Mytemp{}% \long\def\updateMytemp#1_#2{% \MyScratchtoks\expandafter{\Mytemp\underbrace{#1}_{#2}}% \long\xdef\Mytemp{\the\MyScratchtoks}% }%

\begin{document}

\updateMytemp{a}_{b} \show\Mytemp [\Mytemp]

\updateMytemp{c}_{d} \show\Mytemp [\Mytemp]

\end{document}

I suggest doing something more generic using LaTeX's \g@addto@macro:

\documentclass{article}

\newcommand\Mytemp{}% \makeatletter\newcommand\updateMytemp[1]{\g@addto@macro{\Mytemp}{#1}}\makeatother

\begin{document}

\updateMytemp{\underbrace{a}_{b}} \show\Mytemp [\Mytemp]

\updateMytemp{\underbrace{c}_{d}} \show\Mytemp [\Mytemp] \end{document}

With all three examples you get something like the following messages on console and in the .log-file:

> \Mytemp=macro:
->\underbrace {a}_{b}.
l.9 \show\Mytemp

? > \Mytemp=macro: ->\underbrace {a}{b}\underbrace {c}{d}. l.13 \show\Mytemp

With all three examples you get the following pdf-output:

enter image description here

Another hint:

LaTeX's \newcommand, \NewDocumentCommand etc trigger error-messages in case the command to be defined is already defined or cannot be defined due to containing the leading phrase "end" in its name. You can achieve the same behavior with assignments performed via \def/\gdef/\edef/\xdef by wrapping them into LaTeX's \@ifdefinable-test.

E.g., instead of just

\long\def\updateMytemp#1_#2{%
  \long\xdef\Mytemp{\unexpanded\expandafter{\Mytemp\underbrace{#1}_{#2}}}%
}%

you would do

\makeatletter
\@ifdefinable{\updateMytemp}{%
  \long\def\updateMytemp#1_#2{%
    \long\xdef\Mytemp{\unexpanded\expandafter{\Mytemp\underbrace{#1}_{#2}}}%
  }%
}%
\makeatother

If you wonder what \makeatletter/\makeatother is about:

Simplified speaking TeX/LaTeX usually allow only ordinary alphabetic letters a..z/A..Z as components of names of control-word-tokens.

\makeatletter is a directive for telling LaTeX that henceforth @ shall be allowed as component of names of control-word-tokens, too.

\makeatother is a directive for telling LaTeX that henceforth @ shall a be thing which is not allowed as component of names of control-word-tokens.

Without \makeatletter/\makeatother you could use \csname..\endcsname for gathering the name of the control sequence token from what is between \csname..\endcsname and delivering that control sequence token:

\csname @ifdefinable\endcsname{\updateMytemp}{%
  \long\def\updateMytemp#1_#2{%
    \long\xdef\Mytemp{\unexpanded\expandafter{\Mytemp\underbrace{#1}_{#2}}}%
  }%
}%
Ulrich Diez
  • 28,770