newcommand 带有逗号分隔的参数和可选参数

newcommand 带有逗号分隔的参数和可选参数

我想以\def最简单的方式定义一个命令(不是用)

  • 采用逗号分隔的参数,
  • 支持可选参数(第一个)。

也许将来另一种带有额外可选参数的解决方案(不一定是第一个)会受到欢迎。

我想要这样的东西:\mycmd[opt arg1, opt arg2]{arg3, arg4}

我已经看到了https://tex.stackexchange.com/a/29975/92620https://tex.stackexchange.com/a/2860/92620

\documentclass{report}
\newcommand\mycmd[4][]{optional: #1\\ mandatory: #2 - #3 - #4}

\begin{document}
\mycmd[optional arg]{second arg,third arg,fourth arg}
\end{document}

答案1

下面的示例使用\comma@parsekvsetkeys来解析逗号分隔的参数列表。

\documentclass{report}
\usepackage{kvsetkeys}% provides \comma@parse
\usepackage{etexcmds}% provides \etex@unexpanded

\makeatletter
\newcount\arg@count
\newcommand{\arg@parser}[1]{%
  \advance\arg@count\@ne
  \expandafter\let\csname arg\romannumeral\arg@count\endcsname\comma@entry
}
% \mycmd[opt arg1, opt arg2]{arg 3, arg4, arg5}
\newcommand\mycmd[2][]{% Default is empty and will be configured later
  % Set default values
  \arg@count=\z@
  \comma@parse{default1, default2}\arg@parser % Default values
  % Parse optional argument
  \arg@count=\z@
  \comma@parse{#1}\arg@parser
  \ifnum\arg@count>2 %
    \@latex@error{Too many optional arguments}{%
      The macro \string\mycmd\space got \the\arg@count\space
      optional arguments,\MessageBreak
      but expected are 2 optional arguments.\MessageBreak
      \@ehd
    }%
  \fi
  % Mandatory arguments
  \arg@count=2
  \comma@parse{#2}\arg@parser
  \ifnum\arg@count=5 %
  \else
    \@latex@error{Wrong number of mandatory arguments}{%
      The macro \string\mycmd\space got \the\numexpr\arg@count-2\relax\space
      mandatory arguments,\MessageBreak
      but expected are 3 mandatory arguments.\MessageBreak
      \@ehd
    }%
  \fi
  % Either using \argi, \argii, \argiii, \argiv, \argv
  % or
  % \@mycmd\argi\argii\argiii\argiv\argv
  % or
  \edef\process@me{%
    \noexpand\@mycmd
    {\etex@unexpanded\expandafter{\argi}}%
    {\etex@unexpanded\expandafter{\argii}}%
    {\etex@unexpanded\expandafter{\argiii}}%
    {\etex@unexpanded\expandafter{\argiv}}%
    {\etex@unexpanded\expandafter{\argv}}%
  }%
  \process@me
}
\newcommand{\@mycmd}[5]{%
  \noindent
  optional: #1 -- #2\\
  mandatory: #3 -- #4 -- #5%
}
\makeatother

\begin{document}
  \mycmd[optional arg]{third arg,fourth arg, fifth arg }
\end{document}

结果

评论:

  • 可选参数列表可以更短,省略的参数将获取默认值。

  • \comma@parse通过删除前导和尾随空格来修剪参数。

  • \comma@parse删除空条目。空参数的解决方法是使用\relax\empty

答案2

jon 答案的变体,其中使用了原始设置。它主要用于展示如何expl3让事情变得简单。

我假设您想用“Optional:”将可选参数前缀分隔开,然后将它们排版在一行上,用“空格、短破折号、空格”分隔;强制参数以“Mandatory”为前缀,用“空格、长破折号、空格”分隔。

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\mycmd}{om}
 {
  \IfValueT{#1}
   {
    Optional:~\guest_print_list:nn { #1 } { ~--~ } \\*
   }
  Mandatory:~\guest_print_list:nn { #2 } { ~---~ }
 }

\seq_new:N \l_guest_list_seq
\cs_new_protected:Nn \guest_print_list:nn
 {
  \seq_set_from_clist:Nn \l_guest_list_seq { #1 }
  \seq_use:Nn \l_guest_list_seq { #2 }
 }

\ExplSyntaxOff

\setlength{\parindent}{0pt} % just for the example

\begin{document}

\mycmd{a, b , c}

\mycmd{a}

\mycmd[A]{a,b,c}

\mycmd[A, B]{a,b,c}

\end{document}

非常简单,无需重复代码,只需重用该\guest_print_list:nn函数即可,该函数将要打印的参数列表作为第一个参数,将分隔符作为第二个参数。请注意,输入中逗号周围的空格将被忽略。

在此处输入图片描述

答案3

此解决方案与解决方案本身中定义的两个轻量级逗号解析器(\p@rse@csl@opt\p@rse@csl@mnd)配合使用。对解析后的参数执行的操作分别布局到\domycmdopt\domycmdmnd

\documentclass{article}

\makeatletter
\newcommand\mycmd[2][]{%
  \if\relax\detokenize{#1}\relax\else\p@rse@csl@opt#1,\@nil\fi
  \p@rse@csl@mnd#2,\@nil
}
\def\p@rse@csl@opt#1,#2{%
  \domycmdopt{#1}%
  \ifx#2\@nil\else\expandafter\p@rse@csl@opt#2\fi
}
\def\p@rse@csl@mnd#1,#2{%
  \domycmdmnd{#1}%
  \ifx#2\@nil\else\expandafter\p@rse@csl@mnd#2\fi
}
\def\domycmdopt#1{opt-arg:#1\par}
\def\domycmdmnd#1{mnd-arg:#1\par}
\makeatletter

\begin{document}
\def\abc{def}
\mycmd[f00,bar]{hell0,w0rld}
\mycmd{1,2,3,$\alpha$,\abc}
\end{document}

输出裁剪

答案4

如果不知道参数的数量,可以使用pgfkeys如下方法:

\documentclass{article}

\usepackage{pgfkeys,pgffor}

\newcounter{mainargs}
\pgfkeys{mainargs/.is family, mainargs,
step counter/.code=\stepcounter{mainargs},
add argument/.style={step counter, arg\themainargs/.initial={#1}},
}

\newcounter{optargs}
\newif\ifoptargs
\pgfkeys{optargs/.is family, optargs,
    opt args present/.is if=optargs,
    step counter/.code=\stepcounter{optargs},
    add argument/.style={opt args present=true, step counter, arg\theoptargs/.initial={#1}},
}

\newcommand{\mycommand}[2][]{%
    \setcounter{mainargs}{0}%
    \pgfkeys{mainargs, add argument/.list={#2}}%
    \setcounter{optargs}{0}%
    \pgfkeys{optargs, add argument/.list={#1}}%
    %
    main arguments:\\%
    \foreach \n in {1,...,\themainargs}{%
    \pgfkeysvalueof{/mainargs/arg\n}\\%
    }%
    %
    \ifoptargs%
        optional arguments:\\%
        \foreach \n in {1,...,\theoptargs}{%
            \pgfkeysvalueof{/optargs/arg\n}\\%
        }%
    \fi%
}

\parindent=0pt
\begin{document}
    \mycommand{foo, bar}\\
    \mycommand[a,b]{foo, bar, baz}
\end{document}

当然,如果知道参数的数量,则可以使用\pgfkeysvalueof{/mainargs/arg1}\pgfkeysvalueof{/mainargs/arg2}等,而无需foreach循环。

由于参数值即使在命令执行完成后仍然可用,因此使用组来保持本地定义或定义重置键可能更安全,例如:

\pgfkeys{mainargs,
    reset one arg/.style={arg#1/.initial={}},
    reset args/.style={reset one arg/.list={1,...,\themainargs}}    
}

\pgfkeys{optargs,
    reset one arg/.style={arg#1/.initial={}},
    reset args/.style={reset one arg/.list={1,...,\theoptargs}, opt args present=false} 
}

然后应该在命令定义的末尾调用它们:

\pgfkeys{mainargs, reset args}
\pgfkeys{optargs, reset args}

问题中使用的格式的完整命令是:

\newcommand{\mycommand}[2][]{%
    \setcounter{mainargs}{0}%
    \pgfkeys{mainargs, add argument/.list={#2}}%
    \setcounter{optargs}{0}%
    \pgfkeys{optargs, add argument/.list={#1}}%
    %
    \ifoptargs%
        optional: %
        \pgfkeysvalueof{/optargs/arg1}
        \ifnum\theoptargs>1%
        \foreach \n in {2,...,\theoptargs}{%
            --- \pgfkeysvalueof{/optargs/arg\n}%
        }%
        \fi\\%
        \pgfkeys{optargs, reset args}%
    \fi%
    %
    mandatory: %
    \pgfkeysvalueof{/mainargs/arg1} 
    \ifnum\themainargs>1%
        \foreach \n in {2,...,\themainargs}{%
            --- \pgfkeysvalueof{/mainargs/arg\n}%
        }%
    \fi%
    %
    \pgfkeys{mainargs, reset args}%
}

相关内容