[Tex/LaTex] How to concatenate strings, to e.g. create commands that return lists

foreachstringstikz-pgf

I was thinking about Felix' Question, where he wanted to return lists in TikZ. My approach so far was to return the single results as individual \nodes, separated one centimeter each. Now I would like to know if there is a way to append some text to a variable (or command or macro or whatever) on each iteration of a \foreachloop. I tried this:

\documentclass{scrartcl}
\usepackage{tikz}

\newcommand{\myresult}{}

\newcommand{\longertext}[1]%
{   \foreach \x in {1,2,...,#1}
    {   \renewcommand{\myresult}{blah \myresult}
    }
}

\begin{document}
Start

\longertext{5}
\myresult

Stop
\end{document}

I thought this to be like in e.g. Pascal:

s:='';
for i:=1 to 5 do begin
  s:='blah '+s;
end;

So basically I thing my code should do the following:

  • create an empty variable myresult
  • for each iteration of the loop append "blah " at the begining
  • so, after calling \longertext{5}, \myresult should hold "blah blah blah blah blah "

But when I run it, \myresult appears to be empty. Can anybody explain to me why, and possibly offer a solution?

Best Answer

The explanation will need a bit of TeX theory.

A group is a part of the document that confines most changes made inside it only to this group itself. That is, most settings changed inside a group will be reverted to their old values once the group ends. A group is delimited by { … } or \begingroup … \endgroup. For example, a command \itshape switches the font shape to italic. If it is used in the document, all text after that point will be set in italic shape (unless another command switches the font again). But when used inside a group — {\itshape test} — its effect will only last to the end of the group; when the group ends, the font will be restored to whatever it was before the group was started. If an assignment or a macro definition should transcend any groups it might be in and remain in effect after the groups end, it must be prefixed with \global (this only works for primitive TeX operations).

Replacing a macro with its replacement text is called expanding the macro. TeX expands macros as it reads the input, but it does not necessarily expand arguments to commands — they are passed as they are. Therefore, in

\def\a{a}
\def\b{b \a}

(\def is the TeX primitive for defining new macros), the replacement text for \b is b \a, not b a. Only when \b is used, \a will be expanded recursively. If it is needed that \a is expanded at the time of \b definition, there exists a similar primitive to \def\edef, which fully (that is, recursively) expands the to-be replacement text and only then associates the text with the macro name.

Now let's look at the code.

It cannot be seen directly, but the iteration code in \foreach is processed inside a group. Therefore, the result of \renewcommand is immediately forgotten after each iteration, and in the end \myresult is still empty. We can remedy this by using \global\def instead of \renewcommand (\renewcommand is a complex macro and won't be affected by \global):

\global\def\myresult{blah \myresult}

But now we get a different problem. After at least one iteration, \myresult now has the replacement text blah \myresult. If TeX encounters \myresult in input, it will replace the macro with blah \myresult, which it will replace then with blah blah \myresult, … until there is no more memory left. The problem is that we caused an infinite recursion by defining a macro that expands to itself. We wanted to use the old value of \myresult when we redefined it, but ended up using the macro itself. We already know that we can use \edef instead of \def to do the former instead of the latter. Now the relevant line becomes

\global\edef\myresult{blah \myresult}

Plain TeX defines \xdef to be \global\edef. So we can shorten the code:

\xdef\myresult{blah \myresult}

Now it will work as planned.

With all this explained, package etoolbox provides the commands \xpreto<hook>{<code>} and \xappto<hook>{<code>}, which will globally prepend or append the expansion of code to the definition of <hook>. I should also note that spaces and new lines are significant to TeX and the code needs some comment characters.