你的这种写法确实会带来一点性能提升。这是由于 TeX 进行了更少的展开和内部操作。哪怕是在 \else、\expandafter 前增加一个 \relax 都会增加编译时间。
\documentclass{article}
\begin{document}
\long\def\loop#1\repeat{\def\iterate{#1\relax\expandafter\iterate\fi}\iterate \let\iterate\relax}
\long\def\xunhuan#1\repeat{\def\iterate{#1\else\let\iterate\relax \fi\iterate}\iterate}
%\long\def\xunhuan#1\repeat{\def\iterate{#1\relax\else\let\iterate\relax \fi\iterate}\iterate \let\iterate\relax}
\count5656=0
\count5657=300000000
%\xunhuan \advance\count5656by1 \ifnum \count5656<\count5657 \relax \repeat
%\the\count5656
\count5656=0
\loop \advance\count5656by1 \ifnum \count5656<\count5657 \relax \repeat
\the\count5656
\end{document}(谨慎运行!)
这种最简单的循环,在我的电脑上,使用 \loop 大概 61.7s,使用 \xunhuan 大概 57.4s,\loop 大致慢了 6%-8% 左右,但是当需要大量的计算时,用在 \loop 中的时间几乎可以忽略不计,实际区别很小。
但是你的代码与原来的 loop 并不是完全等价的,这里暂时先不说。
只要循环中不保存内容(不修改内部的 hash 表)、不留下 typeset material、不输出信息(log 等),仅包含(有效的)单纯的计算(比如 \advance),(可能所列并不完整,)TeX 不会在循环中消耗一丁点内存,因此理论上这样的“循环”的循环次数是无限的。这与其它编程语言是不同的。
诸如 C 之类的语言,在函数执行时会为其开辟新的内存空间,因此如果在循环时内存不回收,则可能会溢出,
然而在递归时,想要回收这些内存是很难的。
但是 TeX 不一样,TeX 在执行时,只是展开这些宏,
\loop \advance\count5656by1 \ifnum \count5656<\count5657 \repeat展开,变成了
\def\iterate{\advance\count5656by1 \ifnum \count5656<\count5657
\relax\expandafter\iterate\fi}\iterate
\let \iterate \relax定义 \iterate,再展开 \iterate,此时 \let\iterate\relax 还未执行:
\advance\count5656by1 \ifnum \count5656<\count5657 \relax\expandafter\iterate\fi
\let\iterate\relaxcount 寄存器 5656 加 1,即使是 \ifnum 也是执行展开,假设判断为真,则要么向后找到一个 \else(并不必须是 \else,任何一个被 \let 为 primitive \else 的都可以,要么向后找到一个 \fi(同样不必是 \fi),这一点必须要注意,与 \def 中的参数定界符不同。
这里没有发现 \else,TeX 先展开 \fi,然后将 \iterate 放在即将执行的输出流中。继续递归。
如果判断为假,TeX 不展开 \relax\expandafter\iterate\fi,而是直接找 \else 或 \fi(不必是 \else、\fi)。而此时 \relax\expandafter\iterate\fi 四者完全可能是 \else、\fi 二者之一。如果 \relax\expandafter\iterate 均不是 \else\fi 二者之一,且 \fi 是 primitive \fi,那么 TeX 正确的结束此次循环,并
\let\iterate\relax否则,在预想情况下,应该出错(报错)。因为重定义 \iterate 是不被允许的。
可以看到,在此循环中并不涉及内存分配,因此并不会出现爆栈或 overflow。
而若包含 typeset material 等内容,当一个段落的内容过多,或待输出的 typeset material 过多,或在循环过程中在 hash 表中增加了巨量的内容,则会出现内存用光的情况。这是这两种循环都会出现的。
考虑如下代码:
\documentclass{article}
\begin{document}
\long\def\xunhuan#1\repeat{\def\iterate{#1\else\let\iterate\relax \fi\iterate}\iterate}
\count5656=0
\count5657=3 %00000000
\loop \advance\count5656by1 \let\iterate\fi \ifnum \count5656>\count5657 \repeat
\end{document}编译报错了,原因正是如上所说的 TeX 并不需要 \fi 是 \fi,任何被 \let 为 primitive \fi 的都可以。但是换成你的 \xunhuan 则不会报错。
至于哪个更好,可能因人而异。









问 loop 改进循环:嵌套 一》首尾相连接