嵌套 \loop 宏

嵌套 \loop 宏

请问 Knuth 最喜欢的循环宏的“可嵌套”变体是否有\loop括号范围,而答案中没有引入括号范围嵌套循环我正在开发一个名为循环,我在其中收集许多循环(包括一个新的\foreach和一些奇怪的循环宏)。

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

答案1

我最初的例子(我在下面留下)不允许嵌套相同的循环宏,但允许声明可以嵌套在另一个宏中的(结构相同的)宏。

这是基于相同想法的新版本,但实际上编码更简单,并且允许嵌套循环,而无需预先声明不同的循环名称。的语法\nloop与普通 TeX 相同,\loop只是\nloop\repeat必须分别跟有一个标识标记,因此它们可以在嵌套情况下匹配。

该定义几乎不比下面的版本更复杂\loop,也不比下面的版本更简单

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

以及在问题中的测试用例上使用此定义的完整测试文档。在此示例中,我使用标记\a\b来标识两个循环,但和\a\b任意的,不需要定义命令(例如,您可以使用1和)。2

\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
  \advance\@tempcnta by1
  \ifnum\@tempcnta<3\relax
  \@tempcntb0
  \nloop\b
    \advance\@tempcntb by1
    \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声明版本:

这不允许\loop嵌套,但它允许您定义具有基本相同定义的其他宏,因此您可以\loopb在其中嵌套并在需要时\loop制作:\loopc


(/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
  \advance\@tempcnta by1
  \ifnum\@tempcnta<3\relax
  \@tempcntb0
  \loopb
    \advance\@tempcntb by1
    \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}

答案2

这取自Kees van der Laan(TUGboat 14,第 3 期,1993 年,第 310-318 页)我用 Plain TeX 编写

\newcount\cnta
\newcount\cntb

\def\rows{%
  \advance\cnta by 1
  % 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{%
  \advance\cntb by 1
  % 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

输出为

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

并且,由于没有团体参与,定义可以有效执行。

当然\loop不能与其通常的定义嵌套,因为它是一个宏,其参数由以下分隔符分隔\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

在 LaTeX 中略有不同,但限制是一样的:

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

这一个取自Paweł Jackowski(TUGboat 29,第 2 期,2008 年,第 320-323 页)并需要重新定义\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{
  \advance\cnta by1
  \ifnum\cnta<3\relax
  \cntb0
  \loop
    \advance\cntb by1
    \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

输出如下:

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

答案3

在我费尽心思寻找另一个解决方案之前(如果仍然有必要或有趣的话),这里是大卫卡莱尔的涂有巧克力和枣的句法蛋糕。

\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
  \advance\row by1
  \ifnum\row<3\relax
  \column0
  \loopb
    \advance\column by1
    \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} 

这给出

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

编辑

以下是我认为可以构建的解决方案。请注意,在示例中,和\loop都是\repeat嵌套的,并且名称没有任何变化。我们有

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

代码如下:

\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{%
      \advance\amloop@cnt\@ne
      \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}%
          \advance\amloop@cnt\m@ne
          \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
      \cpt@err{'\string\repeat' not found: endless loop}\@ehd
    }{%
      \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
  \advance\row by1\relax
  \ifnum\row<3\relax
  \column0\relax
  \let\rom\romannumeral
  \loop
    \advance\column by1\relax
    \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}

这给出

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

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

更多测试:

\cptnewcounts{rowcnt,colcnt,maxrow,maxcol}
\maxrow4 \maxcol3
\def\entries{}
\let\loop\amloop
\def\generateentries{%
  \loop
    \advance\rowcnt1\relax
    \colcnt0\relax
    \loop
      \advance\colcnt1\relax
      \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} 

相关内容