这个问题来自 在 \textbf 中使用的 AM、PM 缩写(小型大写)。读者将从那里了解剩余的内容。
和
\newcommand\cmd[2][0]{\def\y{#1}}
\edef\x{\cmd{1}}
我明白了
{\edef}
\cmd ->\@protected@testopt \cmd \\cmd {0}
\@protected@testopt #1->\ifx \protect \@typeset@protect \expandafter \@testopt
\else \@x@protect #1\fi
#1<-\cmd
{\ifx}
{true}
{\expandafter}
{\else}
\@testopt #1#2->\kernel@ifnextchar [{#1}{#1[{#2}]}
#1<-\\cmd
#2<-0
\kernel@ifnextchar #1#2#3->\let \reserved@d =#1\def \reserved@a {#2}\def \reser
ved@b {#3}\futurelet \@let@token \@ifnch
#1<-[
#2<-\\cmd
#3<-\\cmd [{0}]
\reserved@a #11#2{->\expandafter \def \expandafter \\cmd \reserved@b #11{
! Argument of \reserved@a has an extra }.
<inserted text>
\par
<to be read again>
}
l.3103 \edef\x{\cmd{1}}
? x
这就是稳健性吗?另请参阅是否有一个强大的 \renewcommand 替代品?。
答案1
使用
\newcommand{\foo}[...][...]
\foo
创建具有定义的宏
\@protected@testopt \foo \\foo {...}
在 LaTeX 的“受保护的扩展”命令(\protected@edef
,\protected@xdef
,\protected@write
)中, 的定义\@protected@testopt
被改变,使得不再发生进一步的扩展,因此
\protected@edef\test{\foo}
\show\test
给出
> \test=macro:
->\protect \foo .
\DeclareRobustCommand
工作原理略有不同,因为
\DeclareRobustCommand{\foo}[...][...]
给出了定义
> \foo=macro:
->\protect \foo .
该内部宏的名称中有一个空格:它被称为“ \foo
”。在这里执行“受保护的扩展”可得到
> \test=macro:
->\protect \foo .
IE保留了“带空格的名称”。由于此方法\protect
比 使用的机制添加了“更早” \newcommand
,因此它更加强大,但需要额外的 cname。
这两种机制都无法阻止在普通的\edef
、\xdef
或内进行扩展。为此,您需要 e-TeX 保护机制,它以类似 的方式\write
包装在\newcommand
\newrobustcmd
etoolbox
包。在那里,做
\newrobustcmd{\foo}[...][...]
给出了定义
> \foo=\protected macro:
->\@testopt \\foo {...}.
这会绝不在扩展上下文中扩展,因为引擎正在进行保护。
答案2
“脆弱性”和“稳健性”的概念对于 LaTeX 来说是恰当的;稳健的命令是指在\protected@edef
或 的替换文本中仍然存在的命令\protected@write
。它具有没有什么\edef
与 生存或有关\write
.
例如,之后
\DeclareRobustCommand{\foo}[1][bar]{...}
就像是
\edef\baz{\foo[x]}
将会失败
! Argument of \reserved@a has an extra }
相反,\protected@edef\baz{\foo[x]}
将赋予\baz
意义
\protect \foo [x]
(注意二空格后\foo
)。如果我们改为
\newcommand\foo[1][bar]{...}
\protected@edef\baz{\foo[x]}
的意思\baz
是
\protect \foo [x]
(后面只有一个空格\foo
)。 强化方法不同,但同样有效。 必须注意的是,强化是不是\newcommand
当为其简单形式时应用\newcommand\foo[n]{...}
。
当然,\protected
给出了更高级的稳健性,因为用 定义的命令\protected\def
甚至会存活下来\edef
。但这是另一个故事。
它是如何工作的?\newcommand\foo[1][bar]{...}
执行时,LaTeX 实际上会
\def\foo{\@protected@testopt \foo \\foo {bar}}
和
\expandafter\def\csname\string\foo\endcsname[#1]{...}
这与 LaTeX2.09 的方式非常相似
\def\foo{\@testopt\@foo{bar}}
\def\@foo[#1]{...}
但 has\@protected@testopt
本质上是“如果\protect
等于\relax
,则执行\@testopt\\foo
,否则输出\protect\foo
”。处理诸如这样的“控制符号”有点复杂\?
,但这几乎是事实。
答案3
此类宏使用 LaTeX 的保护机制,只能在\protected@edef
和其他 LaTeX 宏(例如辅助文件写入宏)中工作,这些宏设置\protect
得当。它们不适用于一般扩展上下文,例如\edef
或普通\write
。
如果您需要定义一个带有可选参数的强健的 LaTeX 宏,请使用核心宏\DeclareRobustCommand
或\newrobustcmd
从etoolbox
包中定义。