我想根据当前级别修补编号列表中的某些命令(enumi
、enumii
、...)。基本上,我想用名称存储在宏中的命令替换名称存储在另一个宏中的宏。这总结了我的问题:
\documentclass{article}
\usepackage{xpatch}
\begin{document}
\def\tobereplaced{foo}
\def\tobepatched{\tobereplaced}
\def\patchedname{tobepatched}
\def\replacedname{tobereplaced}
\xpatchcmd{\csname\patchedname\endcsname}{\csname\replacedname\endcsname}{\csname\replacedname\endcsname!}{yes}{no}
\tobepatched
\end{document}
补丁无效。有趣的是,文档输出为“yesno”(所以补丁同时起作用和不起作用?)和“foo”(我想要“foo!”)。我尝试了几十种组合\expandafter
,但还没有找到解决方案。
应@egreg 的要求,这里有一个扩展的、更实用的示例:
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{enumitem}
\usepackage{xpatch}
\makeatletter
\begin{document}
\begin{enumerate}
\item
\edef\label@listctr{label\@listctr}
\meaning\label@listctr
\item
\expandafter\meaning\csname\label@listctr\endcsname
\item
\csname\label@listctr\endcsname
\item
this works now:
\expandafter\xpatchcmd\csname\label@listctr\endcsname{\c@enumi}{\c@enumi'}{}{n}
\csname\label@listctr\endcsname
\item
\edef\c@@listctr{c@\@listctr}
\meaning\c@@listctr
\item
this still doesn't:
\expandafter\xpatchcmd\csname\label@listctr\endcsname{\csname\c@@listctr\endcsname}{\csname\c@@listctr\endcsname!}{y}{n}
\end{enumerate}
\end{document}
问题是命令名称取决于枚举级别,所以我在“现在有效”中所做的在第二个枚举级别上无效。
答案1
从根本上讲,你想要扩展\csname\macroA\endcsname
为类似的东西\macroB
。这很容易,只需一个,\expandafter
如
\expandafter\xpatch\csname\macroA\endcsname
但是,你要将这三个连接成一个序列。虽然这不是问题本身,它大大增加了 s 的数量和位置\expandafter
。此外,每个\csname
...都被...\endcsname
包围,这进一步增加了 s 的数量。由于控制序列本身就是标记,我们将删除周围的...并定义一些辅助宏,以使其更容易。{
}
\expandafter
{
}
根据您的示例,您本质上想要
% This is what you want
\xpatchcmd{\tobepatched}{\tobereplaced}{\tobereplaced!}{yes}{no}
因此让我们使用一些...结构来定义\x
(比如说)be\tobepatched
和\y
become :\tobereplaced
\csname
\endcsname
\expandafter\def\expandafter\x\expandafter{\csname\patchedname\endcsname}
\expandafter\def\expandafter\y\expandafter{\csname\replacedname\endcsname}
.log
我们看到
> \x=macro:
->\tobepatched .
l.15 ...fter{\csname\patchedname\endcsname}\show\x
> \y=macro:
->\tobereplaced .
l.16 ...ter{\csname\replacedname\endcsname}\show\y
现在你的原始\xpatch
构造应该简化为
\xpatchcmd\x\y{\y!}{yes}{no}
对于它我们可以\expandafter
按照以下方式应用 s :让我们展开第一个\x
:
\expandafter
\xpatchcmd\x\y{\y!}{yes}{no}
现在让我们扩展第一个\y
:
\expandafter\expandafter\expandafter
\xpatchcmd\expandafter\x\y{\y!}{yes}{no}
现在让我们扩展第二个\y
:
\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter
\xpatchcmd\expandafter\expandafter\expandafter\x\expandafter\y\expandafter{\y!}{yes}{no}
也许,为了避免所有\expandafter
s 造成视觉混淆,我们可以使用一些辅助扩展宏:
\let\xpX\expandafter % Expansion of \x
\let\xpY\expandafter % Expansion of first \y
\let\xpYY\expandafter% Expansion of second \y
现在纠缠\expandafter
变成了
\xpYY\xpY\xpYY\xpX\xpYY\xpY\xpYY
\xpatchcmd\xpYY\xpY\xpYY\x\xpYY\y\xpYY{\y!}{yes}{no}
我们首先从左到右编写扩展(即,我们要先扩展\x
,然后是第一个\y
,然后是第二个\y
)。由于\expandafter
从“右到左”工作,因此结果是最深的表达式首先被扩展。这在本例中并不那么重要,因为扩展本身就是单个标记。但是,一般来说,如果扩展导致多标记元素,则\expandafter
可能需要更多 s。
以下是一个最简单的例子:
是的
!
\documentclass{article}
\usepackage{xpatch}
\begin{document}
\def\tobereplaced{foo}
\def\tobepatched{\tobereplaced}
\def\patchedname{tobepatched}
\def\replacedname{tobereplaced}
%% This is what you want
%\xpatchcmd{\tobepatched}{\tobereplaced}{\tobereplaced!}{yes}{no}
\expandafter\def\expandafter\x\expandafter{\csname\patchedname\endcsname}
\expandafter\def\expandafter\y\expandafter{\csname\replacedname\endcsname}
\let\xpX\expandafter% Expansion of \x
\let\xpY\expandafter% Expansion of first \y
\let\xpYY\expandafter% Expansion of second \y
\xpYY\xpY\xpYY\xpX\xpYY\xpY\xpYY
\xpatchcmd\xpYY\xpY\xpYY\x\xpYY\y\xpYY{\y!}{yes}{no}
\tobepatched
\end{document}
答案2
我没有尝试整理 expandafter,但看起来您想在枚举过程中更改标签。您可以通过设置适当的键来实现:
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{enumitem}
\usepackage{xpatch}
\makeatletter
\begin{document}
\begin{enumerate}
\item
\item
\item abc
\enitkv@setkeys{enumitem}{label=\arabic{\@listctr}'.,font=\bfseries}
\item
\item
\item
\end{enumerate}
\end{document}
答案3
我的印象是,你把事情复杂化了,而且没有告诉任何人预期的结果是什么。
\documentclass{article}
\usepackage{xpatch}
\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\expandoncename}{m}
{
\exp_not:c { #1 }
}
\DeclareExpandableDocumentCommand{\expandfullname}{m}
{
\use:c { #1 }
}
\NewDocumentCommand{\expxpatchname}{mmmmm}
{
\bers_expxpatch:cxxnn {#1} {#2} {#3} {#4} {#5}
}
\cs_new_protected:Nn \bers_expxpatch:Nnnnn
{
\xpatch_main:NN \patchcmd #1 { #2 } { #3 } { #4 } { #5 }
}
\cs_generate_variant:Nn \bers_expxpatch:Nnnnn { cxx }
\ExplSyntaxOff
\begin{document}
\def\tobereplaced{foo}
\def\tobepatched{\tobereplaced}
\def\patchedname{tobepatched}
\def\replacedname{tobereplaced}
\texttt{\meaning\tobepatched}
\expxpatchname{\patchedname}
{\expandoncename{\replacedname}}
{\expandfullname{\replacedname}!}{\message{yes}}{\message{no}}
\texttt{\meaning\tobepatched}
\end{document}
答案4
这不完全是我问题的答案,它基本上是 @Werner 解释的扩展 - 但如果我有它,我可以轻松地自己回答我的问题。我尝试按照他的配方制作一些更简单和更复杂的例子。我从开始\def\x{\y{\y}\z{\z}}
并尝试用或替换\y
、{\y}
或。这在标准情况下很简单,但随着您需要扩展的变量(、、 )被其名称( 、、 )替换,\z
情况会变得更加复杂。{\z}
\x
\y
\z
\namex
\namey
\namez
我终于学会了如何\expandafter
确定性地使用(即无需试验和尝试各种数字);也许结果可以作为那些还没有学会的人的某种查找表。
我想让我感到惊讶的是,在几个花括号的情况下,例如\xpatchcmd\x{{\y}}{{\z}}{}{err}
,你需要很多个\expandafter
s 才能扩展一个\namez
,基本上每个标记一个,包括{
甚至}
:
\eaz\xpatchcmd\eaz\x\eaz{\eaz{\eaz\y\eaz}\eaz}\eaz{\eaz{\namez}}{}{err}
我现在明白了这一点,但我以前肯定不明白。
\documentclass{article}
\usepackage{xpatch}
\usepackage[T1]{fontenc}
\makeatletter
\def\verbatim@font{\rmfamily}
\makeatother
\newcommand{\testx}{%
\noindent\meaning\x
}
\newcommand{\restorex}{%
\def\x{\y{\y}\z{\z}}%
}
% this is not even needed:
% \def\y{y}
% \def\z{z}
\def\namex{\x}
\def\namey{\y}
\def\namez{\z}
\let\eax\expandafter
\let\eay\expandafter
\let\eaz\expandafter
\begin{document}
~\\All the following shall be identical to\\ \verb|macro:->\z {\y }\z {\z }| \\
% Level 1: no curly braces
\restorex
\xpatchcmd\x\y\z{}{err}
\testx
\restorex
% replace \x by \namex
\eax\xpatchcmd\namex\y\z{}{err}
\testx
\restorex
% replace \y by \namey
\eay\xpatchcmd\eay\x\namey\z{}{err}
\testx
\restorex
% replace \z by \namez
\eaz\xpatchcmd\eaz\x\eaz\y\namez{}{err}
\testx
\restorex
% replace both \x and \y by \namex and \namey
\eay\eax\eay\xpatchcmd\eay\namex\namey\z{}{err}
\testx
\restorex
% replace both \x and \z by \namex and \namez
\eaz\eax\eaz\xpatchcmd\eaz\namex\eaz\y\namez{}{err}
\testx
\restorex
% replace all \x, \y, \z by \namex, \namey, \namez
\eaz\eay\eaz\eax\eaz\eay\eaz\xpatchcmd\eaz\eay\eaz\namex\eaz\namey\namez{}{err}
\testx
% Level 2: curly braces around \z
\restorex
\xpatchcmd\x\y{\z}{}{err}
\testx
\restorex
% replace \x by \namex
\eax\xpatchcmd\namex\y{\z}{}{err}
\testx
\restorex
% replace \y by \namey
\eay\xpatchcmd\eay\x\namey{\z}{}{err}
\testx
\restorex
% replace \z by \namez
\eaz\xpatchcmd\eaz\x\eaz\y\eaz{\namez}{}{err}
\testx
\restorex
% replace both \x and \y by \namex and \namey
\eay\eax\eay\xpatchcmd\eay\namex\namey{\z}{}{err}
\testx
\restorex
% replace both \x and \z by \namex and \namez
\eaz\eax\eaz\xpatchcmd\eaz\namex\eaz\y\eaz{\namez}{}{err}
\testx
\restorex
% replace all \x, \y, \z by \namex, \namey, \namez
\eaz\eay\eaz\eax\eaz\eay\eaz\xpatchcmd\eaz\eay\eaz\namex\eaz\namey\eaz{\namez}{}{err}
\testx
~\\All the following shall be identical to\\ \verb|macro:->{\z }{\y }\z {\z }| \\
% Level 3: two curly braces around \z
\restorex
\xpatchcmd\x\y{{\z}}{}{err}
\testx
\restorex
% replace all \x, \y, \z by \namex, \namey, \namez
\eaz\eay\eaz\eax\eaz\eay\eaz\xpatchcmd\eaz\eay\eaz\namex\eaz\namey\eaz{\eaz{\namez}}{}{err}
\testx
% Level 4: curly braces around \y, two curly braces around \z
\restorex
\xpatchcmd\x{\y}{{\z}}{}{err}
\testx
\restorex
% replace \x by \namex
\eax\xpatchcmd\namex{\y}{{\z}}{}{err}
\testx
\restorex
% replace \y by \namey
\eay\xpatchcmd\eay\x\eay{\namey}{{\z}}{}{err}
\testx
\restorex
% replace \z by \namez
\eaz\xpatchcmd\eaz\x\eaz{\eaz\y\eaz}\eaz{\eaz{\namez}}{}{err}
\testx
\restorex
% replace all \x, \y, \z by \namex, \namey, \namez
\eaz\eay\eaz\eax\eaz\eay\eaz\xpatchcmd\eaz\eay\eaz\namex\eaz\eay\eaz{\eaz\namey\eaz}\eaz{\eaz{\namez}}{}{err}
\testx
~\\All the following shall be identical to\\ \verb|macro:->\y {\z }\z {\z }| \\
% Level 5: two curly braces around \y, two curly braces around \z
\restorex
\xpatchcmd\x{{\y}}{{\z}}{}{err}
\testx
\restorex
% replace \x by \namex
\eax\xpatchcmd\namex{{\y}}{{\z}}{}{err}
\testx
\restorex
% replace \y by \namey
\eay\xpatchcmd\eay\x\eay{\eay{\namey}}{{\z}}{}{err}
\testx
\restorex
% replace \z by \namez
\eaz\xpatchcmd\eaz\x\eaz{\eaz{\eaz\y\eaz}\eaz}\eaz{\eaz{\namez}}{}{err}
\testx
\restorex
% replace all \x, \y, \z by \namex, \namey, \namez
\eaz\eay\eaz\eax\eaz\eay\eaz\xpatchcmd\eaz\eay\eaz\namex\eaz\eay\eaz{\eaz\eay\eaz{\eaz\namey\eaz}\eaz}\eaz{\eaz{\namez}}{}{err}
\testx
\end{document}