在某些情况下,我希望有一种方法可以定义一个命令,\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}