7

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).

3
  • 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, 2011 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. May 18, 2011 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 May 18, 2011 at 18:00

3 Answers 3

7

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}
4
  • With etoolbox, I'd get rid of pgffor. It's somewhat hard to use the expansion tricks.
    – Leo Liu
    May 18, 2011 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. May 18, 2011 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, 2011 at 15:47
  • Thanks for the explanations. I wish I'd known this functionality ages ago.
    – Chris
    May 22, 2011 at 20:09
2

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}
0
0

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}

You must log in to answer this question.

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