(La)TeX 命令的反向定义

(La)TeX 命令的反向定义

我一次又一次地看到,技术人员定义命令并以相反的顺序执行一些操作,作为业余爱好者,我会这样做。请看此示例:

他的回答scrpage2:标题的三个“标记”egreg 定义了一个\parttitle,并且他还应用了一个补丁:

\apptocmd{\@part}{\parttitle{#2}}{}{}
\def\parttitle#1{\gdef\theparttitle{#1}}
\def\theparttitle{} % initialization

我会按照这种方式来完成——首先初始化,然后进行实际定义,最后进行修补:

\def\theparttitle{} % initialization
\def\parttitle#1{\gdef\theparttitle{#1}}
\apptocmd{\@part}{\parttitle{#2}}{}{}

测试表明,这也有效。所以我的问题是:

向后定义 TeX 命令有什么好处?
(至少从业余爱好者的角度来看……)

答案1

只要使用时内容可用/已声明,则不存在技术差异。这是因为 TeX 是一种宏语言,其中宏及其参数被替换为替换文本 - 后者仅在使用时进行评估(除非在定义时进行评估,例如使用\edef\xdef)。

例如,看看

\apptocmd{\@part}{\parttitle{#2}}{}{}
\def\parttitle#1{\gdef\theparttitle{#1}}
\def\theparttitle{} % initialization

按照上面的步骤:

  1. \apptocmd添加东西\@part,所以\@part应该存在。在定义(此插入)但是\parttitle没有被评估,因此不必存在。
  2. \def只是定义\parttitle并且并不真正关心其替换文本中是否存在任何内容。
  3. 有些如(2)。

从 TeXnician 的角度来看,人们可能会这样编写序列,将更改的来源视为\@part- 需要更改的最重要的事情;也许是实际设置内容的最后一个宏。随后,更改需要\parttitle,它接下来被定义。并且\parttitle需要\theparttitle,它接下来被定义(作为安全/默认)。从最深层次的定义开始,以不同的方式看待它同样有意义。


我认为这里最重要的是要理解 TeX 的宏语言使用

\<csname><argument text>{<replacement text>}

其中<replacement text>只有通过正确调用才会被考虑/评估\<csname>(再次,除非使用某种扩展技术(如\edef\xdef)来定义)。

答案2

事实是,我首先

\apptocmd{\@part}{\gdef\theparttitle{#2}}{}{}

没有初始化。然后我意识到最好有\parttitle{#2}一个宏来\theparttitle在需要的地方进行设置。然后我记得初始化是必要的。

但这种风格在 Knuth 的宏中非常常见。例如,在plain.tex

225 \outer\def\newcount{\alloc@0\count\countdef\insc@unt}
226 \outer\def\newdimen{\alloc@1\dimen\dimendef\insc@unt}
    <other similar lines>
236 \outer\def\newlanguage{\alloc@9\language\chardef\@cclvi}
237 \def\alloc@#1#2#3#4#5{\global\advance\count1#1by\@ne
238   \ch@ck#1#4#2% make sure there's still room
239   \allocationnumber=\count1#1%
240   \global#3#5=\allocationnumber
241   \wlog{\string#5=\string#2\the\allocationnumber}}

再次

264 \outer\def\newif#1{\count@\escapechar \escapechar\m@ne
265   \expandafter\expandafter\expandafter
266    \def\@if#1{true}{\let#1=\iftrue}%
267   \expandafter\expandafter\expandafter
268    \def\@if#1{false}{\let#1=\iffalse}%
269   \@if#1{false}\escapechar\count@} % the condition starts out false
270 \def\@if#1#2{\csname\expandafter\if@\string#1#2\endcsname}
271 {\uccode`1=`i \uccode`2=`f \uppercase{\gdef\if@12{}}} % `if' is required

\@if在定义之前先介绍\newif会让人难以理解。最好在主宏之后定义辅助宏(如果可能),这样每个宏的作用就更清晰了。

最后一个例子

1017 \newbox\rootbox
1018 \def\root#1\of{\setbox\rootbox
1019   \hbox{$\m@th\scriptscriptstyle{#1}$}\mathpalette\r@@t}
1020 \def\r@@t#1#2{\setbox\z@\hbox{$\m@th#1\sqrt{#2}$}\dimen@\ht\z@
1021   \advance\dimen@-\dp\z@
1022   \mkern5mu\raise.6\dimen@\copy\rootbox \mkern-10mu\box\z@}

最好在使用寄存器之前分配它们,就像在编程语言中声明变量一样。有人可能会认为,在开始讨论的代码中,\theparttitle就像一个变量。然而,TeX 基于宏扩展,因此\theparttitle实际上是辅助宏。如果这是用 LaTeX3 编写的,我会事先声明一个标记列表变量。

相关内容