1, p219]:
\def\length#1{{\count0=0 \getlength#1\end \number\count0}} \def\getlength#1{\ifx#1\end \let\next=\relax \else\advance\count0 by1 \let\next=\getlength\fi \next}首先从代码的执行过程体会一下整体思路:
*\tracingmacros=1 *\tracingonline=1 *\def\length#1{{\count0=0 \getlength#1\end \number\count0}} *\def\getlength#1{\ifx#1\end \let\next=\relax \else\advance\count0 by1 \let\next=\getlength\fi \next} *\length{name} \length #1->{\count 0=0 \getlength #1\end \number \count 0} #1<-name \getlength #1->\ifx #1\end \let \next =\relax \else \advance \count 0 by1 \let \next =\getlength \fi \next #1<-n \next #1->\ifx #1\end \let \next =\relax \else \advance \count 0 by1 \let \next =\getlength \fi \next #1<-a \next #1->\ifx #1\end \let \next =\relax \else \advance \count 0 by1 \let \next =\getlength \fi \next #1<-m \next #1->\ifx #1\end \let \next =\relax \else \advance \count 0 by1 \let \next =\getlength \fi \next #1<-e \next #1->\ifx #1\end \let \next =\relax \else \advance \count 0 by1 \let \next =\getlength \fi \next #1<-\end仔细解读这段代码,我们可以学到很多。首先看一下\length的定义,借用count0来统计字符串的长度,这很容易理解。但是要注意\getlength#1\end的调用,这是本段代码的关键点之一。getlength宏的参数为#1\end,比如字符串为”name”,则getlength的参数为:name\end,即5个token,其中的\end是一个宏,但是由于参数中的宏不会展开,因此这里的\end不需要定义,我们只是拿来表示字符串的结束(getlength参数的结束)而已。\getlength#1\end会首先将#1中的第一个字母作为参数,这里即为将n作为getlength的参数。处理完第一个字母,下一步会处理第二个字母,直到遇到\end为止。
如果\getlength name\end只执行一次,那么会输出\getlength n的结果,nme\end会原样输出(实际上,由于\end没有定义,如果\getlength name\end只执行一次,会报告\end没有定义的错误)。因此,\getlength宏巧妙的实现了一个看起来像是递归的过程:
- 首先将\getlength的参数和\end比较,如果相同则表示字符串处理完毕,将\next设置为\relax。注意到,整个\getlength宏的可执行部分只有\next宏。
- 如果\getlength的参数不是\end,则将\count0增加1,并且设置\next宏为\getlength:这样就实现了递归调用,因为\getlength的可执行代码只有\next,而\next是重新执行\getlength宏,即读入token序列的下一个字母作为参数继续处理。
注意到,\getlength name\end的执行过程是,TeX首先将这个命令及其参数切分为token序列:\getlength、␣、n、a、m、e、\end,然后从这个token序列中依次读出token进行处理。
发表评论 取消回复