如何重新定义或修补“\newcommand”命令?

如何重新定义或修补“\newcommand”命令?

问题

我想编写几个软件包,它们需要我重新定义\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处理语法\newcommandletltxmacro处理方式\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但未被保留的宏。

相关内容