你的这种写法确实会带来一点性能提升。这是由于 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\relax
count 寄存器 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 改进循环:嵌套 一》首尾相连接