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.

Tikz/PGF has an extremely useful implementation of \foreach, which allows for instance the construction

\foreach \x / \y in {1/a,2/b,3/c,4/d} {\x is a lot like \y}

in which you can iterate over a pair of lists. This command only works within a tikz environment, so in particular doesn't work in the preamble, where I want to use it for defining a huge number of very similar looking macros, with say \newcommand{\task\x}{\function\y}; (really I'm defining new shapes in tikz, each of which is fifty or more lines of code, so just repeating it all a few hundred times is genuinely impractical).

Is there a function in tex that will easily do something similar? I am aware that I could hack together this functionality using ForArray functions or nested forloops, but the results seemed like they would be comparatively inelegant, and I hoped someone here might have a clean approach (or a clean implementation using arrays or loops).

share|improve this question
3  
\foreach doesn't have to be in a tikzpicture, it also works in the preamble. The problem is that you won't be able to define new commands inside the loop, as each iteration is executed inside a local group, so definitions won't be global. Note also that you have the syntax wrong, it should be in {1/a,2/b,3/c,4/d}. Maybe you could add some more detail about what kinds of shapes you're trying to define? I'm sure that if they can be defined in a for-loop, they can also be parametrised, which would probably be a better approach. –  Jake May 18 '11 at 14:50
    
You could either use \gdef or say \newcommand\foo{...}\global\let\foo\foo, but in both cases you would need to expand \x and \y before the definition. –  Martin Scharrer May 18 '11 at 14:53
    
With regard to the grouping of \foreach, take a look at the answers to this question of mine: tex.stackexchange.com/q/15204/86 –  Loop Space May 18 '11 at 18:00

3 Answers 3

up vote 5 down vote accepted

You can use \xdef (global expanding definition) together with \csname ... \endcsname inside \foreach in the preamble. It works in the preamble but encloses the content in a group which requires the global definition. The loop "variables" (macros) must be expanded before/during the definition, otherwise the macros will just contain \y and not its current content.

Here an example how to define macros like \mymacroa, \mymacrob, etc., which will contain a call to \dostuff which gets the \y content.

\documentclass{article}

\usepackage{pgffor}

\foreach \x/\y in {a/1,b/2,c/3,d/4} {
    \expandafter\xdef\csname mymacro\x\endcsname{%
        \noexpand\dostuff{\y}%
    }%
}

\newcommand{\dostuff}[1]{\texttt{(#1)}}

\begin{document}

\mymacroa

\mymacrob

\mymacroc

\mymacrod

\end{document}

If you want to do something different like defining a PGF shape than it would be better if you simply accumulate the required macro calls with the loop variables expanded and execute the accumulator afterwards.

The following code adds \dostuff{<x>}{<y>} to \mycommands which is executed after the loop outside any group. It then contains \dostuff {a}{1}\dostuff {b}{2}\dostuff {c}{3}\dostuff {d}{4}.

\documentclass{article}

\usepackage{pgf}
\usepackage{pgffor}
\usepackage{etoolbox}

\newcommand{\mycommands}{}

\newcommand{\dostuff}[2]{%
    \pgfdeclareshape{#1}{%
        \anchor{#2}{...}%
    }%
}

\foreach \x/\y in {a/1,b/2,c/3,d/4} {%
    \xappto{\mycommands}{\noexpand\dostuff{\x}{\y}}%
}
%\show\mycommands
\mycommands

\begin{document}

\end{document}
share|improve this answer
    
With etoolbox, I'd get rid of pgffor. It's somewhat hard to use the expansion tricks. –  Leo Liu May 18 '11 at 15:14
    
@Leo: If you use a helper macro like \dostuff which simply accepts the expanded macros there shouldn't be any issue with the expansion. If \x and \y are complex they are better written as \expandonce\x and \expandonce\y. –  Martin Scharrer May 18 '11 at 15:32
    
\xdef, \noexpand, \expandonce... I can do it, but often make mistakes. Expansion is always a blackhole in TeX. That's why I don't like it. I prefer \docsvlist or \forcsvlist, which take the loop variable a argument — then there's no expansion issue at all. –  Leo Liu May 18 '11 at 15:47
    
Thanks for the explanations. I wish I'd known this functionality ages ago. –  Chris May 22 '11 at 20:09

pgffor does work outside tikzpicture environment, although it is not robust enought for some applications.

One problem is, you wrongly use \newcommand. You can't use \newcommand{\task\x}{\function\y}, use \@namedef{task\x}{\function\y} or something similar instead.

The other problem is due to pgffor. It is executed in a group, and you must expand the variable when define a new macro using it.

Example solution, you must control the expansion of \x and \y carefully:

\documentclass{article}
\usepackage{pgffor}
\foreach \x/\y in {a/1,b/2,c/3} {
  \expandafter\xdef\csname cmd\x\endcsname{number \y}
}
\begin{document}
\cmda, \cmdb, \cmdc
\end{document}

Another solution using etoolbox, this is preferred:

\documentclass{article}
\usepackage{etoolbox}
\def\dodo#1/#2{\csdef{cmd#1}{number #2}}
\def\do#1{\dodo#1}
\docsvlist{a/1,b/2,c/3}
\begin{document}
\cmda, \cmdb, \cmdc
% we obtain: number 1, number 2, number 3
\end{document}
share|improve this answer
    
Thanks, I'll try this approach out too. –  Chris May 22 '11 at 20:10

With the catoptions package the auxiliary \dodo is not necessary and you can also use an arbitrary list separator. Also, each element of the list is first trimmed of spurious leading and trailing spaces before \do takes effect.

\documentclass{article}
\usepackage{catoptions}[2011/10/22]

\begin{document}

\def\do#1/#2{\csndef*{cmd#1}{number #2}}
\dofunclist{a/1, b/2, c/3}

%\dofunclist[;]{a/1; b/2; c/3}
\cmda, \cmdb, \cmdc

\end{document}
share|improve this answer

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.