据我所知,定义宏的方式至少有三种主流。
原始的 TeX 方法:我们使用\def
命令(和类似的命令)来定义宏。一个非常有吸引力的功能是能够自定义参数解析。当我们写入时,
\def\mymacro#1.#2-(#3){<macro definition>}
我们知道第一个参数后面必须跟一个点,第二个参数后面必须跟一个破折号,而第三个参数必须括在括号中。
在这种方法中,是否可以直接将参数声明为可选?
LaTeX 方法:我们使用\newcommand
(和\renewcommand
)来定义(和重新定义)宏。可以声明可选参数,但我并不认为这是直截了当的。
如果有多个可选参数怎么办?如果在强制参数中分散了几个可选参数怎么办?是否可以控制参数的解析?
必须使用 \renewcommand
防止意外重新定义现有的定义是一个很好的功能。
\NewDocumentCommand
使用
xparse
:看起来像是最新的。我可以以简单的方式混合强制和可选参数,这对我来说特别有吸引力。附带许多其他功能。
我写这篇文章是想请那些真正经验丰富的人来指导我们这三种主流的优缺点。如果只想坚持一种,我们应该选择哪一种?还是我们继续在同一个文档中使用它们,并使用适合我当前需要的那个?
(虽然这篇文章中有几个以问号结尾的句子,但这并不是一篇包含多个问题的文章。相反,这些是回答者在创建教程时会发现有用的谈话要点。)
答案1
\newcommand
使用结构获取多个可选参数的方法是让被调用的宏使用辅助宏来吸收额外的可选参数。
\documentclass{article}
\usepackage[T1]{fontenc}
\newcommand\mymacro[2][\Humpty]{\def\tmpA{#1}\def\tmpB{#2}\mymacroaux}
\newcommand\mymacroaux[2][\Dumpty]{%
\detokenize\expandafter{\tmpA}
\detokenize\expandafter{\tmpB}
\detokenize{#1}
\detokenize{#2}.%
}
\begin{document}
\mymacro{\Charles}{did \relax}
\mymacro[Fat]{\old}{ate \cake}
\mymacro{\Dumpty}[\fell\&]{broke his \crown}
\mymacro[My]{\mom}[is the]{\greatest!}
\end{document}
至于通过执行可选参数的方式\def
,标准方法是使用\futurelet
,如第 277 页所述https://www.tug.org/TUGboat/tb09-3/tb22bechtolsheim.pdf, 是
\documentclass{article}
\begin{document}
\def\xxWithOpt[#1]#2{Optional argument #1, Mandatory argument #2}
\def\xxNoOpt#1{No optional argument, mandatory argument #1}
\def\xx {%
\futurelet\xxLookedAtToken\xxDecide
}
\def\xxDecide {%
\ifx\xxLookedAtToken [%
\let\next = \xxWithOpt
\else
\let\next = \xxNoOpt
\fi
\next
}
\xx{A}
\xx[a]{A}
\end{document}
该xparse
方法在https://tug.org/TUGboat/tb31-1/tb97wright-xparse.pdf,作者:Joseph。使用此方法指定参数的方法有很多种,这里我仅展示一种,类似于第一个例子:
\documentclass{article}
\usepackage{xparse}
\DeclareDocumentCommand \foo
{ o +m o +m } {%
% Four args, #1, #2, #3 and #4
% Only #2 and #4 can include \par tokens
\IfNoValueTF{#1}{%
a%
}{%
#1%
}%
.#2.%
\IfNoValueTF{#3}{%
c%
}{%
#3%
}%
.#4%
}
\begin{document}
\foo{B}{D}
\foo[A]{B}{D}
\foo{B}[C]{D}
\foo[A]{B}[C]{D}
\end{document}
我自己倾向于不使用该xparse
方法,只是因为它对我来说不太熟悉;但是,我认为现在和将来的 LaTeX 学生将从一开始就学习这种方法,因为它无疑是最强大和最灵活的。正如 OP 所提到的,\newcommand
检查宏名称是否预先存在的功能非常有用。我并不总是使用\def
,但当我使用时,通常是出于以下两个原因之一:1) 需要对参数进行唯一解析,如 所提供\def
,或 2) 我太懒了,不愿意输入 的 10 个字母\newcommand
而不是 的 3 个字母\def
。