11

My \john command is defined like so:

\def\john{\DontExpandMe}

I would now like to repeatedly change its definition, to keep adding some extra stuff on the front.

\foreach\i in {ape,bat,cow,dog} {
  \xdef\john{\i,\unexpanded{\john}}
  \show\john
}

My intention is that the \show\john command at each iteration should result in:

\john=macro: -> \DontExpandMe.
\john=macro: -> ape,\DontExpandMe.
\john=macro: -> bat,ape,\DontExpandMe.
\john=macro: -> cow,bat,ape,\DontExpandMe.
\john=macro: -> dog,cow,bat,ape,\DontExpandMe.

That is to say, I would like \john to be "partially expanded" in some sense. But I can't make that work. I have tried the following

  • If I use an \xdef, then the whole command is expanded, including the \DontExpandMe part.
  • If I just use a \gdef, then the \i is not expanded.
  • If I use \xdef with \unexpanded{...} around \john (as I have in my current code) then I get \john=macro: -> ape,\john. and \john=macro: -> bat,\john. and so on.

Here is my code.

\documentclass{article}
\usepackage{pgffor}
\begin{document}
  \def\john{\DontExpandMe}
  \show\john
  \foreach\i in {ape,bat,cow,dog} {
    \xdef\john{\i,\unexpanded{\john}}
    \show\john
  }
\end{document}
3

4 Answers 4

13

The line in question is:

\xdef\john{\i,\unexpanded{\john}}

\i should expanded (full/once?) and \john should be expanded once

Partial expansion with \xdef and \unexpanded:

Before \unexpanded reads the open curly brace, it is in expanding mode for gobbling spaces. Therefore it can be used to sneak in a \expandafter:

\xdef\john{\i,\unexpanded\expandafter{\john}}

(Without this trick, two \expandafter would be needed:

\xdef\john{\i,\expandafter\unexpanded\expandafter{\john}}

The same can also be achieved without e-TeX by using a token register:

\toks0=\expandafter{\john}% similar trick as above to minimize the number of \expandafter
\xdef\john{\i,\the\toks0}

The contents of the token register is not further expanded inside \edef.

One expansion step for \i and \john:

  • Same as above, but for \i, too:

    \xdef\john{\unexpanded\expandafter{\i},\unexpanded\expandafter{\john}}
    
  • \expandafter orgy with \gdef:

    \expandafter\expandafter\expandafter\gdef
    \expandafter\expandafter\expandafter\john
    \expandafter\expandafter\expandafter{%
      \expandafter\i\expandafter,\john
    }
    

    First \john is expanded once, then \i.

1
  • Thank you Heiko. (+1 for the \expandafter orgy!) May 30, 2013 at 9:45
8

There are many ways to do this; the easiest, but riskier, is to use \xdef:

\def\john{\DontExpandMe}
\show\john
\foreach\i in {ape,bat,cow,dog} {%
  \xdef\john{\i,\unexpanded\expandafter{\john}}%
  \show\john
}

Why riskier? Try with \textbf{ape} in the list to see the effect: commands that don't survive \edef will make the code die horribly. A better choice would be expanding also \i only once:

  \xdef\john{\unexpanded\expandafter{\i},\unexpanded\expandafter{\john}}%

Using token registers might be an option:

\def\john{\DontExpandMe}
\show\john
\foreach\i in {ape,bat,cow,dog} {%
  \toks0=\expandafter{\i}%
  \toks2=\expandafter{\john}%
  \xdef\john{\the\toks0,\the\toks2}%
  \show\john
}

Tokens delivered by \the<token registers> are not subject to further expansion in an \edef or \xdef.

A macro in LaTeX3 language:

\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\prependreverselist}{mm}
 {% #1 is the macro to extend; #2 is the list
  \clist_map_inline:nn { #2 }
   {
    \tl_put_left:Nn #1 { ##1 , }
   }
 }
\ExplSyntaxOff

\newcommand{\john}{\DontExpandMe}

\prependreverselist{\john}{ape,bat,cow,dog}

\show\john
4
  • Cool, thanks :-). Why do you use \toks0 and \toks2 rather than, say, \toks0 and \toks1? May 30, 2013 at 9:39
  • I'd use a group for the toks (see my answer, which I'll probably remove).
    – Joseph Wright
    May 30, 2013 at 9:40
  • 1
    @JohnWickerson By convention, even-numbered scratch registers should be used locally and odd-numbered ones globally. So \toks1 is not appropriate here.
    – Joseph Wright
    May 30, 2013 at 9:40
  • 1
    @JosephWright \foreach does each cycle in a group, so adding one is not necessary.
    – egreg
    May 30, 2013 at 9:44
6

At the TeX level, this can be done at least couple ways depending on whether we assume e-TeX is available. The classical way, with no e-TeX, is to use a token register for the definition:

\long\def\addtoclist#1#2{%
  \begingroup
    \toks@\expandafter{#1}%
    \toks2{#2}%
    \edef#1{\the\toks2,\the\toks@}%
  \expandafter\endgroup
  \expandafter\def\expandafter#1\expandafter{#1}%
}

where I've been cautious and assumed that the new material (#2) should not expand at all. The way that the above works is that TeX only expands token registers ('toks') once inside an \edef, so I can be sure no further expansion will occur. I've used two temporary toks: \toks@ (which is \toks0 addressed by name), and \toks2 (addressed by number). The group means I won't affect any value already stored in these.

With e-TeX, we don't need the group or toks assignments

\protected\long\def\addtoclist#1#2{%
  \edef#1{\unexpanded{#2},\unexpanded\expandafter{#1}}%
}

where the \unexpanded primitive has behaviour similar to a toks: you can expand the material to be assigned using \expandafter just before the {.

(In both cases, you really need to check for an empty initial definition, but that is not the key point here.)

5

With the help of cgnieder's comment, I have fixed my code as follows:

\documentclass{article}
\usepackage{pgffor}
\usepackage{etoolbox}
\begin{document}
  \def\john{\DontExpandMe}
  \show\john
  \foreach\i in {ape,bat,cow,dog} {
    \xdef\john{\i,\expandonce{\john}}
    \show\john
  }
\end{document}

And with the help of cgnieder's other comment, I have shortened my code as follows:

\documentclass{article}
\usepackage{pgffor}
\begin{document}
  \def\john{\DontExpandMe}
  \show\john
  \foreach\i in {ape,bat,cow,dog} {
    \xdef\john{\i,\unexpanded\expandafter{\john}}
    \show\john
  }
\end{document}
1
  • 3
    Actually you don't need etoolbox for this. The definition of \expandonce is simple: \newcommand{\expandonce}[1]{\unexpanded\expandafter{#1}}
    – cgnieder
    May 30, 2013 at 9:17

You must log in to answer this question.

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