我一次又一次地看到,技术人员定义命令并以相反的顺序执行一些操作,作为业余爱好者,我会这样做。请看此示例:
在他的回答到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
按照上面的步骤:
\apptocmd
添加东西\@part
,所以\@part
应该存在。在定义(此插入)但是\parttitle
没有被评估,因此不必存在。\def
只是定义\parttitle
并且并不真正关心其替换文本中是否存在任何内容。- 有些如(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 编写的,我会事先声明一个标记列表变量。