在 (La)TeX 中将一个字符用作另一个宏中的宏

在 (La)TeX 中将一个字符用作另一个宏中的宏

目标:我想要做的是允许_^仅在特定宏内用于在文本模式下生成下标和上标。我知道我可以在整个文档中使_^处于活动状态,但这会与其他假设它们不会发生的软件包混淆。

我挖掘了一下,并没有找到一个接近我想要的例子。

我的代码一直基于类似的例子https://texfaq.org/FAQ-actinarg这适用于创建一个命令,其中的参数是排版的_并且^是活动的:

\documentclass{article}
\begin{document}
\begingroup
\catcode`_=\active%
\gdef_#1{\ensuremath{{}\sb{#1}}}%
\catcode`^=\active%
\gdef^#1{\ensuremath{{}\sp{#1}}}%
\endgroup

\def\subsup{%
  \begingroup
    \catcode`_=\active
    \catcode`^=\active
    \Xsubsup
}
\def\Xsubsup#1{%
    #1%
  \endgroup
}

\subsup{foo_x bar^y baz}

% foo_a bar^b baz % this will cause an error because _ is not active.

现在,如果我添加想要嵌入的功能:

\DeclareRobustCommand*\newMacro[2][\null]{
  % other processing and formatting not directly relevant to this example
  \subsup{#2}%
}

\newMacro{foo_x bar^y baz}
\end{document}

我收到数学模式错误:

ERROR: Missing $ inserted.

--- TeX said ---
<inserted text> 
                $
l.28 \newMacro{foo_x bar^y baz}

因此我尝试将 subsup 宏嵌入newMacro(我重复了上面的一些代码以便于复制和粘贴):

\documentclass{article}
\begin{document}
\begingroup
\catcode`_=\active%
\gdef_#1{\ensuremath{{}\sb{#1}}}%
\catcode`^=\active%
\gdef^#1{\ensuremath{{}\sp{#1}}}%
\endgroup

\DeclareRobustCommand*\newMacro[2][\null]{
\def\subsup{%
  \begingroup
    \catcode`_=\active
    \catcode`^=\active
    \Xsubsup
}
\def\Xsubsup#1{%
    #1%
  \endgroup
}
  % other processing and formatting not directly relevant to this example
  \subsup{#2}%
}

\subsup{foo_x bar^y baz} %this is undefined, and so fails because newMacro hasn't been called yet.

\newMacro{foo_x bar^y baz}

\subsup{foo_x bar^y baz} % this works because it comes after newMacro has been called.
\end{document}

这仍然不起作用,但现在我收到错误:

ERROR: Use of \Xsubsup doesn't match its definition.

--- TeX said ---
\\newMacro  ...\Xsubsup #1{#1\endgroup } \subsup {
                                                  #2}
l.28 \newMacro{foo_x bar^y baz}

为什么这两个subsup函数只有在没有嵌入另一个宏时才有效?为什么只有当它嵌入另一个宏时才会出现Xsubsup与其定义不匹配的错误?有什么办法可以解决这个问题吗?

答案1

简短的回答是,这是不可能的。

这就是为什么你不能\verb在另一个宏的参数中使用。catcode 分配仅有的影响将输入文件中未解析的字符转换为字符标记。一旦您以#1etc 的形式传递标记列表,您就会传递标记列表,并且每个标记都已有一个 catcode,因此不会查看 catcode 分配。

所以当你有

\subsup{#2}%

它作为已经构造的标记列表传递,因此尽管 catcode_已更改,但这并不重要,tex 不会看到未解析的_字符,而只会_看到已经具有其正常 catcode 的标记。

但是,如果你准备采用非常现代的方法来使用 1990 年代的 e-Tex 扩展,那么你可以使用原语重新标记标记列表\scantokens\scantokens本质上是将其参数写入内部文件流,然后将其读回,就像\input使用 catcodes 一样,因此更改

\def\Xsubsup#1{%
    #1%
  \endgroup

\def\Xsubsup##1{%
   \scantokens{##1}%
  \endgroup
}

修复问题,满足其定义的错误是因为您使用了#1而不是##1在内部定义。

答案2

作为 David 答案的替代方案,您可以尝试\newMacro使用较少的参数,并让最后的调用\subsup吸收第二个参数。这是我建议的代码:

\documentclass{article}
\begin{document}

\makeatletter
\DeclareRobustCommand\newMacro[1][\null]{
  \let\subsup\@subsup
  % Other stuff
  \subsup
}

\def\@subsup#{
  \bgroup
  \catcode`^=\active
  \catcode`_=\active
  \let\next=
}
\makeatother

\begingroup
\catcode`^=\active
\catcode`_=\active
\gdef^#1{\ensuremath{{}\sp{#1}}}
\gdef_#1{\ensuremath{{}\sb{#1}}}
\endgroup

\newMacro{ab_c}

\end{document}

我喜欢#{诡计在分隔宏中。它强制在要传递的参数中使用括号\subsup,同时使用括号形成一个保护 catcode 更改的组。

当然,这只有在最后的\newMacro所做的就是调用\subsup。然而,即使它想做其他事情,以

\newcommand\newMacro[1]{
  % stuff
  \subsup{#1}
  % more stuff
}

您可以简单地将其分解为几个宏,就像\subsup分解原来的定义一样,借助有点晦涩的\aftergroup指令:

% #1 is a token to put after \subsup
\def\subsup#1#{
  \bgroup
  % catcode changes
  \aftergroup#1
  \let\next=
}
\def\newMacro{
  % stuff
  \subsup{\XnewMacro}
}
\def\XnewMacro{
  % more stuff
}

这样,您就可以完全跳过发生 catcode 更改的“参数”。

相关内容