TeX 常见问题解答修补现有命令“提供一种方法来添加到具有可选参数的现有命令:
\documentclass{article}
\usepackage{letltxmacro}
\newcommand{\rough}[2][\default]{...}
\LetLtxMacro{\OldRough}{\rough}
\renewcommand{\rough}[2][\newdef]{\mumble\OldRough[{#1}]{#2}}
但是,它假设您需要一个新的默认参数“ \newdef
”。如果要保留默认参数,您可以将“ \newdef
”更改为“ \default
”,但这只有在您对先前定义的命令有内部了解的情况下才有可能。此外,如果修改了该值,您也必须在修补程序中更改它。
是否可以向现有命令添加可选参数,同时保留默认参数,即使您不知道该参数是什么?
注意:该参数在新的定义中仍然可用。
编辑:尝试了建议后\xapptocmd
,我仍然无法在我\xapptocmd
正在使用的命令中重现所需的结果clevelref
。\label
我希望下面的代码写入“aaa”,然后应用标签,但事实并非如此,即使用数学之类的东西替换,内容仍然不会出现在最终文档中:
\documentclass[]{article}
\usepackage{cleveref,xpatch}
\xpretocmd\label{aaa}{}{}
\begin{document}
\label{example}
\end{document}
这给我的印象是它没有被正确附加。
此外,当尝试访问的第二个参数时\label
,TeX 会抛出错误Illegal parameter number in definition of \etb@resrvda. \xpretocmd\label{#1#2}
:
\documentclass[]{article}
\usepackage{cleveref,xpatch}
\xpretocmd\label{#1#2}{}{}
\begin{document}
\end{document}
我推测这是因为它正在检测 的标准定义\label
,它只有一个参数,而不是cleverf
的参数。
如何纠正这些问题?
答案1
在处理带有可选参数的命令定义时,LaTeX 会做什么?假设我们有
\newcommand{\test}[2][default]{Optional: #1; mandatory: #2.}
由于步骤很长,与讨论无关,基本工作如下:
\def\test{\@protected@testopt\test\\test{default}}
\expandafter\def\csname\string\test\endcsname[#1]#2{Optional: #1; mandatory: #2.}
在第一行中,\\test
代表单身的名称中带有反斜杠的令牌,可通过 获得\csname\string\test\endcsname
。
宏\@protected@testopt
根据是否\protect
是\relax
进行分支,然后调用\@testopt
(删除\else...\fi
部分)或\@x@protect\test
(删除直到的所有内容\fi
并仅留下\protect\test
)。宏\@testopt
检查后续内容[
并执行正确的操作。
因此,如果你想修补此类命令的真实替换文本,你需要执行以下操作
\expandafter\patchcmd\csname\string\test\endcsname
{<search>}
{<replace>}
{<success>}{<fail>}
如果你有
\DeclareRobustCommand{\test}[2][default]{Optional: #1; mandatory: #2.}
因为在这种情况下你需要做
\expandafter\patchcmd\csname\string\test\space\endcsname
{<search>}
{<replace>}
{<success>}{<fail>}
对于没有可选参数的情况,在调用时\DeclareRobustCommand{\test}[<n>]{...}
应该
\expandafter\patchcmd\csname test \endcsname
{<search>}
{<replace>}
{<success>}{<fail>}
这就是我编写该软件包的原因xpatch
,它能够测试命令是如何定义的,并\patchcmd
使用正确的咒语进行调用。它还支持\newrobustcmd
frometoolbox
并具有用于修补 内部的内置工具biblatex
。
所以你可以简单地做
\usepackage{xpatch}
\xpatchcmd{\test}
{<search>}
{<replace>}
{<success>}{<fail>}
并且它在所有情况下都有效。
如果你想修改可选参数的默认值怎么办?即使不知道默认值是什么?
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\def\replaceoptionalargument#1#2#3#4{%
\ifcsname\string#1\endcsname
\@replaceoptionalargument#1{#2}{#3}%
\else
#4%
\fi
}
\def\@replaceoptionalargument#1#2#3{%
\edef#1{%
\unexpanded\expandafter\expandafter\expandafter{%
\expandafter\@@replaceoptionalargument#1{#2}%
}%
}%
#3%
}
\def\@@replaceoptionalargument#1#2#3#4#5{#1#2#3{#5}}
\makeatother
\newcommand{\test}[2][default]{}
\replaceoptionalargument\test{new}{\message{YES}}{\message{NO}}
关于修补的补充\label
当你尝试修补命令时,你必须详细了解它的定义方式。例如,如果你\show\label
在测试文档的序言中这样做,你将看到以下信息
> \label=macro:
#1->\@bsphack \protected@write \@auxout {}{\string \newlabel {#1}{{\@currentlabel }{\thepage }}}\@esphack .
这是标准的 LaTeX 定义。事实上,cleveref
延迟了在文档开始时的重新定义。好的,让我们\show\label
在之后发出\begin{document}
,得到
> \label=macro:
->\@ifnextchar [\label@optarg \label@noarg .
这表明\label
没有像
\renewcommand{\label}[2][<default>]{...}
因此你的尝试将失败,因为\label
没有任何参数。开发人员为什么要这样做?因为不可选参数的默认值,并且无论是否使用可选参数都必须执行不同的代码。
答案2
该xpatch
软件包可以修补包含可选参数的命令。以下更改定义而不更改可选参数的值(并且可以使用#1
或访问参数#2
):
\documentclass[]{article}
\usepackage{xpatch}
\newcommand\foo[2][default]
{%
Normal definition. Optional argument: #1. Mandatory argument: #2.%
}
\begin{document}
\foo{abc}
\xpatchcmd\foo{Normal}{Patched}{}{}
\foo{abc}
\xpretocmd\foo{Optional=#1. }{}{}
\foo{abc}
\xapptocmd\foo{ Mandatory=#2.}{}{}
\foo{abc}
\end{document}
答案3
\show\rough
揭示了:
> \rough=macro:
->\@protected@testopt \rough \\rough {\newdef }.
l.12 \show\rough
\rough
只是一个包装器,用于启动 LaTeX 2e-kernel 的机制,用于在考虑\protect
离子机制的同时检测可选参数的存在。
内部调用宏\\rough
来处理参数,因此需要修补该宏:
\documentclass{article}
\newcommand{\rough}[2][\default]{... s.th. with #1 and #2 ...}
\expandafter\show\csname\string\rough\endcsname
% > \\rough=\long macro:
% [#1]#2->... s.th. with #1 and #2 ....
% <recently read> \\rough
%------------------------------------------------------------
\newcommand\PassFirstToSecond[2]{#2{#1}}%
\newcommand\Exchange[2]{#2#1}%
\expandafter\PassFirstToSecond\expandafter{%
\romannumeral
\expandafter\expandafter\expandafter\Exchange
\expandafter\expandafter\expandafter{%
\csname\string\rough\endcsname[{#1}]{#2}%
}{0 \mumbleatbegin}%
\mumbleatend%
}{%
\long\expandafter\def\csname\string\rough\endcsname[#1]#2%
}%
\expandafter\show\csname\string\rough\endcsname
% > \\rough=\long macro:
% [#1]#2->\mumbleatbegin ... s.th. with #1 and #2 ...\mumbleatend .
% <recently read> \\rough
\stop