\pretocmd e argomenti opzionali

  • Creatore
    Topic
  • #64098
    Up
    0
    Down
    ::


    Perché questo codice
    `
    \documentclass{article}
    \usepackage{etoolbox}

    \newenvironment{ambiente}[2]{%
    \hrule\textit{#1, #2}%
    }{}

    \pretocmd{\ambiente}{%
    \textbf{#1, #2}%
    }\relax\relax

    \begin{document}
    \begin{ambiente}{primo}{secondo}
    \end{ambiente}
    \end{document}
    `
    funziona, mentre quest’altro
    `
    \documentclass{article}
    \usepackage{etoolbox}

    \newenvironment{ambiente}[2][]{%
    \hrule\textit{#1, #2}%
    }{}

    \pretocmd{\ambiente}{%
    \textbf{#1, #2}%
    }\relax\relax

    \begin{document}
    \begin{ambiente}[primo]{secondo}
    \end{ambiente}
    \end{document}
    `
    no va?

    E soprattutto, esiste un modo per farlo funzionare senza usare i soliti \let\old…=…?
    Vorrei usare strumenti forniti da etoolbox o simili.

    Grazie,
    Antonio

Visualizzazione 4 filoni di risposte
  • Autore
    Risposte
    • #64099
      Up
      0
      Down
      ::


      Mi sono appena reso conto che non funziona nemmeno con \let\old<...>=<...> e \renewcommand. ❗

      `
      \documentclass{article}
      \usepackage{etoolbox}

      \newenvironment{ambiente}[2][]{%
      \hrule\textit{#1, #2}%
      }{}

      \let\oldambiente\ambiente
      \renewcommand{\ambiente}[2][]{%
      \textbf{#1, #2}%
      \oldambiente[#1]{#2}
      }

      \begin{document}
      \begin{ambiente}[primo]{secondo}
      \end{ambiente}
      \end{document}
      `
      Dà errore di TeX capacity exceeded. Questo mi spiazza non poco, ma posso ammetterlo per via del funzionamento interno di \renewcommand (che non ho capito).

      L’unica soluzione è la seguente?
      `
      \documentclass{article}
      \usepackage{etoolbox}

      \newenvironment{ambiente}[2][]{%
      \hrule\textit{#1, #2}%
      }{}

      \makeatletter
      \let\oldambiente\ambiente
      \def\ambiente{%
      \@ifnextchar[{\ambiente@o}{\ambiente@o[]}%
      }
      \def\ambiente@o[#1]#2{%
      \textbf{#1, #2}%
      \oldambiente[#1]{#2}%
      }
      \makeatother

      \begin{document}
      \begin{ambiente}[primo]{secondo}
      \end{ambiente}
      \end{document}
      `

    • #64100
      Up
      0
      Down
      ::


      \pretocmd, \apptocmd e \patchcmd non funzionano sui comandi con argomento facoltativo, né sui comandi definiti con \DeclareRobustCommand. Il motivo è un po’ riposto, ma non difficile. Supponiamo di avere
      `\newcommand{\pippo}[2][noccioline]{…}
      \DeclareRobustCommand{\pluto}[1]{…}
      \DeclareRobustCommand{\paperino}[2][313]{…}`
      in ordine crescente di difficoltà. 🙂 Indico con \Xcmd una qualsiasi delle funzioni di etoolbox.

      Nel primo caso il comando che fa il lavoro vero è “\\pippo”, nel senso che il nome del comando è “\pippo” con la barra rovescia in quanto carattere. Dare \Xcmd{\pippo}{…}{}{} non servirebbe a nulla e infatti etoolbox si rifiuta. Occorre agire sul comando “vero”:
      `\expandafter\Xcmd\csname\string\pippo\endcsname{…}{}{}`
      funzionerà perfettamente, perché \csname\string\pippo\endcsname costruisce l’esatto nome che ci serve.

      Secondo caso. Il comando che fa il lavoro vero è “\pluto “, nel senso che il nome del comando ha uno spazio in fondo che fa parte integrante del nome. Soluzione:
      `\expandafter\Xcmd\csname pluto \endcsname{…}{}{}`
      perché \csname pluto \endcsname produce il nome corretto.

      Terzo caso, cioè la somma dei precedenti: il comando su cui agire è “\\paperino “; qui la situazione è un pochino più complessa, perché
      `\csname\string\paperino \endcsname`
      non produce il nome corretto: lo spazio dopo “\paperino” è ignorato! Dunque
      `\expandafter\Xcmd\csname\string\paperino\space\endcsname{…}{}{}`
      Nel tuo caso devi usare
      `\expandafter\pretocmd\csname\string\ambiente\endcsname{\textbf{#1, #2}}{}{}`

      Codice un po’ più generale

      Qui c’è una definizione di \xpatchcmd, \xpretocmd e \xapptocmd che fanno la stessa cosa degli originali, ma si possono applicare a comandi definiti con argomento facoltativo oppure costruiti con \DeclareRobustCommand e anche con \newrobustcmd.
      `\documentclass[a4paper]{article}
      \usepackage{etoolbox}

      \makeatletter
      \def\@X@cmd#1#2{%
      %% Start from scratch: define \@tempc to expand to the candidate command
      \def\@tempc{#2}%
      %% Analyze whether #2 is defined via \DeclareRobustCommand; if it is, and
      %% the command is called \abc, its \meaning is the string “\protect \abc ”
      %% (where spaces are significant)
      \edef\@tempa{\expandafter\strip@prefix\meaning#2}%
      \edef\@tempb{\string\protect\space\string#2\space\space}%
      %% If the two strings are equal, the command was defined by \DeclareRobustCommand
      %% and was a control sequence
      \ifx\@tempa\@tempb
      \message{\string#2 is a LaTeX robust control sequence}%
      \expandafter\@robusttempc#2%
      \fi
      %% In case of \DeclareRobustCommand{\?}{…}, the first level expansion is
      %% different: “\x@protect \?\protect \? ”
      \edef\@tempb{\string\x@protect\space\string#2\string\protect\space\string#2\space\space}
      \ifx\@tempa\@tempb
      \message{\string#2 is a LaTeX robust control symbol}%
      \expandafter\@xrobusttempc#2%
      \fi
      %% In any case, \@tempc expands to the candidate for patching; we need one more
      %% passage if the command has optional arguments; if it has, its first
      %% level expansion is “\@protected@testopt \abc \\abc {}” (where \abc stands for
      %% the command chosen before, which might have final spaces). Since it is the
      %% expansion of \@tempc we get its first level expansion in two steps.
      \edef\@tempa{\expandafter\meaning\@tempc}%
      \edef\@tempa{\expandafter\strip@prefix\@tempa}%
      \edef\@tempb{\string\@protected@testopt\space\string#2\space\space
      \@backslashchar\string#2\space\space\string{\string}}
      %% If the two strings in \@tempa and \@tempb are equal, the command has an
      %% optional argument and was defined with \newcommand.
      \ifx\@tempa\@tempb
      \message{\string#2 has an optional argument}%
      \expandafter\expandafter\expandafter\@optionaltempc\@tempc
      \fi
      %% Commands with an optional argument defined with \newrobustcmd have a different
      %% first level expansion: “\@testopt \\abc {}”
      \edef\@tempb{\string\@testopt\space\@backslashchar\string#2\space\string{\string}}
      \ifx\@tempa\@tempb
      \message{\string#2 has an optional argument}%
      \expandafter\expandafter\expandafter\@eoptionaltempc\@tempc
      \fi
      %% Finally we do the patch (#1 will be either \patchcmd, \pretocmd or \apptocmd
      \expandafter#1\@tempc
      }
      %% In case of a control sequence defined with \DeclareRobustCommand,
      %% the patchable form is the second token in the first level expansion
      \def\@robusttempc#1#2{\def\@tempc{#2}}
      %% In case of a control symbol defined with \DeclareRobustCommand,
      %% the patchable form is the fourth token in the first level expansion
      \def\@xrobusttempc#1#2#3#4{\def\@tempc{#4}}
      %% In case of a control sequence with an optional argument (traditional
      %% LaTeX) the patchable form is the third token in the first level expansion
      \def\@optionaltempc#1#2#3#4{\def\@tempc{#3}}
      %% In case of a control sequence with an optional argument (\newrobustcmd)
      %% the patchable form is the second token in the first level expansion
      \def\@eoptionaltempc#1#2#3{\def\@tempc{#2}}

      \def\xpatchcmd{\@X@cmd\patchcmd}
      \def\xpretocmd{\@X@cmd\pretocmd}
      \def\xapptocmd{\@X@cmd\apptocmd}

      %% Now we define some test commands

      \DeclareRobustCommand{\abc}{abc}
      \DeclareRobustCommand{\abcd}[1][]{abcd}
      \newrobustcmd{\abcde}[2][]{abcde}
      \DeclareRobustCommand\?[1][]{abc?}
      \DeclareRobustCommand{\xyz}{abc}
      \expandafter\def\expandafter\cheat\expandafter{\xyz}

      \tracingpatches
      \xpatchcmd\abc{ab}{AB}{}{}
      \xpatchcmd\abcd{ab}{AB}{}{}
      \xpatchcmd\abcde{ab}{AB}{}{}
      \xpatchcmd\?{ab}{AB}{}{}

      \xpatchcmd\cheat{ab}{AB}{}{}

      \begin{document}
      We should get “AB'' instead of “ab'' except for \verb|\cheat|

      \verb|\abc|: \abc

      \verb|\abcd[x]{y}|: \abcd[x]{y}

      \verb|\abcde|: \abcde{xxx}

      \verb|\?|: \?

      \xapptocmd\?{PIPPO}{}{}

      \verb|\?|: \?

      \verb|\cheat|: \cheat
      \end{document}`
      Non l’ho scritto adesso, sia chiaro. 🙂

      Ciao
      Enrico

    • #64101
      Up
      0
      Down
      ::


      Grazie della risposta!
      Domani ci ragiono su e cerco di indagare meglio.

      Per ora solo una breve precisazione. Mi sembra di capire che col tuo codice basterebbe fare:
      `
      \documentclass[a4paper]{article}
      \usepackage{etoolbox}

      \makeatletter

      \def\xpatchcmd{\@X@cmd\patchcmd}
      \def\xpretocmd{\@X@cmd\pretocmd}
      \def\xapptocmd{\@X@cmd\apptocmd}
      \makeatother

      \newenvironment{ambiente}[2][]{%
      \hrule\textit{#1, #2}%
      }{}

      \xpretocmd{\ambiente}{%
      \textbf{#1, #2}%
      }\relax\relax

      \begin{document}
      \begin{ambiente}[primo]{secondo}
      \end{ambiente}
      \end{document}
      `
      (sostituire cioè \pretocmd col comando \xpretocmd da te definito) per risolvere. Però mi pare che non funzioni 🙁

      Stampa ancora “#1, #2”. E nel log non viene scritto “\ambiente has an optional argument”, come mi aspetterei, ma posso sbagliare perché non cosa usa internamente \newenvironment (quando definisce il nuovo comando di apertura dell’ambiente, cioè \ambiente in questo caso).

      Ciao,
      Antonio

    • #64102
      Up
      0
      Down
      ::


      Il metodo rapido (con \expandafter e \csname) mi funziona, grazie!

    • #64103
      Up
      0
      Down
      ::


      C’è in effetti un problema con i comandi con argomento facoltativo. Riguardo.

      Ciao
      Enrico

Visualizzazione 4 filoni di risposte
  • Devi essere connesso per rispondere a questo topic.

Go to top