[Tex/LaTex] Why do paramaters of \renewcommand need to double up the # within a \foreach

foreachmacros

Introduction:

When you use \renewcommand within another macro or environment, one needs to double up the ## to access the parameters of the inner macro. This serves as a way to distinguish the inner macros ##1 with the outer macros #1 (even if the outer macro does not have any parameters, it provides for error checking). For example:

\newcommand*{\OuterCommand}{%
    \renewcommand*{\SomeCommand}[1]{\color{red}##1}%
    ....
}%  

The references below provide more details on this.

Issue:

It appears that the same doubling up of the # is needed within a \foreach loop as illustrated in the MWE below.

This took me quite some time to figure this out as the error message that the MWE below gives is:

Illegal parameter number in definition of \pgffor@body

This was within a very deeply nesting of \foreach loops, so I was sure the problem was that I was nesting the \foreach too deeply. Surprisingly, if you skip past the error, the output is still correct, so this made it even harder to locate the problem.

Questions:

  • Since the \foreach does not have parameters like #1 (as \newcommand does), why is it necessary to double up the # within a definition within a \foreach?
  • How is that the output is still correct, if you simply ignore the error and hit return to continue processing?

References:

Code:

\documentclass{article}
\usepackage{xcolor}
\usepackage{pgffor}

\newcommand*{\MyList}{A,B,C}%
\newcommand{\SomeCommand}[1]{#1}%

\begin{document}
\foreach \x in \MyList {%
    \renewcommand*{\SomeCommand}[1]{\color{red}#1}%  Using ## here eliminates the error.
    \par\SomeCommand{\x}%
}%
\end{document}

Best Answer

Internally the definition of foreach will be saving the body of the loop in a macro so it is like (if looping over a,b,... )

\def\body{%
\renewcommand*{\SomeCommand}[1]{\color{red}#1}%  Using ## here eliminates the error.
    \par\SomeCommand{\x}%
}%
}
\def\x{a}\body
\def\x{b}\body
...

That initial \def (or \newcommand if you prefer) will require a # in the body to be entered as ##. As shown above you would get an error on #1 as \body doesn't take parameters it would have to be entered, as you found, as ##1.

It is possible to define loops in such a way not to require this (for example it could use a token register rather than a macro for saving the body) but that is apparently not the case here.


Note that the need to double # is unrelated to the fact that there is an inner macro definition. It is not that inner macros require ##1 to refer to the first parameter, it is simply that the inner macro definition requires the two tokens # and 1 to refer to the first parameter and to get a # into a macro body you need ##. To see this consider a definition that doesn't have ##1.

You can go

\let\hash=#

and \hash will be let to # but you can not define a macro as

\def\definehash{\let\hash=#}

as that generates

! Illegal parameter number in definition of \definehash.

You need ## to refer to a literal # in a macro definition. So it needs to be

\def\definehash{\let\hash=##}

You ask in comments why it needs four # for a double nested definition. Again it is nothing special about the inner definitions, they never "see" ##1 by the time they are evaluated they just see a single #. If you tried to define

\def\ddefinehash{\def\definehash{\let\hash=##}}

The definition would proceed without error but then if you try to execute \ddefinehash you find that it just has a single # in its definition and so you get the error as before:

! Illegal parameter number in definition of \definehash.
<to be read again> 
                   }
l.12 \ddefinehash

So you need to get two # into the definition of \definehash and each of those has to be entered as ## so you end up with

\def\ddefinehash{\def\definehash{\let\hash=####}}

Putting it all together:

{
\let\hash=#
}

{
\def\definehash{\let\hash=##}
}

{
\def\ddefinehash{\def\definehash{\let\hash=####}}
\ddefinehash
\definehash

\show\ddefinehash
\show\definehash
\show\hash
}

\bye

> \ddefinehash=macro:
->\def \definehash {\let \hash =####}.
l.15 \show\ddefinehash

? 
> \definehash=macro:
->\let \hash =##.
l.16 \show\definehash

? 
> \hash=macro parameter character #.
l.17 \show\hash

? 
 )
No pages of output.
Related Question