# Nested \loop macro

Please are there 'nestable' variants of Knuth's favourite looping macro \loop without the brace-scope introduced in the answer at nested loops. I am developing a package called loops, where I am collecting many loops (including a new \foreach and some weird looping macros).

\@tempcnta0 % LaTeX
\loop
\ifnum\@tempcnta<3\relax
\@tempcntb0
\loop
\ifnum\@tempcntb<4\relax
\@namedef{w@\romannumeral\@tempcnta @\romannumeral\@tempcntb}{x}%
\repeat
\repeat

-

My initial example, which I leave below did not allow the same loop macro to be nested but did allow declaration of (structurally identical) macros which could be nested one inside the other.

Here is a new version based on the same idea, but actually the coding is simpler and it does allow nesting of loops without having to pre-declare different loop names. The syntax of \nloop is as for plain TeX \loop except that \nloop and \repeat must each be followed by an identifying token, so they may be matched in nested cases.

The definition is hardly any more complicated than that of \loop and simpler than the version below

\def\nloop#1{%
\def\nl@@p##1##2\repeat#1{%
\def##1{##2\relax\expandafter##1\fi}%
##1\let##1\relax}%
\expandafter\nl@@p\csname nl@@p-\string#1\endcsname
}


and a full test document using this definition on the test case in the question. In this example I use the tokens \a and \b to identify the two loops, but the \a and \b are arbitrary, and don't need to be defined commands (you could use 1 and 2 for example).

\documentclass{article}

\begin{document}

\makeatletter

\def\nloop#1{%
\def\nl@@p##1##2\repeat#1{%
\def##1{##2\relax\expandafter##1\fi}%
##1\let##1\relax}%
\expandafter\nl@@p\csname nl@@p-\string#1\endcsname
}

\@tempcnta0 % LaTeX
\nloop\a
\ifnum\@tempcnta<3\relax
\@tempcntb0
\nloop\b
\ifnum\@tempcntb<4\relax
\typeout{defining \expandafter\string\csname
w@\romannumeral\@tempcnta @\romannumeral\@tempcntb\endcsname}
\@namedef{w@\romannumeral\@tempcnta @\romannumeral\@tempcntb}{x}%
\repeat\b
\repeat\a

\end{document}


\newloop declaration version:

This doesn't let \loop be nested but it does let you define other macros that have essentially identical definitions, so you can nest \loopb inside a \loop and can make a \loopc if need be:

(/usr/share/texmf-dist/tex/latex/base/size10.clo)) (./lp.aux)
defining \w@i@i
defining \w@i@ii
defining \w@i@iii
defining \w@ii@i
defining \w@ii@ii
defining \w@ii@iii
(./lp.aux) )


\documentclass{article}

\begin{document}

\makeatletter

%\def\loop#1\repeat{\def\iterate{#1\relax\expandafter\iterate\fi}%
%  \iterate \let\iterate\relax}

\def\nloopx#1#2#3#4#5{repeat}
\def\nloopy#1#2#3#4#5{iterate}
\def\nloopz#1#2#3{%
\let#2\fi
\def#1##1#2{\def#3{##1\relax\expandafter#3\fi}#3\let#3\relax}}

\def\newloop#1{%
\expandafter\def\expandafter\@tempa\expandafter{%
\expandafter#1%
\csname\expandafter\nloopx\string#1\expandafter\endcsname
\csname\expandafter\nloopy\string#1\endcsname}%
\expandafter\nloopz\@tempa}

\newloop\loopb

\@tempcnta0 % LaTeX
\loop
\ifnum\@tempcnta<3\relax
\@tempcntb0
\loopb
\ifnum\@tempcntb<4\relax
\typeout{defining \expandafter\string\csname
w@\romannumeral\@tempcnta @\romannumeral\@tempcntb\endcsname}
\@namedef{w@\romannumeral\@tempcnta @\romannumeral\@tempcntb}{x}%
\repeatb
\repeat

\end{document}

-
The second solution, which now appears on top, is also DC-like. The minor difference is that it redefines inner loops every time the outer loop is fired. –  Ahmed Musa Jun 4 '12 at 0:04

This is taken from Kees van der Laan (TUGboat 14, n. 3, 1993, p. 310–318). I write it in Plain TeX

\newcount\cnta
\newcount\cntb

\def\rows{%
% Here the code we want to be executed at every outer cycle
\cntb0 \cols
%%%
\ifnum\cnta=2 \swor\fi % Two outer cycles
\rows}
\def\swor#1\rows{\fi}
\def\cols{%
% Here the code we want to be executed at every inner cycle
\immediate\write16{Defining \string\w@\romannumeral\cnta @\romannumeral\cntb}%
\expandafter\def\csname w@\romannumeral\cnta @\romannumeral\cntb\endcsname{x}%
%%%
\ifnum\cntb=3 \sloc\fi % Three inner cycles
\cols}
\def\sloc#1\cols{\fi}

\rows

\bye


The output is

Defining \w@i@i
Defining \w@i@ii
Defining \w@i@iii
Defining \w@ii@i
Defining \w@ii@ii
Defining \w@ii@iii


and, since there's no group involved, the definitions are effectively performed.

Of course \loop can't be nested with its usual definition, because it's a macro with its argument delimited by \repeat:

\def\loop#1\repeat{\def\body{#1}\iterate}
\def\iterate{\body \let\next\iterate \else\let\next\relax\fi \next}
\let\repeat=\fi % this makes \loop...\if...\repeat skippable


In LaTeX it's slightly different, but the limitation is the same:

\def\loop#1\repeat{\def\iterate{#1\relax\expandafter\iterate\fi}%
\iterate \let\iterate\relax}


This one is taken instead from Paweł Jackowski (TUGboat 29, n. 2, 2008, p. 320–323) and requires a redefinition of \loop:

\long\def\loop#1\repeat{%
\iterate\gobbleone{#1}}
\long\def\iterate\gobbleone#1{%
#1\expandafter\iterate\fi
\gobbleone{#1}}
\long\def\gobbleone#1{}

\newcount\cnta
\newcount\cntb

\loop{
\ifnum\cnta<3\relax
\cntb0
\loop
\ifnum\cntb<4\relax
\immediate\write16{Defining \string\w@\romannumeral\cnta @\romannumeral\cntb}%
\expandafter\def\csname w@\romannumeral\cnta @\romannumeral\cntb\endcsname{x}%
\repeat
}\repeat

\immediate\write16{\expandafter\meaning\csname w@ii@iii\endcsname}

\bye


Here's the output:

Defining \w@i@i
Defining \w@i@ii
Defining \w@i@iii
Defining \w@ii@i
Defining \w@ii@ii
Defining \w@ii@iii
macro:->x

-
BTW, thanks for leading us to Kees van der Laan. I tasted some syntactic honey there. Now, Paweł Jackowski's scheme too requires brace-scoping and so it isn't really a black box. The user has to know and remember where to place the scope, to initially hide the inner loop from the scanner. –  Ahmed Musa May 31 '12 at 23:52

Before I labour over another solution (if that remains necessary or interesting), here is David Carlisle's syntactic cake coated with chocolate and dates.

\documentclass{article}
\makeatletter
\def\declarenewloop#1{%
\begingroup
\def\newloopa##1{repeat}%
\def\newloopb##1{iterate@}%
\def\newloopc##1##2##3{\endgroup
\ifx##1\@undefined\else
\edef\reserved@a{\expandafter\@gobble\string##1}%
\@notdefinable
\fi
\let##2\fi
\def##1####1##2{%
\def##3{####1\relax\expandafter##3\fi}%
##3\let##3\relax
}%
}%
\def\x##1##2{\expandafter\noexpand\csname\expandafter##1\string##2\endcsname}%
\edef\x{\noexpand#1\x\newloopa#1\x\newloopb#1}%
\expandafter\newloopc\x
}

% Tests:
\declarenewloop\loopb

\newcount\row\newcount\column
\row0 \column0
\def\stack{}
\loop
\ifnum\row<3\relax
\column0
\loopb
\ifnum\column<4\relax
\let\rom\romannumeral
\typeout{Including numbers \rom\row, \rom\column}%
\edef\stack{%
\unexpanded\expandafter{\stack}\noexpand\\{\rom\row}{\rom\column}%
}%
\repeatloopb
\repeat
\makeatother

\begin{document}
x
\end{document}


This gives

Including numbers i, i
Including numbers i, ii
Including numbers i, iii
Including numbers ii, i
Including numbers ii, ii
Including numbers ii, iii


EDIT

Here is the solution I thought I could build. Notice, in the example, that both \loop and \repeat are nested without any change of their names. We have

\loop
...
\loop
***
\repeat
\repeat


The code follows:

\documentclass{article}
\usepackage{catoptions}
\makeatletter
\newcount\amloop@cnt
\newtoks\amloop@toks
\new@def*\this@ghost{\@gobble\this@ghost}
\new@def\amloop@ifcmdeq#1{\expandafter\ifcseqTF\cpt@car#1\this@ghost\car@nil}
\new@def\amloop@orig#1\repeat{\amloop@origb\@gobble{#1}}
\let\repeat\fi
\new@def\amloop@origb\@gobble#1{%
#1\relax\expandafter\amloop@origb\fi\@gobble{#1}%
}
\amloop@toks{\amloop@orig}
\new@def\amloop@iiofii#1#2{#1#2}
\new@def\amloop@ifflochtype#1{%
\csname @\if0\pdfstrcmp{\cptoxdetok{\amloop@iiofii#1{}{}}}%
{\detokenize{#1{}}}first\else second\fi oftwo\endcsname
}
\robust@def*\am@defnewloop{%
\begingroup
\def\def@loop##1##2##3{\endgroup
\def\name@cmd{##1}\def\repeat@cmd{##2}%
\let##2\fi
\def##1####1##2{%
\def##3{####1\relax\expandafter##3\fi}%
##3\let##3\relax
}%
}%
\def\x##1{\noexpandcsn{##1\romannumeral\amloop@cnt}}%
\edef\x{\x{amloop@}\x{amrepeat@}\x{amiterate@}}%
\expandafter\def@loop\x
}
\robust@def*\amloop{%
\edef\amloop@restoreeof{\everyeof{\the\everyeof}}%
\everyeof{EOF}\amloop@cnt\z@
\amloop@pushloop
}
\robust@def\amloop@pushloop{\futurelet\next\amloop@pushloopb}
\robust@def\amloop@pushloopb#1{%
\amloop@ifflochtype{#1}{%
\amloop@ifcmdeq{#1}\amloop{%
\am@defnewloop
\amloop@toks\expandafter{\the\expandafter\amloop@toks\name@cmd}%
\amloop@pushloop
}{%
\ifstrcmpTF{#1}\repeat{%
\ifnumcmpTF\amloop@cnt=\z@{%
\amloop@restoreeof
\amloop@toks\expandafter{\expandafter\amloop@orig
\expandafter}\the\amloop@toks\repeat
}{%
\amloop@toks\expandafter{\the\expandafter\amloop@toks\repeat@cmd}%
\amloop@pushloop
}%
}{%
\ifcseqTF\next\@sptoken{%
\amloop@toks\expandafter{\the\expandafter\amloop@toks\space#1}%
}{%
\amloop@toks\expandafter{\the\amloop@toks#1}%
}%
\amloop@pushloop
}%
}%
}{%
\ifstrcmpTF{#1}{EOF}{%
\amloop@restoreeof
}{%
\ifcseqTF\next\@sptoken{%
\amloop@toks\expandafter{\the\expandafter\amloop@toks\space{#1}}%
}{%
\amloop@toks\expandafter{\the\amloop@toks{#1}}%
}%
\amloop@pushloop
}%
}%
}

% Tests
\newcount\row\newcount\column
\row0 \column0
\def\stack{}
\let\loop\amloop
\loop
\ifnum\row<3\relax
\column0\relax
\let\rom\romannumeral
\loop
\ifnum\column<4\relax
\typeout{Including numbers \rom\row, \rom\column}%
\edef\stack{\expandcsonce{\stack}\noexpand\\{\rom\row}{\rom\column}}%
\repeat
\typeout{Including row \rom\row}%
\edef\stack{\expandcsonce{\stack}\noexpand\do{\rom\row}}%
\repeat
%\show\stack
\makeatother

\begin{document}
x
\end{document}


This gives

Including numbers i, i
Including numbers i, ii
Including numbers i, iii
Including row i
Including numbers ii, i
Including numbers ii, ii
Including numbers ii, iii
Including row ii


with

\stack=macro:
->\\{i}{i}\\{i}{ii}\\{i}{iii}\do {i}\\{ii}{i}\\{ii}{ii}\\{ii}{iii}\do {ii}


More test:

\cptnewcounts{rowcnt,colcnt,maxrow,maxcol}
\maxrow4 \maxcol3
\def\entries{}
\let\loop\amloop
\def\generateentries{%
\loop
\colcnt0\relax
\loop
\edef\entries{\expandcsonce\entries\the\numexpr\rowcnt*\colcnt\relax}%
\ifnum\colcnt<\numexpr\maxcol+1\relax\relax
\edef\entries{\expandcsonce\entries\noexpand&}%
\repeat
\ifnum\rowcnt<\numexpr\maxrow+1\relax\relax
\edef\entries{\expandcsonce\entries\noexpand\cr}%
\repeat
\edef\entries{\expandcsonce\entries\noexpand\crcr}%
}
\generateentries

\begin{document}
\vbox{\halign{&\ \hfil#\hfil\strut\cr\entries}}
\end{document}

-