# Defining a macro with a variable number of arguments

I want to define a macro that defines a series of other macros, each of which takes a variable (but in any given document fixed) number of arguments. I know that I can do this using something like this:

\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}


but I am sure that there is a better way of doing this...I get the feeling that it is time I learnt to use keys...

Does anyone have a better solution?

• It would be definetly easier \MyCommand{one,two,three}, and let TeX process the comma list separating items with , . – Manuel Oct 15 '14 at 7:49
• ...perhaps more appropriate is using a key-value approach: \MyCommand{one=first, two=second, three=third}. – Werner Oct 15 '14 at 7:52
• It depends on what you want to do with those arguments; if it's just printing them separated by comma and space, then I don't see why you'd want a macro. If it's for printing them with a variable separator, then it's another matter, but definitely not to be solved with a variable number of arguments which has the problem of “where should I stop?” – egreg Oct 15 '14 at 8:33
• @egreg This is a cut-down MWE. The real application is more than just printing... – Andrew Oct 15 '14 at 10:46
• @Werner Thank you. What you are suggesting is the right approach. I thought I should be using keys, but I didn't quite realise how. Am happy to accept this as an answer is you want to post it as one. – Andrew Oct 15 '14 at 10:52

I would suggest creating a number of keys that the user can decide to use or not. It also allows them the freedom to specify only a portion of them, in any order they wish. Here's a quick example using 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}


As reference, see How to create a command with key values?.

My answer is only Werner's answer by another tools: instead of usage of xkeyval, there are five lines of code with TeX primitives. The result is exactly the same as Werner's result.

\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}


If somebody will comment this as reinventing the wheel, I disagree. Writing such five lines is much more simple for me than the reading 72 pages of xkeyval documentation.

• I think this is indeed reinventing the wheel because reading the TeXBook (many more than 72 pages) to understand expansion is significantly more work than understanding how a package is used. But still a nice one anyway, I like its simplicity for this task. – percusse Oct 16 '14 at 9:32
• But after reading the TeXbook I am able to solve much more things than only xkeyval features described at 72 pages and basically implemented by five lines-:). When I count up all pages of documentation of all packages where a few lines of implementation is possible then the total number of pages will be many times more than pages in the TeXbook. – wipet Oct 16 '14 at 10:33
• The answer is the same for this particular input, but if you put white space about , you'll get differences, normalizing white space while controlling how many {} are needed to get a , into the value is usually the larger part of a keyval parser, splitting up on , and = is the simple bit, as you show. – David Carlisle Oct 16 '14 at 10:58
• The criticisms above not withstanding, I always enjoy reading Wipet's solutions. Děkuji! – Andrew Oct 16 '14 at 11:14
• @DavidCarlisle If users need to remove white space (for example around =) then they can use \replacestrings{ =}{=}\replacestrings{= }{=} (the \replacestrings from OPmac) as is shown at my site of various pakages-like plain TeX simple solutions: petr.olsak.net/opmac-tricks.html#keyval – wipet Oct 16 '14 at 11:21

Similar to Werner's approach but using 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}


The reason I'd favour the expl3 key module (l3keys) over xkeyval is that the behaviour of l3keys with respect to brace retention, space stripping and the category codes of , and = in arguments is all very well-defined and clear. I also find the xkyeval interface rather awkward (somewhat ironic): l3keys shares with pgfkeys.

(The above work with other formats if you use \input expl3-generic rather than \RequirePackage{expl3}. If you are definitely targetting LaTeX I'd use xparse for the user interface.)

• I've defined the keys here using a comma list mapping as it's convenient: in a real case you might well want to do them using a keyval list (part of the point of l3keys). – Joseph Wright Oct 16 '14 at 9:24

I don't know in what sense this is useful, but here's an option with 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}


It takes up to nine arguments, and separates them with commas.

• Thanks. This is better than my mock MWE, but I;ll try using keys following Werner's suggestion. – Andrew Oct 15 '14 at 10:53
• We don't know what you want to achieve, so saying that the best solution is a comma separated list is not a “suggestion” but a “blind shot” to see if it works. You could add details about what you want to achieve, you may receive better answers about the best “interface”. – Manuel Oct 15 '14 at 13:50
• I think you meant \ExplSyntaxOff`. – jon Oct 16 '14 at 1:28
• @Manuel Sorry, I wasn't trying to obscure. The application I have has several components, so it would be quite lengthy to explain (and I haven't worked out all the details year). This aspect of it is quite simple, so I tried to distil what I need in a simple MWE. Of course, you are right in that the "best" solution depends on what I am really trying to do... Thanks again! – Andrew Oct 16 '14 at 3:22