Как определить или переопределить команду (смешивая \providecommand + \renewcommand)?

Как определить или переопределить команду (смешивая \providecommand + \renewcommand)?

В некоторых ситуациях мне хотелось бы иметь возможность определить команду \tmpтак, чтобы

  • если \tmpне существует: Определено\tmp
  • если \tmpделает выходы: переопределить\tmp

Мой текущий подход заключается в том, чтобы просто использовать либо , \newcommandлибо \renewcommand. Однако это означает, что мне часто приходится переходить с одной версии на другую, если я меняю порядок своих документов, а иногда ограничивает общую возможность повторного использования моего кода.

Вэтот вопросЯ узнал о \providecommand, что почти решает мою проблему: его можно использовать независимо от того, \tmpопределено ли оно, но оно определяет только при первом появлении и не перезаписывает. Это привело меня к наивной попытке:

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

Однако этот подход явно недостаточно универсален:

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

Есть ли другой способ достичьопределить или перезаписатьповедение?

решение1

Да, есть такой метод:

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

Почему это работает? Потому что TeX использует макрорасширение и неважно, что именно \providecommandопределяется #1, если #1оно не было определено, потому что вы немедленно его переопределяете.

Теперь, когда вы знаете, как это сделать, попробуйте

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

и наслаждайтесь крушением!

Тамявляетсяпричина, по которой LaTeX не предоставляет функцию \declarecommand: выДОЛЖЕНбудьте внимательны, переопределяете ли вы существующую команду.

Если вы хотите разрешить необязательный параметр *, то

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

Сделаю.

решение2

Я понимаю, что вам нужно, \defно с параметрами типа \newcommand. Можно определить свой собственный \newcommand, который игнорирует, если определенная последовательность управления имеет значение. Например, мы можем использовать код изэта страница:

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

решение3

Поскольку классические примеры уже были показаны, я просто добавлю способ xparse, который весьма удобен для пользователя:

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

Связанный контент