Como definir _ou_ redefinir um comando (misturando \providecommand + \renewcommand)?

Como definir _ou_ redefinir um comando (misturando \providecommand + \renewcommand)?

Em algumas situações eu gostaria que houvesse uma maneira de definir um comando \tmppara que

  • se \tmpnão existir: Definido\tmp
  • if \tmpsai: Redefinir\tmp

Minha abordagem atual é simplesmente usar \newcommandou \renewcommand. No entanto, isso significa que muitas vezes terei que mudar de uma versão para outra se reordenar meus documentos e, às vezes, limitar a capacidade de reutilização geral do meu código.

Emessa questãoAprendi sobre \providecommand, o que quase resolve meu problema: pode ser usado independentemente de estar \tmpdefinido, mas só define na primeira ocorrência e não sobrescreve. Isso me levou à tentativa ingênua:

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

No entanto, esta abordagem obviamente não é suficientemente geral:

% 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.

Existe alguma outra maneira de conseguir odefinir ou substituircomportamento?

Responder1

Sim, existe um método:

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

Por que isso funciona? Porque o TeX usa expansão macro e é irrelevante o que \providecommanddefine #1ser, se #1não foi definido, porque você o redefine imediatamente.

Agora que você sabe como fazer, experimente

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

e aproveite o naufrágio!

éuma razão pela qual o LaTeX não fornece uma \declarecommandfunção: vocêDEVEesteja ciente se você está redefinindo um comando existente.

Se você quiser permitir o opcional *, então

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

vai fazer.

Responder2

Eu entendo que você precisa \def, mas com parâmetros como \newcommand. É possível definir o nosso próprio \newcommandque ignora se a sequência de controle definida tem um significado. Por exemplo, podemos usar o 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}} 

Responder3

Como os exemplos clássicos já foram mostrados, vou apenas acrescentar o xparsecaminho, que é bastante amigável:

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

informação relacionada