我可以重新定义一个命令来包含它自己吗?

我可以重新定义一个命令来包含它自己吗?

我想通过编写类似以下内容来“添加”现有命令

\renewcommand{\somecommand}{\begin{something}\somecommand\end{something}}

但(毫不奇怪)我收到有关“嵌套太深”的错误。

有没有办法采用现有命令,向其中添加内容(通过附加或添加其他命令或文本),然后让该“增强”命令替换现有命令?

答案1

您可以使用\let以另一个名称保存命令,然后在中使用该新名称\renewcommand

\documentclass{article}

\let\oldemph\emph
\renewcommand{\emph}[1]{\textbf{\oldemph{#1}}}

\begin{document}
\oldemph{Old emph}
\emph{New emph}
\end{document}

(编辑:TeX 常见问题解答有更多关于“修补”命令的信息。编辑2:下面 egreg 的回答描述了\let)的一些陷阱

答案2

重要更新

从 2020-10-01 版本开始,LaTeX 内核具有\NewCommandCopy与 相同的功能,\LetLtxMacro并且做得更好。它还提供了\ShowCommand获取内部定义的强大命令。因此,下面的代码现在应该是

\NewCommandCopy{\oldemph}{\emph}
\renewcommand{\emph}[1]{\textbf{\oldemph{#1}}}

下面关于问题的分析\let仍然有效。

原始答案

虽然诸如此类的事情

\let\oldemph\emph
\renewcommand{\emph}[1]{\textbf{\oldemph{#1}}}

似乎有效,但实际上无效,因为\emph用 定义\DeclareRobustCommand。它在哪里不起作用?假设您编写该代码,然后\emph在标题中使用:

\caption{This is \emph{emphasized}}

文件中写的内容.aux

\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces This is \textbf  {\emph  {emphasized}}}}{1}}

这是因为原来的扩展\emph

\protect\emph§

(其中§表示名称中的空格)。因此,\emph{emphasized}标题中的 会连续扩展为

\textbf{\oldemph{emphasized}}
\protect\textbf§{\protect\emph§{emphasized}}
\textbf§{\emph§{emphasized}

并写为

\textbf  {\emph  {emphasized}}

(因为 TeX 在写入时会在命令名称后添加一个空格)。在这种情况下,在读取文件以构建图表或表格列表时,TeX 会发现

\textbf{\textbf{\oldemph{emphasized}}}

这没什么大不了的。但不难想象,在某些情况下,这可能会导致无限循环。

该软件包letltxmacro可以防止出现此问题:

\usepackage{letltxmacro}
\LetLtxMacro{\oldemph}{\emph}
\renewcommand{\emph}[1]{\textbf{\oldemph{#1}}}

是一种更安全的方法。

绝不在用定义的命令或用并具有可选参数定义的命令\let上使用。\DeclareRobustCommand\newcommand

前者可以通过以下事实来识别\show\command

> \command=macro:
->\protect \command  .

后者的特点是\show\command回报

> \command=macro:
->\@protected@testopt \command \\command {...}.

(其中...实际上是可选参数的默认值)。

答案3

\g@addto@macro是的,使用或某些扩展定义(使用)可以实现这一点\expandafter。以下是一些示例:

在此处输入图片描述

\documentclass{article}
\setlength{\parindent}{0pt}% Just for this example
\begin{document}
\newcommand{\somecmd}{some \textbf{text}}
\somecmd \par
\makeatletter
\g@addto@macro\somecmd{ and some \textit{more} text}
\makeatother
\somecmd

\renewcommand{\somecmd}{some \textbf{text}}
\somecmd \par
\makeatletter
\expandafter\def\expandafter\somecmd\expandafter{\somecmd{} and some \textit{more} text}
\makeatother
\somecmd
\end{document}

后一个示例与前一个示例一样,将内容附加到宏中。后者中的 的位置\somecmd可用于添加某些内容。

但是,没有什么可以阻止您创建一个包含旧命令作为其扩展的一部分的新命令,因为内存问题已成为过去。

当新定义的宏的内容无法正确扩展时,您就会遇到问题。以您的情况为例:虽然\somecommand可能按预期扩展,但center环境却没有。

答案4

附加到已定义的命令

如果您只想在命令后附加一些内容,则可以使用 LaTeX 内核\g@addto@macro

\def\authors{}
\def\addauthor#1{%
    \g@addto@macro\authors{
        \begin{tabular}[t]{c}%
            #1
        \end{tabular}%
    }
}

然后在序言中你可以:

\addauthor{Cristiano Ronaldo}
\addauthor{Lionel Messi}

\authors包含您可以在其中使用的可打印作者列表maketitle

附加到已定义的命令

我还没有测试过,但是可以尝试一下:

\def\aroundto#1#2#3{\toks0={#2}\toks1=\expandafter{#1}\toks2={#3}\edef#1{\the\toks0 \the\toks1 \the\toks2}}

参考:两种解决方案均在Hacks 博客

相关内容