Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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?

share|improve this question
add comment

2 Answers

up vote 9 down vote accepted

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.

share|improve this answer
    
Ah, much nicer answer! –  Jake Aug 23 '11 at 13:50
    
Thanks a lot for the expalinations! I constantly run into such things where my analogies to certain programming languages fail. Now at least I have a clue as to why :D –  Tom Bombadil Aug 23 '11 at 13:55
    
@Tom: May I suggest you accept this answer instead of mine? It'll be much more helpful to other people stumbling across similar issues than my very brief explanation. –  Jake Aug 23 '11 at 13:57
    
@Jake: Done. You were both very helpful, so I decided to go with the first answer posted –  Tom Bombadil Aug 23 '11 at 14:41
add comment

\foreach runs its commands inside scopes, so changed macros won't be available outside the loop. What you could do instead is use the \global command to make the assignment available outside the scope, and use \edef instead of \renewcommand to expand the old macro before assigning it again. Also, you don't need to use {1,2,...,#1} to indicate a step of one, instead you can just say {1,...,#1}. That allows the macro to be used for single repetitions as well (not that that makes much sense in this case).

Edit: \myresult should be initially set to {} inside the \longertext macro, as Tom Bombadil pointed out.

\documentclass{article}
\usepackage{tikz}

\newcommand{\longertext}[1]%
{%
    \gdef\myresult{} %
    \foreach \x in {1,...,#1}%
    {%
        \global\edef\myresult{blah \myresult}%
    }%
}

\begin{document}
Start

\longertext{5}
\myresult

\longertext{3}
\myresult

Stop
\end{document}
share|improve this answer
    
Thanks! If you however use the command twice (e.g. with parameters 3 and 5) then the second one contains 3+5=8 blahs. I solved it by putting \gdef\myresult{} before the loop to reset the text to nil. Are there any objections to doing so, and is the initial \gdef\myresult{} needed if it is defined inside \longertext? –  Tom Bombadil Aug 23 '11 at 13:44
    
@Tom: That sounds like a sensible way to do it (initialising \myresult inside the \longertext macro). In that case, you don't need the initial \gdef\myresult{}. I've incorporated that suggestion into my answer. –  Jake Aug 23 '11 at 13:48
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.