问题
我想编写几个软件包,它们需要我重新定义\newcommand
(\renewcommand
等)命令,以便我可以跟踪或更改作者随后定义的命令。但我不知道该怎么做。到目前为止,我的幼稚尝试都失败了。可能是因为我在 TeX 层面上仍然很弱。
我很惊讶我竟然找不到关于这个概念的更多暗示。感觉这是一件很明显的事情。
动机
一个简单的用例,也是我计划编写的第一个包,是为了确保以下内容:使用\newcommand
定义一个\cmd
已经定义的命令 - 比如: - 不会立即产生错误。\cmd
只会处于冲突状态。随后尝试扩展\cmd
将会产生错误(因为不清楚您想要的是两个定义中的哪一个)。
\cmd
随后可以通过使用重新定义来解决冲突\renewcommand
,之后\cmd
可以再次安全地扩展。例如:
\newcommand{\cmd}{FIRST} %
\cmd % outputs FIRST
\newcommand{\cmd}{SECOND} % no problem yet
\cmd % error: expanding ambiguous command
\renewcommand{\cmd}{THIRD} %
\cmd % outputs THIRD
例如,这可以用于调解使用 的软件包之间的冲突\newcommand
。当然,这个概念仍然很薄弱(例如,如果两个软件包\renewcommand
在同一个命令上独立使用,该怎么办?)。但它足以作为我的问题的用例。
在进一步了解这一点之后,我计划进行更细致的控制。
伪代码解决方案
感觉我必须做这样的事情(暂时忽略可选参数):
\let\old@newcommand\newcommand
\MetaRenewCommand{\newcommand}{
\ifdefined#1
\old@newcommand{#1}{
\PackageError{lazyfail}{Expanding ambiguous command \protect #1}
}
\else
\old@newcommand{#1}{#3}
\fi
}
当然,这段代码有很多错误。没有,\MetaRenewCommand
而且我仍然需要处理可选参数\newcommand
。
那么,我该如何开始呢?感觉这一定是可能的,因为\newcommand
和 朋友不是 LaTeX 的原语,而是根据较低级别的命令定义的。
进一步的动机
这是我为此想到的另一个用例。当包含一个包时,我想忽略它提供的所有命令,除了我指定的一个小列表:
\usepackagefor[\Lightning]{marvosym}
这里我正在加载marvosym
包,但仅供使用\Ligntning
。我选择这个示例是因为该marvosym
\CheckedBox
命令与类中的命令冲突llncs
。
我已经例行地用注释指定我计划使用包的命令,但实际上执行这一点会更好。
答案1
由于\newcommand
工作方式的原因,解决这个问题很棘手。Heiko 的方法可能更优雅,但一种可能的方法是使用xparse
处理语法\newcommand
和letltxmacro
处理方式\newcommand
是设立
\documentclass{article}
\usepackage{letltxmacro,xparse}
\makeatletter
\LetLtxMacro{\saved@newcommand}{\newcommand}
\DeclareDocumentCommand{\newcommand}{s+mo+o+m}{%
\begingroup
\edef\x{%
\endgroup
\ifdefined#2%
\unexpanded{%
\renewcommand{#2}%
{%
\PackageError{lazyfail}
{Expanding ambiguous command \protect #2}%
\@ehc
}%
}
\else
\noexpand\saved@newcommand
\IfBooleanT{#1}{*}%
{\noexpand#2}%
\IfNoValueF{#3}{[#3]}%
\IfNoValueF{#4}{[\unexpanded{#4}]}%
{\unexpanded{#5}}
\fi
}%
\x
}
\makeatother
\begin{document}
\newcommand{\cmd}{FIRST} %
\cmd % outputs FIRST
\newcommand{\cmd}{SECOND} % no problem yet
\cmd % error: expanding ambiguous command
\renewcommand{\cmd}{THIRD} %
\cmd % outputs THIRD
\end{document}
\newcommand
这里的方法是一次性获取重新定义的所有参数,然后确定是否要“回收”它们。
当然,你可以在没有的情况下设置所有这些xparse
,但这会很麻烦:很多\@ifstar
和\@ifnextchar
(或稍微好一点的\@testopt
)东西和一些辅助工具。
答案2
而不是\newcommand
我会重新定义。然后还会捕获\@ifdefinable
一些其他的东西,例如\newcounter
或:\newsavebox
\documentclass{article}
\makeatletter
% Save old meaning of \@ifdefinable in \saved@ifdefinable
\newcommand*{\saved@ifdefinable}{}
\let\saved@ifdefinable\@ifdefinable
% Redefine \@ifdefinable
% #1: command token
% #2: code that defines the command in #1
\renewcommand{\@ifdefinable}[2]{%
% Here the same test for checking #1 is used as in the
% original definition.
\edef\reserved@a{\expandafter\@gobble\string#1}%
% \reserved@a contains the name without backslash
\@ifundefined\reserved@a{%
\saved@ifdefinable{#1}{#2}%
}{%
% Report the command with the name clash in the .log file
\@latex@info{Ambigous command: \string#1}%
% Redefine the command to generate an error message.
% \@ehd is the standard help text that starts with "You're in trouble here."
\def#1{\@latex@error{Expanding ambiguous command}\@ehd}%
}%
}
\makeatother
\begin{document}
\newcommand*{\cmd}[1]{FIRST(#1)}
\cmd{argument}
\newcommand*{\cmd}[2]{SECOND(#1,#2)}
\cmd{param1}{param2}
\renewcommand*{\cmd}{THIRD}
\cmd
\end{document}
“边境案件”:
\end...
如果有人尝试定义以 开头的命令end
,或者在 的情况下, LaTeX 会发出抱怨\relax
。这些情况不会被上述重新定义重新定义。对参数的处理不明确。在出现模糊错误的情况下,参数在输入中保持不变。
\newcommand
只有通过 LaTeX 界面 ( 、\newcounter
、 、 …)定义的命令才会被检测到。也可以通过 TeX 的原始命令、、、 … 或普通 TeX 命令 (等)\newsavebox
进行定义。这些命令不区分新旧命令。\def
\edef
\gdef
\newcount
回答进一步的激励:
当然,\newcommand
可以重新定义删除一些定义,并保留\Lightning
为包的唯一命令marvosym
,但是
- 其他未加 定义的宏
\newcommand
可能会产生不良影响。 - 保留的宏可能依赖于其他已定义
\newcommand
但未被保留的宏。