如何通过提供可选参数来切换宏扩展?

如何通过提供可选参数来切换宏扩展?

定义带有可选参数的宏的常用方法是使用 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} 

在此处输入图片描述

相关内容