1 区别
def和edef都可以定义宏,其区别是:def定义宏时,其宏体不做展开操作;而edef定义宏时,其宏体需要进行展开操作,直到不能再展开为止。def定义的宏,在实际调用时才进行展开,因此宏定义时可以引用还没有定义的其他的宏;edef定义宏时,由于马上就进行展开操作,因此不能引用还没有定义的宏。无论def定义的宏还是edef定义的宏,在实际调用时都会进行“彻底”的展开操作。 既然宏最终还是要展开的,在定义时展开(edef)和在调用时展开(两种情况下都是)到底有什么区别呢?首先看一下def的常见用法(*为TeX提示符,删除了部分不影响理解的输出):*\tracingall *\def\b{b} *\def\c{\b} *\def\a{\b\c} *\a \a ->\b \c \b ->b \c ->\b \b ->b%输出bb *\def\b{foo} *\a \a ->\b \c \b ->foo \c ->\b \b ->foo%输出foofoo可见,宏\a 的定义是\b\c ,每次执行\a ,其内容会随\b 或者\c 的不同定义而发生变化。再观察一下edef的常见用法:
*\tracingall *\def\b{b} *\def\c{\b} *\edef\a{\b\c} {\edef} \b ->b \c ->\b \b ->b *\a \a ->bb%输出bb *\def\b{foo} *\a \a ->bb%输出bb可见,当使用edef定义宏\a 时,在定义时\b\c就展开成为了bb,即宏\a成为固定不变的字符串bb,不再随\b或者\c发生变化。
2 noexpand配合edef的用法
既然edef定义宏时就要展开,如何设置能够禁止某些特定的宏在edef中展开呢?办法有很多[1, p216],最常规和最简单的做法是使用\noexpand命令,比如:\def\a{a} \def\b{b} \def\c{c} \edef\foo{\a\noexpand\b\c}%\foo此时为a\b c \foo%a\b c继续展开为abc \def\b{bar} \foo%输出为:afooc可以看出,使用\noexpand 禁止某些宏展开,保证了edef所定义的宏具有一定的灵活性。在本例中,\foo 的输出内容还是可以跟随\b发生变化的。如果不使用\noexpand\b 的话,\foo 就成为固定不变的‘abc’了。
3 expandafter配合def的用法
和2中的情形相反,有的时候需要在def定义的宏中马上展开一个宏,怎么办?如果大量的宏需要展开,直接切换edef是良策;如果只有个别的宏需要展开,可以首先展开这个宏,然后把展开的宏“拼接”进去,如下所示:\def\a{a} \def\b{b} \def\c{c} \toks0=\expandafter{\b} \def\foo{\a\the\toks0\b\c}
4 权衡
使用def还是edef来定义宏,取决于宏定义时需要展开多少宏,以及为什么要展开宏。展开某些宏的目的是让宏的定义“固化”,防止在宏定义后“不经意”或者“有意”的修改(侵犯),提高了宏的安全性。5 edef和let
edef展开宏定义,let不展开宏定义,如下的指令执行序列可以证明\let不会展开:*\tracingall *\def\b{\c\d} *\def\c{c} *\def\d{d} *\let\a=\b *\a%调用宏\a,输出cd \a ->\c \d \c ->c \d ->d可以看出,调用\a时展开了\b\c从而输出“cd”。let操作时,\a仅仅复制了\b的内容(tokens),即\c\d,并没有展开\c\d。 TeX中似乎没有\redef命令,因此无法验证重新给\c或者\d赋值时的情形。
发表评论 取消回复