尝试理解 \newcommand 的定义(参数标记的复杂处理)

尝试理解 \newcommand 的定义(参数标记的复杂处理)

我仍在尝试了解 tex 究竟是如何解析文档的。这一次,我试图了解\newcommand和 朋友中的所有参数标记处理,这对我来说就像黑魔法一样 - 这里的关键似乎发生在\@yargdef和 中\@yargd@f,分别可以在第 868 行和第 877 行中找到latex.ltx。目的显然是创建最终将跟在 后面的参数字符串\def\foo,但我不明白这是如何工作的。

为了解释我认为我的问题出在哪里,我不得不稍微解释一下我的想法,不幸的是。所以:如果我这样做\newcommand\foo[2]{\bar},那么据我所知,这最终(在多次检查是否\foo 已经定义并检查可选参数等之后)扩展为\@argdef\foo[2]{\bar}

我将逐步讲解如何解读这些定义:

  • \@argdef定义如下:
    \long\def\@argdef#1[#2]#3{%
    \@ifdefinable #1{\@yargdef#1\@ne{#2}{#3}}}
    
    所以我们最终\@yargdef\foo\@ne{2}(离开{\bar})。
  • \@yargdef定义如下:
    \long \def \@yargdef #1#2#3{%
    \ifx#2\tw@
    \def\reserved@b##11{[####1]}%
    \else
    \let\reserved@b\@gobble
    \fi
    \expandafter
    \@yargd@f \expandafter{\number #3}#1%
    }
    
    它定义\reserved@b\@gobble ,最终扩展为\@yargd@f{2}\foo(离开{\bar})。
  • 现在\@yargd@f是最奇怪的部分。它的定义如下:
    \long \def \@yargd@f#1#2{%
    \def \reserved@a ##1#1##2##{%
    \expandafter\def\expandafter#2\reserved@b ##1#1%
    }%
    \l@ngrel@x \reserved@a 0##1##2##3##4##5##6##7##8##9###1%
    }
    
    因此,就我的情况而言,扩展应该是(如果我没记错的话?):
    \def\reserved@a #12#2#{%
    \expandafter\def\expandafter\foo\reserved@b #12%
    }%
    \l@ngrel@x \reserved@a 0#1#2#3#4#5#6#7#8#9#2
    
    因此在应用\reserved@a,第一个参数应该是0#1#,第二个参数应该是......空的,我猜,因为紧随其后的标记已经是#......?3#4#5#6#7#8#9#2{\bar}作为下一个标记保留?

这肯定是我的想法出了错,因为如果我假装之前的字符串{\bar} 不是在那里,剩下的就说得通了: \reserved@a然后扩展为 \expandafter\def\expandafter\foo\reserved@b 0#1#2-\reserved@b被定义为\@gobble只吃掉它找到的第一个标记,所以它的扩展应该是\def\foo#1#2- 接下来是{\bar}

所以显然我对参数字符串的解释是\reserved@a错误的?如果不是空的,那么它的应用中的第二个参数到底是什么\reserved@a?我能想到的唯一可能的其他解释是,它吞噬了下一个 #-token,但即便如此,它也只会吃掉部分#3并留下一整串参数标记……?

我是否扩大了定义0##1##2##3##4##5##6##7##8##9###1或者##1#1##2##定义了\reserved@a错误?

答案1

除了#{参数的作用之外,您的推理是正确的。

当 TeX在宏的#中看到 a 时(如),后面的标记可以是 1-9 范围内的数字,也可以是。当参数是“可怕的怪异参数”时,TeX 的行为就像前一个参数的分隔符是 a 一样(因为你不能在 中使用 a )。<parameter text>\def\macro<parameter text>{<replacement text>}{#{{{<parameter text>

在这个定义中(记住{\bar}仍然在输入流中):

\def\reserved@a #12#2#{%
\expandafter\def\expandafter\foo\reserved@b #12%
}%
\l@ngrel@x \reserved@a 0#1#2#3#4#5#6#7#8#9#2{\bar }

\reserved@a扩展时,#1将会是,正如你所说,,0#1#并且#2将是直到下一个的一切{,并且不是为空,所以,由中#3#4#5#6#7#8#9#2的 分隔。 的这个定义实际上抛弃了剩余的参数标记。因此,你“假装”在 之前的字符串{{\bar }\reserved@a{\bar} 不是那里”实际上是正确的。其余代码按您的假设执行。


引自TeXbook,关于#{参数:

允许对这些规则进行特殊扩展:如果 的最后一个字符<parameter text>是 #,因此 #紧接着 {,TeX 将表现得好像 {已插入参数文本和替换文本的右端。例如,如果您说 ' \def\a#1#{\hbox to #1}',则后续文本 ' \a3pt{x}' 将扩展为 ' \hbox to 3pt{x}',因为 的参数\a由左括号分隔。

答案2

我已经在我的 LaTeX 编程书中处理过这个问题了。我的例子略有不同,但更容易复制。;-) 假设我们有

\newcommand{\xyz}[2]{ab#1cd#2ef}

这变成

\@star@or@long\new@command[2]{ab#1cd#2ef}

由于没有*after \new@command,因此 this 确实存在\let\l@ngrel@x=\long\@star@or@long消失了。现在\new@command被扩展,产生

\@testopt{\@newcommand\xyz}0[2]{ab#1cd#2ef}

这变成

\kernel@ifnextchar[{\@newcommand\xyz}{\@newcommand\xyz[{0}]}[2]{ab#1cd#2ef}

目的是[0]在未给出参数数量时添加。由于有[,我们得到

\@newcommand\xyz[2]{ab#1cd#2ef}

现在开始查找另一个可选参数,我跳过了它,我们得到

\@argdef\xyz[2]{ab#1cd#2ef}

检查token 的\xyz可定义性;如果不可定义,我们会收到错误消息,否则

\@yargdef\xyz\@ne{2}{ab#1cd#2ef}

真正的乐趣就从这里开始:

\ifx\@ne\tw@
  \def\reserved@b#11{[##1]}
\else
  \let\reserved@b\@gobble
\fi
\expandafter\@yargd@f\expandafter{\number2}\xyz{ab#1cd#2ef}

这定义\reserved@b\@gobble并导致

\@yargd@f{2}\xyz{ab#1cd#2ef}

回想一下 的定义\@yarg@def

\long\def\@yargd@f#1#2{%
  \def\reserved@a##1#1##2##{%
    \expandafter\def\expandafter#2\reserved@b##1#1}%
  \l@ngrel@x\reserved@a0##1##2##3##4##5##6##7##8##9###1%
}

参数是#1=2#2=\xyz,所以我们得到(双精度##在这里简化为单精度#

\def\reserved@a#12#2#{\expandafter\def\expandafter\xyz\reserved@b#12}
\l@ngrel@x\reserved@a0#1#2#3#4#5#6#7#8#9##1{ab#1cd#2ef}

第一个参数以\reserved@a分隔2,第二个参数以{分隔。这是 TeX 的一个特殊功能:最后一个参数可以用替换文本的左括号分隔。

因此,在我们的例子中,第一个参数是0#1#。由于\l@ngrel@x\long它会触发 的扩展,\reserved@a并带有以下参数:

\long\expandafter\def\expandafter\xyz\reserved@b0#1#2{ab#1cd#2ef}

因为\reserved@b\@gobble,我们最终得到

\long\def\xyz{ab#1cd#2ef}

相关内容