定义具有可变数量参数的宏

定义具有可变数量参数的宏

我想定义一个宏,该宏定义一系列其他宏,每个宏都接受可变数量的参数(但在任何给定文档中都是固定的)。我知道我可以使用类似下面的方法来实现这一点:

\documentclass{standalone}
\newcommand\MakeCommands[1]{%
  \ifcase#1\relax%
  \or\newcommand\MyCommand[1]{##1}%
  \or\newcommand\MyCommand[2]{##1, ##2}%
  \or\newcommand\MyCommand[3]{##1, ##2, ##3}%
  \or\newcommand\MyCommand[3]{##1, ##2, ##3, ##4}%
  \fi%
}
\begin{document}
  \MakeCommands{3}
  \MyCommand{one}{two}{three}
\end{document}

但我确信有更好的方法可以做到这一点...我觉得是时候学会使用钥匙了......

有谁有更好的解决方案吗?

答案1

我建议创建一些键,用户可以决定是否使用。这也允许他们自由地以他们希望的顺序指定其中的一部分。这是一个使用xkeyval

在此处输入图片描述

\documentclass{article}
\usepackage{xkeyval}
\makeatletter
% ========= KEY DEFINITIONS =========
\define@cmdkey{mycmd}{one}[one]{}
\define@cmdkey{mycmd}{two}[two]{}
\define@cmdkey{mycmd}{three}[three]{}
\define@cmdkey{mycmd}{four}[four]{}
% ========= KEY DEFAULTS =========
\setkeys{mycmd}{one,two,three,four}% Defaults
\newcommand{\MyCommand}[1]{%
  \begingroup%
  \setkeys{mycmd}{two=SECOND,#1}% Set defaults for this macro + new keys
  \texttt{one}: \cmdKV@mycmd@one;
  \texttt{two}: \cmdKV@mycmd@two;
  \texttt{three}: \cmdKV@mycmd@three;
  \texttt{four}: \cmdKV@mycmd@four
  \endgroup%
}
\makeatother
\begin{document}

\MyCommand{one}

\MyCommand{two=second,one=first,three=third}

\MyCommand{four=Last}

\end{document}

作为参考,请参阅如何创建带有键值的命令?

答案2

我的答案只是 Werner 使用另一个工具给出的答案:不是使用xkeyval,而是使用 TeX 原语的五行代码。结果与 Werner 的结果完全相同。

\def\kv#1{\expandafter\ifx\csname kv:#1\endcsname \relax \expandafter\kvunknown
   \else \csname kv:#1\expandafter\endcsname\fi }
\def\kvunknown{???}
\def\kvscan #1#2=#3,{\ifx#1,\else \kvdef{kv:#1#2}{#3}\expandafter\kvscan\fi}
\def\kvdef#1{\expandafter\def\csname#1\endcsname}

\def\mymacro#1{\kvscan one=one, two=SECOND, three=three, four=four,,=,% implicit values
   \kvscan#1,,=,% actual values
   {\tt one}: \kv{one}; {\tt two}: \kv{two}; {\tt three}: \kv{three}; {\tt four}: \kv{four}
}

\mymacro{}

\mymacro{two=second, one=first, three=third}

\mymacro{four=Last}

沃纳的画像

如果有人认为这是在重新发明轮子,我不同意。对我来说,写这五行代码比阅读 72 页xkeyval文档要简单得多。

答案3

如同沃纳的方法但使用expl3

\RequirePackage{expl3}
\ExplSyntaxOn
\clist_map_inline:nn { one , two ,  three , four }
  {
    \keys_define:nn {  mycmd } { #1 .tl_set:c = { l__mycmd_ #1 _tl } }
  }
\cs_new_protected:Npn \MyCommand #1
  {
    \group_begin:
      \keys_set:nn { mycmd } {#1}
      Key~values~are:
     \clist_map_inline:nn { one , two ,  three , four }
       { ~ ##1 ~ = ~  ` \tl_to_str:c  { l__mycmd_ ##1 _tl } ' }
    \group_end:
  }
\ExplSyntaxOff
\documentclass{article}
\begin{document}

\MyCommand{one = a}

\end{document}

我之所以更喜欢expl3关键模块 ( l3keys),xkeyval是因为 的行为l3keys在括号保留、空格剥离以及参数中的,和类别代码=方面都非常明确和清晰。我还发现xkyeval界面相当尴尬(有点讽刺):l3keys与 共享pgfkeys

\input expl3-generic(如果您使用而不是,上述内容可以与其他格式一起使用\RequirePackage{expl3}。如果您肯定以 LaTeX 为目标,我会使用它xparse作为用户界面。)

答案4

我不知道这在什么意义上有用,但这里有一个选项xparse

\documentclass{scrartcl}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand \mycommand { g g g g g g g g g }
 {
  \seq_clear:N \l_tmpa_seq
  \IfValueT { #1 } { \seq_put_right:Nn \l_tmpa_seq { #1 } }
  \IfValueT { #2 } { \seq_put_right:Nn \l_tmpa_seq { #2 } }
  \IfValueT { #3 } { \seq_put_right:Nn \l_tmpa_seq { #3 } }
  \IfValueT { #4 } { \seq_put_right:Nn \l_tmpa_seq { #4 } }
  \IfValueT { #5 } { \seq_put_right:Nn \l_tmpa_seq { #5 } }
  \IfValueT { #6 } { \seq_put_right:Nn \l_tmpa_seq { #6 } }
  \IfValueT { #7 } { \seq_put_right:Nn \l_tmpa_seq { #7 } }
  \IfValueT { #8 } { \seq_put_right:Nn \l_tmpa_seq { #8 } }
  \IfValueT { #9 } { \seq_put_right:Nn \l_tmpa_seq { #9 } }
  \seq_use:Nn \l_tmpa_seq { , ~ }
 }

\ExplSyntaxOn

\begin{document}
\mycommand{one}{two}{three}
\mycommand{one}{two}{three}{four}{five}{six}{seven}
\mycommand{one}{two}
\end{document}

它最多需要九个参数,并用逗号分隔。

相关内容