经过几个小时的苦思冥想,我终于将问题缩小到\makeatletter
在 内部不起作用的事实\newcommand
。我调用的命令\makeatletter
被注册为未定义,好像\makeatletter
没有执行任何操作。
为什么会这样呢?
在下面的例子中,我定义了两个命令。从文档主体调用其中一个命令会导致未定义错误,而另一个则不会。
\documentclass[11pt]{book}
\usepackage{kvsetkeys}
\newcommand{\commandThatFails}{
\makeatletter \comma@parse{}{} \makeatother
}
\makeatletter
\newcommand{\commandThatWorks}{
\comma@parse{}{}
}
\makeatother
\begin{document}
Hi there.
\commandThatFails
\commandThatWorks
\end{document}
答案1
当\makeatletter
执行时,它会改变
猫码至@
11(字母),因此它可以是宏名称的一部分。但是,在定义宏或使用宏的参数时,标记会被存储但不执行。
所以在这里,
\newcommand{\commandThatFails}{
\makeatletter \comma@parse{}{} \makeatother
}
\makeatletter
被存储为命令中的标记,而不是被执行。由于@
仍为非字母,因此命令将其解析为标记\comma
、
@
、p
、a
、r
、s
和e
,即不是 \comma@parse
然而,在你的第二个例子中,\makeatletter
执行前该命令已定义,因此可以\comma@parse{}{}
正确解析:
\makeatletter
\newcommand{\commandThatWorks}{
\comma@parse{}{}
}
\makeatother
原因如下:\verb|...|
在参数内失败:它不会被执行,只是被存储,导致任何奇怪的字符都会引起各种错误。
正如 @percusse 所指出的,在这种情况下,您@
只需要 -macros 一次或两次,您可以使用\csname comma@parse\endcsname
它来扩展所有宏,然后将\csname
和之间的每个标记转换\endcsname
为宏名称。所以这也可以工作:
\newcommand{\commandThatDoesntFail}{\csname comma@parse\endcsname{}{}}
命令解析为的位置\comma@parse{}{}
(请注意,尾部{}{}
被视为 的参数\comma@parse
,而不是\endcsname
)。
进一步阅读: