为什么 \makeatletter 在 \newcommand 中不起作用?

为什么 \makeatletter 在 \newcommand 中不起作用?

经过几个小时的苦思冥想,我终于将问题缩小到\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@parse,即不是 \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)。

进一步阅读:

相关内容