Since I normally use tikz
in my documents, I mostly use the \foreach \x in \list
loop to repeat things. However, this famously fails in tabular
environments and also everywhere where things must be expanded.
Thus I've written a \for
macro which works in tabular
, but I'd like to know how to do it better. It's not completely expandable and probably also quite inefficient.
This is okay, because most tables don't have thousands of cells, so performance is usually not an issue. It works in tabular
environments to generate multiple columns in a single line or - with some additional care - to generate multiple rows:
As mentioned above, there are clearly some issues with this loop, but most of the time it "works for me". Still I'd like to improve it, so these are the points that bug me the most:
- Each iteration's loop body is completely expanded using
\edef
. This ensures that the loop counter is expanded to its value during that iteration. However this also requires the user to protect some things using\noexpand
, like\pgfmathparse
or\\
and\hline
in the tables below. Also it would require double protection (triple\noexpand
maybe?) for nested loops. - Obviously it's not completely expandable because it uses
\def
and\edef
, so it's not possible to use the loop inside, say, another\edef
in order to define a macro that has a regular structure. Although of course it's perfectly possible to\def
a macro that expands to a loop invocation.
Now, how could these be solved?
- For problem 1 it would be great to have a way to expand only the
loop counter anywhere it's used in the loop body, but leave
everything else alone. This would eliminate all the
\noexpand
s. - For problem 2 I suspect that it could be possible to sacrifice some functionality, like the flexible loop counters and conditions (e.g.
\repeat{\i}{5}{some code using \i}
instead of the C-like syntax).
However I don't really know how to approach these problems. Can anyone give me some pointers in the right direction? Is it at all possible to achieve both, selective expansion and complete expandability?
Finally, here's the code:
\documentclass{article}
\begin{filecontents*}{myforloop.sty}
\usepackage{pgf}
\usepackage{pgffor}
\usepackage{ifthen}
% Example:
% \for (\i = 0; \i < 10; \i = \i + 1) {
% some code using \i
% }
\def\for(#1=#2;#3;#4=#5)#6{%
\def\forloopresult{}% Macro to store the result
\pgfmathtruncatemacro{#1}{#2}% Set initial value of counter
\def\forloopinner{% Recursive loop macro
\pgfmathparse{int(#3)}% Calculate loop condition
\ifthenelse{1=\pgfmathresult\relax}{% if loop condition is true:
\toks@=\expandafter{\forloopresult}% Get tokens from result macro
\edef\forloopresult{\the\toks@#6}% Append expanded code to result
\pgfmathtruncatemacro{#1}{#5}% Set new counter value
\forloopinner% Recurse
}{% else:
\forloopresult% Expand to result
}%
}%
\forloopinner% Start looping
}
\end{filecontents*}
\usepackage{myforloop}
\newcommand\N{5}% Number of loop iterations
\begin{document}
\begin{tabular}{|r|*{\N}{r|}}
\hline
$x$: \for(\x=0; \x<\N; \x=\x+1) {&\x}\\\hline
$e^x$: \for(\x=0; \x<\N; \x=\x+1) {%
& \noexpand\pgfmathparse{exp(\x)}\noexpand\pgfmathresult%
}\\\hline
\end{tabular}
\vspace{1em}
\begin{tabular}{|l|l|}
\hline
$x$ & $e^x$ \\\hline
\for(\x=0; \x<\N; \x=\x+1) {%
\x & \noexpand\pgfmathparse{exp(\x)}\noexpand\pgfmathresult
\noexpand\\\noexpand\hline
}
\end{tabular}
\end{document}