按如下方式定义一个宏(#2后面再跟一个#):
\def\foo#1#2#{*#1,#2*}经测试,foo会将其接收的第一个参数(#1)后,紧接着遇到的第一个左花括号“{”前的内容作为#2,比如,
然后按如下方式赋值:
\foo 1 2 {} % 会得到 *1,2*
\foo 1{2} % 会得到 *1,*2请问为何会如此,如何理解?
这个就是 TeX 中的一个特殊规则(见 TeXbook 204 页),在早些年代,计算机内存较小,这种定义方式可以节省一些字节量。
现在定义宏不推荐这种写法,能看明白就行。
用 pdftex 跑下面的代码:
\tracingmacros=1
\def\foo#1#2#{*#1,#2*}
\foo 1 2{}
\foo 1 {2}
\bye在 log 文件中会看到:
\foo #1#2{->*#1,#2*{
#1<-1
#2<- 2
\foo #1#2{->*#1,#2*{
#1<-1
#2<- %%%注意这里有一个空格,自己测试一下对于 \foo 1 2{},\foo 接收的第一个参数是 1,第二个参数为第一个参数与左花括号之间的所有 token,也就是空格符和 2;而对于 \foo 1 {2},第一个参数还是 1,而第二个参数只剩下一个空格符;再改一下,如果删掉 1 后面的空格符的话(即 \foo 1{2}),第二个参数就是空的。
我们可以通过这个特性定义带可选参数的宏,看看从 xcolor 里面节选的一段代码:
\documentclass{article}
\usepackage{xcolor}
\makeatletter
\def\testclr#1#{\@testclr{#1}}
\def\@testclr#1#2{{\fboxsep\z@\fbox{\colorbox#1{#2}{\phantom{XX}}}}}
\makeatother
\begin{document}
\testclr{red}
\testclr[rgb]{1,0,1}
\end{document}\testclr 只有一个参数,且这个参数位于宏本身与左花括号之间,所以
\testclr{red},#1 为空,于是传给 \@testclr 的第一个参数为空,第二个参数为 red\testclr[rgb]{1,0,0},第一个参数为 [rgb],第二个参数为 1,0,0,于是传给 \@testclr 的两个参数也一样
\foo 1 2 {} 得到的是 *1, 2 *,而不是 *1,2*。
TeX 宏的参数有两种,一种是没有分隔符的参数(或定界符,undelimited parameter),另一种是有分隔符的参数(delimited parameter)。参数的分隔符就是两个参数之间的记号,是标记参数结束的一些符号。
TeX 在开始读取 undelimited parameter 时,会忽略空格,之后如果发现第一个记号是 begin-group character,则认为这个参数是由一对括号包裹(可以认为 { 标记参数的开始位置,} 标记结束位置,它们不会在最终的参数里出现),否则这个参数就是遇到的第一个非空格记号。
读取 delimited parameter 时,不会忽略空格,这个参数由指定的定界符来标记结束的位置(定界符必须与定义时的完全匹配),并且 {} 需要正确嵌套,并且如果结果是一对正确嵌套的组,则最外层的括号不会出现在实参中。在这两种情况中,{ 可以是任意 catcode 为 1 的字符(即 begin-group character),} 可以是任意 catcode 为 2 的字符(即 end-group character)。
macro parameter(catcode=6 的字符或被 let 为这些字符的控制序列,一般是 #) + begin-group character 可以看成是一个特殊的定界符,只能放在最末尾。在匹配时,begin-group character 必须和定义时使用的字符完全一致。
如假定 [ 和 { 的 catcode 都为 1,则 \def\foo#1#[...} 在使用时也必须是 \foo aaa[...,不能是 \foo aaa{...。
了解这些之后,答案就很清楚了。\def\foo#1#2#{...},第一个参数是 undelimited parameter,第二个参数由 { 定界,#1 是 1,#2 分别是 2 和空。
需要注意的是,TeX 在读取一个控制词(如 \a、\foo 是控制词,\; 是控制符,它们都是控制序列)会忽略它后面的空格,所以 \foo␣␣ 等于 \foo␣ 等于 \foo。如前所述,macro parameter 可以是字符,也可以是控制序列,如
\let\pp#
\def\foo#1\pp2{\pp1,#2}
\def\foo#1#2{#1,#2}两种完全一致。
空格只比较 catcode,不比较 charcode,TeX 在读取输入文本时,所有的 catcode=10 的字符都会变成 charcode=32, catcode=10 的记号。
更详细的内容可以参考 The TeXBook 第 20 章。
好的,谢谢大佬解惑!
我原本是在
LaTeX3的l3prg模块对函数\prg_set_conditional:Npnn实现的底层代码中看到这样的定义:第一次见这种定义方法,但又不符合“定界符参数”的用法,觉得新奇,现在看来,确实是有特殊用途啊。