3

I want to patch some command in a numbered list based on the current level (enumi, enumii, ...). Basically, I want to replace, in a command the name of which is stored in a macro, a macro the name of which is stored in another macro. This summarizes my problem:

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

The patch is not effective. Interestingly, the document output is "yesno" (so the patch worked and did not, at the same time?), and "foo" (I would like "foo!"). I have tried dozens of combinations of \expandafter, but have not found a solution.

At @egreg's request, here is an extended, more applied example:

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

The problem is that the command name depends on the enumeration level, so what I did in the "it works now" is not valid on a second enumeration level.

  • 1
    \expandafter\xpatchcmd\csname\patchedname\endcsname{search}{replace}{true}{false} But you can't use macros for the search and replace part: they're not expanded at all. – egreg Nov 23 '16 at 16:42
  • @egreg Wow, so my problem in the first argument was the extra {...} around \csname. With fixed search and replace, this works. How do I apply this to my MWE, with search and replace other variable macros? – bers Nov 23 '16 at 16:47
  • No, you also missed \expandafter. I see no reason for doing something like that in the search and replace parts. And also for the first argument, actually. – egreg Nov 23 '16 at 16:51
  • @egreg Yes, as I have written, "I have tried dozens of combinations of \expandafter" - the one that you suggested was among them, yet with an extra pair of {...}. Anyway - maybe you could enlighten me why you see no reason to do what I am doing? Although I might also be interested in a solution to the direct problem, understanding why you think it can be done differently may help, too! – bers Nov 23 '16 at 16:54
  • I can't explain what I don't understand. And a reason for such a complicated approach is very difficult to grasp. You should explain what you're for. – egreg Nov 23 '16 at 16:59
4

Fundamentally you want to expand \csname\macroA\endcsname to something like \macroB. This is easy enough with a single \expandafter as in

\expandafter\xpatch\csname\macroA\endcsname

However, you concatenate three of these in a sequence. While this is not a problem per se, it drastically increases the amount and placement of the \expandafters. Additionally, you have each \csname...\endcsname surrounded by {...} which further increases the number of \expandafters. Since control sequences themselves are tokens, we are going to remove the surrounding {...} and also define some helper macros, just to make it easier.

Based on your example, you essentially want

% This is what you want
\xpatchcmd{\tobepatched}{\tobereplaced}{\tobereplaced!}{yes}{no}

So let's define \x (say) to be \tobepatched and \y to become \tobereplaced using some \csname...\endcsname constructions:

\expandafter\def\expandafter\x\expandafter{\csname\patchedname\endcsname}
\expandafter\def\expandafter\y\expandafter{\csname\replacedname\endcsname}

We see in the .log that

> \x=macro:
->\tobepatched .
l.15 ...fter{\csname\patchedname\endcsname}\show\x


> \y=macro:
->\tobereplaced .
l.16 ...ter{\csname\replacedname\endcsname}\show\y

Now your original \xpatch construction should simplify to

\xpatchcmd\x\y{\y!}{yes}{no}

for which we can apply \expandafters in the following way: Let's expand the first \x:

\expandafter
  \xpatchcmd\x\y{\y!}{yes}{no}

Now let's expand the first \y:

\expandafter\expandafter\expandafter
  \xpatchcmd\expandafter\x\y{\y!}{yes}{no}

Now let's expand the second \y:

 \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter
  \xpatchcmd\expandafter\expandafter\expandafter\x\expandafter\y\expandafter{\y!}{yes}{no}

Perhaps, to avoid visual confusion with all the \expandafters, we can use some helper expansion macros:

\let\xpX\expandafter % Expansion of \x
\let\xpY\expandafter % Expansion of first \y
\let\xpYY\expandafter% Expansion of second \y

Now the entangled \expandafters become

\xpYY\xpY\xpYY\xpX\xpYY\xpY\xpYY
  \xpatchcmd\xpYY\xpY\xpYY\x\xpYY\y\xpYY{\y!}{yes}{no}

We start by writing the expansions from left-to-right (that is, we want to expand \x first, then the first \y, then the second \y). Since \expandafter works from "right-to-left" the result is that the deepest expression is expanded first. This is not all that important in this example as the expansions are single-tokens themselves. However, in general, if the expansion results in multi-token elements, more \expandafters may be required.

Here is the minimal example:

yes
foo!

\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}
  • This works! And it looks very much like the 11=7+3+1 calculation discussed here: tex.stackexchange.com/questions/7703/… – bers Nov 23 '16 at 17:37
  • Your explanation is very good, thank you! Just to make sure I understood it, I am trying to apply the same scheme to \xpatchcmd\x{\y}{\y!}{yes}{no} (note the {...} around the first \y). I have observed that basically, you start from left to right, expanding one item after the other, and existing \relaxafters are duplicated in each step according to the 2^n-1 rule (1 --> 3 --> 7 --> ...). The problem that I have following that scheme is the \expandafter than appears between \x and the FIRST \y when you expand the SECOND \y. Where does that come from? – bers Nov 23 '16 at 17:51
  • Oh, I see: This expands {...} to .... – bers Nov 23 '16 at 17:57
  • 1
    @bers: I've added some more \expandafter explanation. – Werner Nov 23 '16 at 19:03
4

I didn't try to sort out the expandafter, but it looks like you want to change the label in the mid of an enumerate. You could do it by setting the appropriate keys:

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

enter image description here

  • You are right, I am trying something along those lines. I haven't tried your idea yet, but I wonder whether it works with \ref, hyperref, cleveref, and/or without enumitem. – bers Nov 24 '16 at 0:14
  • It won't work without enumitem as it uses its interface to set labels. – Ulrike Fischer Nov 24 '16 at 7:44
3

My impression is that you're overcomplicating things, besides not telling anyone what the expected result should be.

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

enter image description here

  • Thanks! I see where the confusion is coming from. Yes, I wanted foo! in the output, but by means of \tobereplaced!. But that may not make much a difference - your code should work, too. I assume my variant would just use \expandoncename twice. – bers Nov 24 '16 at 0:18
  • @bers Exactly. I was uncertain about it, so I showed both possibilities. – egreg Nov 24 '16 at 7:03
0

This is not exactly an answer to my question, it's basically an extension of @Werner's explanations - but if I had had it, I could have easily answered my question myself. I tried to follow his recipe for a number of simpler and more complicated examples. I started with \def\x{\y{\y}\z{\z}} and tried to replace \y, or {\y}, by \z or {\z}. This is simple in the standard case, but gets more complicated the more variables (\x, \y, \z) you replace by their names (\namex, \namey, \namez) that you need to expand.

I have finally learnt how to use \expandafter deterministically (that is, without experimenting and trying out various numbers); maybe the results can serve as some kind of look-up table for those who haven't yet.

I guess what surprised me was that in the case of a few curly braces, such as \xpatchcmd\x{{\y}}{{\z}}{}{err}, you need a LOT of \expandafters to just expand a single \namez, basically one for every token, including { and even }:

\eaz\xpatchcmd\eaz\x\eaz{\eaz{\eaz\y\eaz}\eaz}\eaz{\eaz{\namez}}{}{err}

I have understood that now, but I certainly had not before.

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.