我注意到 TeX 并不总是将宏的文本保存为输入的内容。例如,以下纯 TeX 手稿:
\def\a#1{\relax#1}%
\font\myfont=cmtt12\myfont%
\meaning\a%
\bye
产生输出
宏:#1->\relax #1
请注意,输出显示“\relax”和“#1”之间有一个空格,而手稿中没有这个空格。这种差异会对手稿的执行产生巨大影响。
例如,请考虑下面的手稿。
\def\a#1{\def\b#1{Hello, world!}}%
\a c%
\bc%
\bye
如果\a
的文本按原样保存,则此手稿将成功编译,第二行将定义宏\bc
,第三行将输出:Hello, world!
。相反,编译会中止并显示以下错误:
! Undefined control sequence.
l.3 \bc
因此有必要知道:宏文本在保存到内存之前究竟经历了哪些转换?
答案1
基本上:TeX 存储的不是文本(字符序列),而是标记:\def
它会记住的定义\a
具有控制序列\relax
,后跟宏参数 1,当您要求打印此标记列表时,它节目\relax
为了清楚起见,请在标记后添加一个空格。
我不确定“用户”级别的描述是否正确,TeXbook等等,但我可以指出“内部”表示,如TeX:程序。请参阅 的第 20 部分(标记列表)texdoc tex
。标记可以是一(command code, character)
对,也可以是控制序列。根据以下背景:
- 你输入的内容
\def
将被存储为一系列标记:
- 然后,当您要求
\mac
显示定义时,上述标记列表将显示为:
(在这个例子中,文本恰好是相同的,但请注意,您可以\b
在输入文本中省略后面的空格,并且内部标记列表表示将是相同的,因此\b
如果您要求打印它,后面仍然会有一个空格。)
答案2
代码
\def\a#1{\def\b#1{Hello, world!}}%
\a c%
\bc%
用一个参数定义\a
;调用时\a c
,#1
变为c
,因此执行的是
\def•\b•c•{•H•e•l•l•o•,• •w•o•r•l•d•!•}
它\b
用参数文本c
和替换文本进行定义Hello, world!
。(•
这里用来分隔标记,以便更清晰。)
如果你这样做\show\a
,你会得到
> \a=macro:
#1->\def \b #1{Hello, world!}.
它使用了不同的表示形式(控制字后有一个空格),但这完全等同于同样的事情:\b
已经被标记化。如果你想定义\bc
,你需要
\def\a#1{\expandafter\def\csname b#1\endcsname{Hello, world!}}
现在\a c
将定义\bc
有替换文本Hello, world!
。
如前所述\show
,\meaning
表示法在控制字后添加了一个空格,如果您复制并粘贴显示的代码,则这无关紧要,因为该位置的空格会被忽略。这样您就可以看划分为 token。当然,如果做了一些奇怪的事情,可能会被误导:
\catcode`z=12
\def\a#1{\def\z#1{Hello, world!}}
\show\a
将显示
> \a=macro:
#1->\def \z#1{Hello, world!}.
但\a c
会不是定义一个宏\zc
,因为#1
仍然是一个单独的参数标记。
不\def
在转换过程中,除了标记化(顺便说一下,它修复了类别代码)之外,对标记进行的任何转换都不会进行。
构成宏名称的字符的类别代码不会以任何方式记录。但是,TeX 只会在控制字后而不是控制符号后在\show
/表示中添加空格。因此,如果您添加\meaning
\catcode`z=11
\show\a
对于上面的代码,第二个\show
将输出
> \a=macro:
#1->\def \z #1{Hello, world!}.
因为此时,\z
应被视为控制字。这更多地与来自文件的输入有关.tex
,而不是与标记有关:始终记住,已存储的标记不会受到类别代码更改的影响,但输入的标记会受到影响。
现实世界中的例子是 LaTeX 宏\@
。在 的类别代码为 11 的代码中@
(通常用于定义“私有”宏),\@
仍然具有与 相同的定义@
类别代码为 12 时相同。但是,当使用 \@
在代码中,因为
\@x
在正常情况下(的 catcode 为 12)被读入为两个标记@
,但是当的 catcode 为@
11 时,只读入一个标记。这不是什么大问题,但需要注意。