![Как определить или переопределить команду (смешивая \providecommand + \renewcommand)?](https://rvso.com/image/305894/%D0%9A%D0%B0%D0%BA%20%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B8%D1%82%D1%8C%20%D0%B8%D0%BB%D0%B8%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B8%D1%82%D1%8C%20%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D1%83%20(%D1%81%D0%BC%D0%B5%D1%88%D0%B8%D0%B2%D0%B0%D1%8F%20%5Cprovidecommand%20%2B%20%5Crenewcommand)%3F.png)
В некоторых ситуациях мне хотелось бы иметь возможность определить команду \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}