在某些情況下,我希望有一種方法來定義命令,\tmp
以便
- 如果
\tmp
不存在:已定義\tmp
- 如果
\tmp
退出:重新定義\tmp
我目前的方法只是使用\newcommand
or \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}