(x)修补名称在另一个宏中的命令

(x)修补名称在另一个宏中的命令

我想根据当前级别修补编号列表中的某些命令(enumienumii、...)。基本上,我想用名称存储在宏中的命令替换名称存储在另一个宏中的宏。这总结了我的问题:

\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\ybecome :\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}

也许,为了避免所有\expandafters 造成视觉混淆,我们可以使用一些辅助扩展宏:

\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},你需要很多个\expandafters 才能扩展一个\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}

相关内容