为什么\expandafter\newcommand\csname hello\endcsname{Hello world}
创建新的宏时会\expandafter\newcommand{\csname hello\endcsname}{Hello world}
产生垃圾?
根据我的理解,\csname hello\endcsname
以编程方式创建一个可用作命令名称的新字符串,在本例中,类似于\hello
。您可以同时编写\newcommand\hello{Hello world}
和\newcommand{\hello}{Hello world}
,它们都可以工作。那么为什么在涉及 csname 时,我必须使用不带花括号的形式来括住名称呢?
答案1
的“官方”语法\newcommand
是
\newcommand{<macro name>}[<args>]{<text>}
或者
\newcommand{<macro name>}[<args>][<default>]{<text>}
但事实证明,<macro name>
只要出现一个标记,括号周围的括号是可选的。括号有助于避免错误,但一些自我控制通常就足够了。
和
\expandafter\newcommand\csname hello\endcsname{world}
令牌在开始行动并吸收其第一个参数\hello
之前被构建\newcommand
(实际上,故事很长,但这个简短的解释足够精确)。
另一方面,
\expandafter\newcommand{\csname hello\endcsname}{world}
将尝试扩展括号:\expandafter
仅对单个标记起作用,如果标记可扩展,则触发其扩展,否则不执行任何操作。你能如果你愿意的话,可以使用大括号,但是你还需要\expandafter
:
\expandafter\newcommand\expandafter{\csname hello\endcsname}{world}
然而,对于这样一个简单的结构来说,这也太多了,不是吗?
如果你发现自己必须做几个这样的定义,那么构建一个包装器就“很容易”了
\makeatletter
\newcommand\newnamecommand{\@star@or@long\new@name@command}
\newcommand\new@name@command[1]{\expandafter\new@command\csname #1\endcsname}
\newcommand\renewnamecommand{\@star@or@long\renew@name@command}
\newcommand\renew@name@command[1]{\expandafter\renew@command\csname #1\endcsname}
\newcommand\providenamecommand{\@star@or@long\provide@name@command}
\newcommand\provide@name@command[1]{\expandafter\provide@command\csname #1\endcsname}
\makeatother
\newnamecommand{hello}{Hello World}
\newnamecommand*{helloarg}[1]{Hello #1}
\renewnamecommand{hello}{Whatever}
\providenamecommand{foo}{FOO}
答案2
您需要为 提供一个明确的控制序列\newcommand
,并且\csname <text>\endcsname
直到扩展之前它才是一个控制序列。
使用
\expandafter\newcommand\csname hello\endcsname{<stuff>}
扩展为
\newcommand\hello{<stuff>}
\expandafter
这是跳过\newcommand
并扩展\csname hello\endcsname
到的结果\hello
。然而,
\expandafter\newcommand{\csname hello\endcsname}{<stuff>}
扩展为
\newcommand{\csname hello\endcsname}{<stuff>}
as{
不可扩展,因此\expandafter
毫无用处。如果您想使用“分组接口\newcommand
”,则需要额外的\expandafter
:
\expandafter\newcommand\expandafter{\csname hello\endcsname}{<stuff>}
但是,如果没有它,\newcommand
就会失败,因为没有明确的控制序列。
etoolbox
\@namedef{<csname>}
通过与 LaTeX 内核的接口提供了一种解决此问题的方法latex.ltx
):
\csdef{<csname>}<args>{<stuff>}
也就是说,你可以使用
\csdef{hello}<args>{<stuff>}
<args>
以上是可选的,但采用\def
参数指定(或模式)的形式。也就是说,#1
而不是[1]
说。