6

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?

5
  • 3
    It would be definetly easier \MyCommand{one,two,three}, and let TeX process the comma list separating items with , .
    – Manuel
    Oct 15, 2014 at 7:49
  • ...perhaps more appropriate is using a key-value approach: \MyCommand{one=first, two=second, three=third}.
    – Werner
    Oct 15, 2014 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, 2014 at 8:33
  • @egreg This is a cut-down MWE. The real application is more than just printing...
    – user30471
    Oct 15, 2014 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.
    – user30471
    Oct 15, 2014 at 10:52

4 Answers 4

6

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:

enter image description here

\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?.

6

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}

Werner's picture

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.

9
  • 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, 2014 at 9:32
  • 1
    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, 2014 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. Oct 16, 2014 at 10:58
  • The criticisms above not withstanding, I always enjoy reading Wipet's solutions. Děkuji!
    – user30471
    Oct 16, 2014 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, 2014 at 11:21
5

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.)

1
  • 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, 2014 at 9:24
3

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.

4
  • Thanks. This is better than my mock MWE, but I;ll try using keys following Werner's suggestion.
    – user30471
    Oct 15, 2014 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, 2014 at 13:50
  • 1
    I think you meant \ExplSyntaxOff.
    – jon
    Oct 16, 2014 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!
    – user30471
    Oct 16, 2014 at 3:22

You must log in to answer this question.