# What's the difference between the various methods for producing for loops?

I've been wondering about the various methods available for looping over a comma separated list and the like. In particular, I was wondering about their various strengths and weaknesses. In other words, I was wondering about such things as the following (but not solely limited to this list):

1. whether or not they're expandable,
2. how they handle empty items,
3. how they handle extraneous leading and trailing spaces
4. whether you can use \def/\edef or need to use \gdef/\xdef to save information from within the loop for later use.

In no particular order, here's a list of looping methods I'm familiar with. In the following list \current@item represents a macro, taking one argument, for formatting the current item in iteration.

Using commands from the 2ekernal:

%% \@for
\def\@for@myloop#1{%%
\@for \x:=#1 \do{\current@item \x}}

%% \@tfor (not a comma separated list--probably shouldn't be here)
\def\@tfor@myloop#1{%%
\@tfor \x:=#1 \do{\if,\x\relax\else\current@item \x\fi}}


Using etoolbox package

%% etoolbox:  need to be careful whether passed a macro:
%%  in that case expansion may be necessary to that the
%%  delimiters are visible to \forcsvlist
\def\etoolbox@myloop#1{%%
\expandafter\forcsvlist
\expandafter\current@item
\expandafter{#1}}


Using pgffor package

%% pgffor
\def\pgffor@myloop#1{%%
\foreach \x in {#1} {\current@item \x}}


A homebrew method:

%% version a la ae
\def\@ae@myloop#1,#2\@nil{%%
\current@item{#1}%%
\expandafter\ifx\expandafter\relax\detokenize{#2}\relax\else
\@ae@myloop#2\@nil
\fi
}
\def\ae@myloop#1{%%
\@ae@myloop#1,\@nil
}


Then there are also the various expl3 methods such as (there are quite a few, so this is hardly exhaustive):

\tl_map_inline:nn
\tl_map_function:Nn
\clist_map_inline:Nn
\seq_map_inline:Nn


Here is a MWE illustrating the results of each of these (including one LaTeX3 version):

\documentclass{article}
\usepackage[margin=0.5in,paperheight=15in]{geometry}
\usepackage{xparse}
\usepackage{etoolbox}
\usepackage{pgffor}
\usepackage{enumitem}
\makeatletter

\def\my@item@count{0}
\def\step@my@counter{\xdef\my@item@count{\number\numexpr\my@item@count+1\relax}}
%% \rules to emphasize how spaces are seen and treated!
\def\current@item#1{\step@my@counter\item \rule{0.4pt}{2ex}#1\rule{0.4pt}{2ex}}

%% etoolbox
\def\etoolbox@myloop#1{%%
\forcsvlist \current@item {#1}}
%% but if passed a macro, then it  first needs to be expanded so the delimiters are visible to \forcsvlist.  You'll need to write
%% \expandafter\forcsvlist \expandafter\current@item \expandafter{#1}}

%% \@for
\def\@for@myloop#1{%%
\@for \x:=#1 \do{\current@item \x}}

%% \@tfor
\def\@tfor@myloop#1{%%
\@tfor \x:=#1 \do{\current@item \x}}

%% pgffor
\def\pgffor@myloop#1{%%
\foreach \x in {#1} {\current@item \x}}

%% version a la ae
\def\@ae@myloop#1,#2\@nil{%%
\current@item{#1}%%
\expandafter\ifx\expandafter\relax\detokenize{#2}\relax\else
\@ae@myloop#2\@nil
\fi
}
\def\ae@myloop#1{%%
\@ae@myloop#1,\@nil
}

\def\listoffruit#1#2#3{%%
\def\my@item@count{0}%%
\noindent
List of type \texttt{#1}: \parbox[t]{3in}{\raggedright#3}
\begin{itemize}[topsep=4pt,itemsep=2pt]
\csname#1@myloop\endcsname{#2}%%
\end{itemize}
Total number of bulleted items: \my@item@count
\par \vspace{2ex}\hrule\par \vspace{2ex}
}

\ExplSyntaxOn
\NewDocumentCommand{\expl@myloop}{ m }
{
\clist_map_inline:nn{#1}{\current@item {##1}}
}
\ExplSyntaxOff

\makeatother
\def\apples{apples}
\def\bananas{bananas}
\def\cherries{cherries}

\begin{document}

\listoffruit{etoolbox}{apples,, oranges, bananas ,cherries}{Ignores leading spaces and empty items. Trailing spaces not ignored.}

\listoffruit{@for}{apples,, oranges, bananas ,cherries}{Empty lines and trailing or leading spaces not ignored.  Something goes on with last item in list.}

\listoffruit{@tfor}{\apples,{} {oranges}\bananas\cherries}{Spaces ignored, all other token respected, bracketed tokens treated as one.}

\listoffruit{pgffor}{apples,, oranges, bananas ,cherries}{Ignores leading spaces, empty items and trailing spaces not ignored.}

\listoffruit{ae}{apples,, oranges, bananas ,cherries}{Leading or trailing spaces not ignored.  Empty items not ignored.}

\listoffruit{expl}{apples,, oranges, bananas ,cherries}{Trailing or leading spaces ignored.   Empty items ignored.}

\end{document}


Issues I'm aware of:

• I believe \@for is not expandable,
• \cslist_map_inline:nn is expandable but with limitations: i.e., it is not expandable in an f-type argument.
• pgffor's \foreach loop in executed within a group. So you need to use \gdef or \xdeg to save information from within the group for later use. I've not fully explored which others of the loops presented here have a similar short-coming (not sure that's the right choice of word). I have no idea whether \foreach is expandable or not.
• Some methods handle their lists rather nicely whether passed explicitly or implicitly via a macro. For example, pgffor's \foreach knows what do to with a list passed via a macro. etoolbox's \forcsvlist needs that macro to first be expanded: hence the reason my first illustration of a \forcslist is as complicated as it is.

So what I'm interested in here is:

1. Responses which address the strengths and weaknesses of the different approaches presented here as itemized at the beginning of this post, but not necessarily limited to those suggestions since I may be unaware of other important issues.. For example, I'm not really sure which are expandable.
2. Responses which introduce other methods for iterating over a list of items along with their known weaknesses and strengths.
3. Responses which can illustrate realistic examples for which one would want to use such a loop in an expandable context
4. Reponses which illustrate how one can save information gathered from within the group for later use.
• There is also forloop. – marczellm Oct 27 '13 at 7:50
• @marczellm forloop is for looping a counter: much more like expl3's \int_step_function:nnnN or similar. – Joseph Wright Oct 27 '13 at 8:12
• Serendipity. The way you passed that list to be directly used by \foreach saved me asking a question. I'm on a steep learning curve with tikz and pgfplots and as yet just getting started on loops and your question is right up my alley. Can't wait to see the answers. – Geoff Pointer Oct 27 '13 at 9:28
• Why \expandafter\ifx\relax\detokenize{#1}\relax? The shorter version \if\relax\detokenize{#1}\relax is safe and reliable. About the bulk of the question: remove the braces around the items and try also with leading and trailing spaces. – egreg Oct 27 '13 at 10:02
• @egreg I used \ifx because I understand it better than \fi. I'm not really sure what you mean in your suggestion for improving the readability of the question. Could you post a link to another question that does something similar to what you're trying to suggestion? – A.Ellett Oct 27 '13 at 14:50

I propose a different definition of \current@item

\def\current@item#1{%
\stepcounter{item@count}
\item $|$#1$|$\ $|$\texttt{\detokenize{#1}}$|$%
}


so the output also shows what's really passed as its argument. Also I changed the complicated management of \my@item@count with a simple counter. I don't comment about \@tfor, which is a different tool not designed for comma separated lists.

1. etoolbox: \forcsvlist doesn't remove trailing spaces; your definition is too complicated, because

%% etoolbox
\def\etoolbox@myloop#1{%%
\forcsvlist\current@item{#1}}


suffices. Items are passed explicitly.

2. \@for is the basic tool defined in the LaTeX kernel. Leading and trailing spaces are not removed. Items are passed as \x, the control sequence used after \@for.

3. \foreach doesn't remove trailing spaces. Items are passed as \x, the control sequence used after \foreach.

4. ae is basically like \@for, although it works by expansion. Leading and trailing spaces are not removed. Items are passed explicitly.

5. expl removes leading and trailing spaces, but also empty items; items are passed explicitly.

\documentclass{article}
\usepackage[margin=0.5in,paperheight=15in]{geometry}
\usepackage{xparse}
\usepackage{etoolbox}
\usepackage{pgffor}
\usepackage{enumitem}
\makeatletter

\newcounter{item@count}
%% \rules to emphasize how spaces are seen and treated!
\def\current@item#1{%
\stepcounter{item@count}
\item $|$#1$|$\ $|$\texttt{\detokenize{#1}}$|$%
}

%% etoolbox
\def\etoolbox@myloop#1{%%
\forcsvlist\current@item{#1}}

%% \@for
\def\@for@myloop#1{%%
\@for \x:=#1\do{\current@item \x}}

%% pgffor
\def\pgffor@myloop#1{%%
\foreach \x in {#1} {\typeout{pgf:\x}\current@item \x}}

%% version a la ae
\def\@ae@myloop#1,#2\@nil{%%
\current@item{#1}%%
\if\relax\detokenize{#2}\relax\else
\@ae@myloop#2\@nil
\fi
}
\def\ae@myloop#1{%%
\@ae@myloop#1,\@nil
}

\def\listoffruit#1#2{%%
\setcounter{item@count}{0}%%
\noindent
List of type \texttt{#1}
\begin{itemize}[topsep=4pt,itemsep=2pt]
\csname#1@myloop\endcsname{#2}%%
\end{itemize}
Total number of bulleted items: \arabic{item@count}%
\par \vspace{2ex}\hrule\par \vspace{2ex}
}

\ExplSyntaxOn
\NewDocumentCommand{\expl@myloop}{ m }
{
\clist_map_inline:nn{#1}{\current@item {##1}}
}
\ExplSyntaxOff

\makeatother

\begin{document}

\listoffruit{etoolbox}{apples,, oranges, bananas ,cherries}

\listoffruit{@for}{apples,, oranges, bananas ,cherries}

\listoffruit{pgffor}{apples,, oranges, bananas ,cherries}

\listoffruit{ae}{apples,, oranges, bananas ,cherries}

\listoffruit{expl}{apples,, oranges, bananas ,cherries}

\end{document}


I'd add another expl3 loop, that also considers empty items:

\ExplSyntaxOn
\NewDocumentCommand{\seq@myloop}{ m }
{
\seq_set_split:Nnn \l_tmpa_seq { , } { #1 }
\seq_map_inline:Nn \l_tmpa_seq { \current@item {##1} }
}
\ExplSyntaxOff


(of course, using \l_tmpa_seq is discouraged, better allocate a new variable).

The comparison should leave no doubt. The only place where \foreach is superior is in its treatment of “incomplete lists”, such as 1,2,...,20.

• The only reason my \forcsvlist is as complicated as presented is because I frequently pass my comma separated lists via macros. This is not necessarily an issue with pgffor's \foreach. It would be an issue with my homebrew example. – A.Ellett Oct 27 '13 at 16:40
• @A.Ellett In my opinion, the macros for dealing with explicit and implicit (given via a macro) lists should be different: you can get wrong result if an explicit list starts with a macro which will be expanded too early. – egreg Oct 27 '13 at 16:43
• I am aware of that issue. Do you have suggestions for a better approach? Or are you suggesting that's an entirely different question? Actually, this who question arose because I had a list saved in a macro and I wanted to use etoolbox's \forlistloop. But apparently the <listmacro> expected needs to be built internally which didn't work for me because of how I was generating the list. – A.Ellett Oct 27 '13 at 16:48
• Incidentally, I like your idea for illustrating what was passed via \detokenize. – A.Ellett Oct 27 '13 at 16:49
• @A.Ellett For expandable loops, if your list can be gathered before in a clist variable, you have \clist_map_function:NN which is expandable. I'm sure there's code around for expandably removing leading and trailing space, but I don't know how robust they are (if Bruno's not able to do it, …) – egreg Oct 27 '13 at 17:26

LuaLaTeX:

\documentclass{standalone}
\usepackage{tikz}
\usepackage{filecontents}
\begin{document}
\newcommand{\drawCircle}[1]{\tikz \draw (0,0) circle (#1);}
\begin{filecontents*}{testlua.lua}
for i=1,10 do
tex.print("\\drawCircle{",0.1*i,"}")
end
\end{filecontents*}
\directlua{dofile('testlua.lua')}
\end{document}

• Seems like this is rather an answer but another method worth to be mentioned in the question ;-) – Tobi Oct 27 '13 at 16:19
• To make this an "Answer" you should add discussion of how it compares with the other methods. – Andrew Swann Oct 27 '13 at 18:59