TeX

10 用形如\def\foo#1#2#{*#1,#2*}这样的代码定义宏时,如何理解参数检索?

发布于 2023-11-26 16:16:06

按如下方式定义一个宏(#2后面再跟一个#):

\def\foo#1#2#{*#1,#2*}

经测试,foo会将其接收的第一个参数(#1)后,紧接着遇到的第一个左花括号“{”前的内容作为#2,比如,
然后按如下方式赋值:

\foo 1 2 {} % 会得到 *1,2*
\foo 1{2}   % 会得到 *1,*2

请问为何会如此,如何理解?

查看更多

关注者
0
被浏览
759
Swit
Swit 2023-11-26
LaTeX nubility!

这个就是 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 的两个参数也一样

image.png

2 个回答
雾月
雾月 2023-11-26
这家伙很懒,什么也没写!

\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,第二个参数由 { 定界,#11#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 章。

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览