请问 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}