目标:我想要做的是允许_
和^
仅在特定宏内用于在文本模式下生成下标和上标。我知道我可以在整个文档中使_
和^
处于活动状态,但这会与其他假设它们不会发生的软件包混淆。
我挖掘了一下,并没有找到一个接近我想要的例子。
我的代码一直基于类似的例子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 分配仅有的影响将输入文件中未解析的字符转换为字符标记。一旦您以#1
etc 的形式传递标记列表,您就会传递标记列表,并且每个标记都已有一个 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 更改的“参数”。