¿Cómo definir _o_ redefinir un comando (mezclando \providecommand + \renewcommand)?

¿Cómo definir _o_ redefinir un comando (mezclando \providecommand + \renewcommand)?

En algunas situaciones desearía que hubiera una manera de definir un comando \tmppara que

  • si \tmpno existe: Definido\tmp
  • si \tmpsale: Redefinir\tmp

Mi enfoque actual es simplemente usar \newcommando \renewcommand. Sin embargo, esto significa que a menudo tengo que cambiar de una versión a otra si reordeno mis documentos y, a veces, limita la reutilización general de mi código.

Enesta preguntaAprendí sobre \providecommand, lo que casi resuelve mi problema: se puede usar independientemente de si \tmpestá definido, pero solo se define en la primera aparición y no se sobrescribe. Esto me lleva al ingenuo intento:

\newcommand{\overwritecommand}[2]{
  \providecommand{#1}{#2}
  \renewcommand{#1}{#2}
}

Sin embargo, este enfoque obviamente no es lo suficientemente general:

% it works for
\overwritecommand{\tmp}{test}

% but not for commands with arguments like
\overwritecommand{\tmp}[1]{test: #1} 
% Error: You can't use `macro parameter character #' in horizontal mode.

¿Hay alguna otra manera de lograr eldefinir o sobrescribir¿comportamiento?

Respuesta1

Sí, existe un método:

\newcommand{\declarecommand}[1]{\providecommand{#1}{}\renewcommand{#1}}

¿Por qué funciona? Porque TeX usa expansión macro y es irrelevante lo que \providecommanddefine #1ser, si #1no se definió, porque lo redefines inmediatamente.

Ahora que sabes cómo hacerlo, prueba

\declarecommand{\box}[1]{\fbox{#1}}

¡Y disfruta del naufragio!

Alláesuna razón por la cual LaTeX no proporciona una \declarecommandfunción: ustedDEBEtenga en cuenta si está redefiniendo un comando existente.

Si desea permitir el opcional *, entonces

\makeatletter
\newcommand\declarecommand{\@star@or@long\@declarecommand}
\newcommand\@declarecommand[1]{%
  \provide@command{#1}{}%
  \renew@command{#1}%
}
\makeatother

servirá.

Respuesta2

Entiendo que lo necesitas \defpero con parámetros como \newcommand. Es posible definir el nuestro\newcommand , que ignora si la secuencia de control definida tiene un significado. Por ejemplo podemos usar el código deesta página:

\def\newcommand#1{\isnextchar[{\newcommandA#1}{\newcommandA#1[0]}}
\def\newcommandA#1[#2]{\edef\tmpp{\ifcase#2%
   \or1\or12\or123\or1234\or12345\or123456\or1234567\or12345678\or123456789\fi}%
   \edef\tmpp{\expandafter\addhashs\tmpp.}%
   \isnextchar[{\newcommandB#1}{\long\expandafter\def\expandafter#1\tmpp}%
}
\def\newcommandB#1[#2]{%
   \def#1{\isnextchar[{\runcommand#1}{\runcommand#1[#2]}}%
   \long\expandafter\def\csname\string#1X\expandafter\endcsname\tmpp
}
\def\addhashs#1{\ifx.#1\else #####1\expandafter\addhashs\fi}
\long\def\runcommand#1[#2]{\csname\string#1X\endcsname{#2}} 

Respuesta3

Como ya se han mostrado los ejemplos clásicos, simplemente agregaré la xparseforma, que es bastante fácil de usar:

\documentclass{article}
\usepackage{xparse}
\DeclareDocumentCommand{\foo}{m}
 {Foo: #1}
\begin{document}

\foo{bar}

\DeclareDocumentCommand{\foo}{om}
 {\IfNoValueTF{#1}%
   {Bar: no optional, just #2}%
   {Bar: optional = #1, plus #2}%
 }%

\foo{Baz}

\foo[Bar]{Baz}

\end{document}

información relacionada