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?

  • 3
    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
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.

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

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

  • 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
  • 1
    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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.