删除命令名称的首字母 \,以便与 \csname 一起使用

删除命令名称的首字母 \,以便与 \csname 一起使用

TeXBook 第 40 页有以下文字(重点添加)

相反,你可以从字符标记列表转换为控制序列,方法是说。出现在和\csname⟨tokens⟩\endcsname之间的此构造中的标记可能包含其他控制序列,只要这些控制序列最终扩展为字符而不是 TEX 基元;最终的字符可以是任何类别,不一定是字母。例如,本质上与 相同;但是是非法的,因为扩展为包含\csname\endcsname\csname TeX\endcsname\TeX\csname\TeX\endcsname\TeX\kern此外,\csname\string\TeX\endcsname将产生不寻常的控制序列\\TeX,即标记 [ \TeX],这是通常无法编写的。

不幸的是,它没有说明如果要删除初始值\并最终得到控制序列,您可以对最后一点做什么\TeX。这个问题是关于如何做到这一点的。

我为什么想要这个?

我想创建一个“工厂”命令,可以动态生成几个新命令。以下有效:

\documentclass{article}

\NewDocumentCommand{\myfactory}{m m}{
    \NewDocumentCommand{#1}{m}{A command has been created with argument ``#2'' and called with argument ``##1''}
}

\myfactory{\mycommand}{Hello}

\begin{document}

\mycommand{World!}

\end{document}

它给出了预期以下输出:

在此处输入图片描述

但是,如果我更改工厂以制作该命令的“特殊”版本,那么它会失败

\documentclass{article}

\NewDocumentCommand{\myfactory}{m m}{
    \NewDocumentCommand{#1}{m}{A command has been created with argument ``#2'' and called with argument ``##1''}
    \NewDocumentCommand{#1special}{m}{A `special' command has been created with argument ``#2'' and called with argument ``##1''}
}

\myfactory{\mycommand}{Hello}

\begin{document}

\mycommand{World!}
\mycommandspecial{Special world!}

\end{document}

错误信息如下,看起来很合理

./newcommand name mangling.tex:8: LaTeX cmd Error: First argument of '\NewDocum
entCommand' must be a command.

因此我可以尝试使用\csname和生成命令名称\endcsname,但由于上述原因,以下操作失败:

\documentclass{article}

\NewDocumentCommand{\myfactory}{m m}{
    \NewDocumentCommand{#1}{m}{A command has been created with argument ``#2'' and called with argument ``##1''}
    \expandafter\NewDocumentCommand\csname #1special\endcsname{m}{A `special' command has been created with argument ``#2'' and called with argument ``##1''}
}

\myfactory{\mycommand}{Hello}

\begin{document}

\mycommand{World!}

\mycommandspecial{Special world!}

\end{document}

这里我们得到错误

./newcommand name mangling.tex:13: Undefined control sequence.
l.13 \mycommandspecial
                      {Special world!}

我猜是因为它实际上定义了一个名为的命令\\mycommandspecial,而该命令没有正常的调用方式。

另一方面,以下工作,但它有点烦人,因为用户必须像\myfactory{mycommand}{Hello}而不是 那样来调用它\myfactory{\mycommand}{Hello},而我更喜欢后者。

\documentclass{article}

\NewDocumentCommand{\myfactory}{m m}{
    \expandafter\NewDocumentCommand\csname #1\endcsname{m}{A command has been created with argument ``#2'' and called with argument ``##1''}
    \expandafter\NewDocumentCommand\csname #1special\endcsname{m}{A `special' command has been created with argument ``#2'' and called with argument ``##1''}
}

\myfactory{mycommand}{Hello}

\begin{document}

\mycommand{World!}

\mycommandspecial{Special world!}

\end{document}

答案1

定义您的\csstring命令。它已包含在 LuaTeX 中;如果未定义,它将使用\cs_to_str:Nfrom expl3

\documentclass{article}

\ExplSyntaxOn
\ProvideExpandableDocumentCommand{\csstring}{m}{\cs_to_str:N #1}
\ExplSyntaxOff

\NewDocumentCommand{\myfactory}{m m}{%
  \NewDocumentCommand{#1}{m}{%
    A command has been created with argument ``#2'' and called with argument ``##1''%
  }%
  \ExpandArgs{c}\NewDocumentCommand{\csstring #1special}{m}{%
    A `special' command has been created with argument ``#2'' and called with argument ``##1''%
  }%
}

\myfactory{\mycommand}{Hello}

\begin{document}

\mycommand{World!}

\mycommandspecial{Special world!}

\end{document}

对于您需要的“特殊”命令\ExpandArgs{c},它将跳过以下标记并从后面的括号组构建命令名称。该构造相当于

\expandafter\NewDocumentCommand\csname \csstring#1special\endcsname{m}{...}

但更具可读性。

不要忘记保护端线。

答案2

如果您使用的是 LuaTeX,则可以使用\csstring原语代替\string,这样可以省去初始的反斜杠。

使用其他引擎,您可以轻松定义\csstring自己:

\def\gobble#1{}
\def\csstring{\expandafter\gobble\string}

这是有效的,因为\string它扩展为一系列 12 类标记(即可打印的非字母字符),您可以在扩展后丢弃第一个标记。

编辑:如下所述,如果\escapechar设置为-1或 ,此宏将不起作用32。为了完整起见,下面的内容在这些情况下也将起作用:

\def\gobble#1{} \def\gobbletwo#1#2{}
\def\csstring{%
    \ifnum-1=\escapechar \expandafter\gobbletwo \else
    \ifnum32=\escapechar \expandafter\expandafter
        \expandafter\gobbletwo\fi\fi
    \expandafter\gobble\string}

设置提供了解决问题的另一种方法,例如在 plain.tex 的宏\escapechar=-1中使用。\newif

相关内容