定义带有可选参数的宏的常用方法是使用 TeX\def
和\@ifnextchar[
/或 LaTeX 的\newcommand
。在这两种情况下,宏的定义通常都包含其可选参数。
现在,我想使用可选参数作为决定宏如何扩展的一种连接点。选项本身并未在宏的定义中明确使用。
举一个简单的例子,我先定义\conjTrans
,它用三种符号来表示矩阵的共轭转置。 的规范\conjTrans
如下:
\documentclass{standalone}
\usepackage{conjTrans} % \conjTrans is defined here
\begin{document}
$\conjTrans{A}$,
$\conjTrans[asterisk]{B}$,
$\conjTrans[Hermite]{C}$, and
$\conjTrans[dagger]{D}$
\end{document}
将产生
如果dager
给出了无效选项(例如),则会发生类似的错误the option is incorrect
。
我首先尝试使用\ifx
或其他条件分支,但是它与我的能力不符,因为选项不止一个标记而且我不知道它的解决方案。
我\conjTrans
用下面的代码实现的。
% conjTrans.sty
\ProvidesPackage{conjTrans}[2016/01/05 my first question on TeX.SX]
\RequirePackage{amsmath}
\def\conjTrans{\@ifnextchar[\nkt@conjTrans{\nkt@conjTrans[asterisk]}}
\def\nkt@conjTrans[#1]#2{%
\expandafter\ifx\csname nkt@conjTrans@#1\endcsname\relax
\PackageError{conjTrans}{The option #1 is unknown to \string\conjTrans}{Optional argument for \string\conjTrans\space must be either ast, H, or dagger.}%
\else
\csname nkt@conjTrans@#1\endcsname#2%
\fi
}
\def\nkt@conjTrans@asterisk#1{\boldsymbol#1^{\ast}}
\def\nkt@conjTrans@Hermite#1{\boldsymbol#1^{\text{H}}}
\def\nkt@conjTrans@dagger#1{\boldsymbol#1^{\dagger}}
这\conjTrans
似乎很有效,但我想知道是否有一种方法比我的方法更复杂、更安全或在某些方面更好。 有没有标准的“习惯用法”以这种方式使用可选参数?
答案1
在这种情况下,我认为键值方法是最好的方法:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\conjTrans}{O{asterisk}m}
{
\yudai_conj_trans:nn { #1 } { #2 }
}
\keys_define:nn { yudai/conjtrans }
{
asterisk .code:n = \tl_set:Nn \l_yudai_conj_trans_symbol { * },
Hermite .code:n = \tl_set:Nn \l_yudai_conj_trans_symbol { \mathrm{H} },
dagger .code:n = \tl_set:Nn \l_yudai_conj_trans_symbol { \dagger },
asterisk .value_forbidden:n = true,
Hermite .value_forbidden:n = true,
dagger .value_forbidden:n = true,
unknown .code:n = \msg_error:nnx { yudai/conjtrans } { bad-option } { \l_keys_key_tl }
\tl_set:Nn \l_yudai_conj_trans_symbol { ??? },
}
\msg_new:nnnn { yudai/conjtrans } { bad-option }
{
Bad~option~#1
}
{
You~used~#1,~which~is~not~among~the~predefined~options
}
\tl_new:N \l_yudai_conj_trans_symbol
\cs_new_protected:Nn \yudai_conj_trans:nn
{
\keys_set:nn { yudai/conjtrans } { #1 }
#2^{\l_yudai_conj_trans_symbol}
}
\ExplSyntaxOff
\begin{document}
$\conjTrans{A}$,
$\conjTrans[asterisk]{B}$,
$\conjTrans[Hermite]{C}$,
and $\conjTrans[dagger]{D}$
Error: $\conjTrans[foo]{E}$
\end{document}
这很容易扩展。
一个“经典”的实现:
\documentclass{article}
\newcommand{\conjTrans}[2][asterisk]{#2\conjTransAppend{#1}}
\newcommand{\conjTransAppend}[1]{%
\ifcsname conjTrans#1\endcsname
^{\csname conjTrans#1\endcsname}
\else
\PackageError{conjTrans}{Invalid option #1}
{The option #1 is not among the predefined ones}%
^{???}%
\fi
}
\newcommand{\conjTransasterisk}{*}
\newcommand{\conjTransHermite}{\mathrm{H}}
\newcommand{\conjTransdagger}{\dagger}
\begin{document}
$\conjTrans{A}$,
$\conjTrans[asterisk]{B}$,
$\conjTrans[Hermite]{C}$,
and $\conjTrans[dagger]{D}$
Error: $\conjTrans[foo]{E}$
\end{document}
答案2
\documentclass[a4paper]{article}
\usepackage{filecontents}
\begin{filecontents*}{conjTrans.sty}
\ProvidesPackage{conjTrans}[2016/01/05 my first question on TeX.SX]
\RequirePackage{amsmath}
\RequirePackage{xkeyval}
\define@choicekey*+{conjTrans}{symbol}[\val\nr]{,asterisk,Hermite,dagger}%
{\ensuremath{\ifcase\nr\relax
\cTB@se^*\or\cTB@se^\text{H}\or\cTB@se^{\dagger}\fi}}
{\PackageWarning{conjTrans}{erroneous input ignored}}
\newcommand\conjTrans[2][asterisk]{\def\cTB@se{\boldsymbol{#2}}\setkeys{conjTrans}{symbol=#1}}
\endinput
\end{filecontents*}
\usepackage{conjTrans}
\begin{document}
\conjTrans{A}, \conjTrans[asterisk]{B}, \conjTrans[Hermite]{C}, and
\conjTrans[dagger]{D}
\conjTrans[whatever]{A}%% For an error message in the log file
\end{document}