2

I am using the following generator to generate variadic macros:

% USAGE :: \VARIADIC{name}{start}{mid}{stop}
%
\newcommand{\VARIADIC}[4]{%
  \expandafter\newcommand\csname GobbleNext#1Arg\endcsname[1]{%
    #3##1\csname CheckNext#1Arg\endcsname%
  }%
  \expandafter\newcommand\csname CheckNext#1Arg\endcsname{%
    \csname @ifnextchar\endcsname\bgroup{\csname GobbleNext#1Arg\endcsname}{#4\endgroup}%
  }%
  \expandafter\newcommand\csname #1\endcsname[2]{%
    \begingroup#2##1#3##2\csname CheckNext#1Arg\endcsname%
  }%
}

Essentially, it checks if there are more "arguments" -- tokens that start with \bgroup i.e. \{ and then decides whether to expand recursively or halt.

I use it as below:

\VARIADIC {List} {[} {;} {]}
\VARIADIC {Sum} {(} {+} {)}

...

\begin{document}
  \List {a} {b} {c} {d} {e}
  \Sum {x} {y} {z}
\end{document}

I can also apply simple styles like:

\VARIADIC {Sum} {\textbf\bgroup(} {+} {)\egroup}

But I am not sure if this is the best / right way to do so. I use \ensuremath a lot, and the following macro doesn't work -- I'm not sure why ...

\VARIADIC {Tuple} {\ensuremath\bgroup\langle} {,} {\rangle\egroup}

I want to be able to compose a \VARIADIC macro with existing macros easily. Concretely, I am looking for a way to compose an arbitrary existing unary macro, say \XYZ as below:

\VARIADIC {Test} {\XYZ\bgroup(} {,} {)\egroup}
  • 1
    \textbf\bgroup a\egroup only works by accident; \ensuremath\bgroup a\egroup doesn't work at all. The proper syntax is with braces around the argument. – egreg Sep 8 at 8:07
  • 1
    while it is possible to define this, it goes against all latex syntax guidelines, the arguments are optional and so should use [] not {} No standard latex command takes a variable number of {} arguments. – David Carlisle Sep 8 at 8:40
  • @DavidCarlisle: Thanks, I will keep that in mind. I was using {} so that the macros look like regular macros. I think it to make the macros work with [], I basically have to replace the \newcommands with \defs – Saswat Padhi Sep 8 at 8:51
3

The problem with your definition is that some macros require their argument in an explicitly braced broup. In your \Tuple example, \ensuremath would read the following argument \bgroup and make sure that this argument is set in math mode. But this is not what you want. (I think the \textbf example also just works coincidentally because of how this macro is defined.)

As a solution you could extrend your \VARIADIC macro by a new parameter #2 which is then wrapped around all the material your newly defined command inserts into the output stream in the form of #2{...}. As splitting the opening and closing brace accross different macros is quite tricky, instead of directly outputting the macro's material, we collect it in another parameter that is passed around by \GobbleNext...Arg and \CheckNext...Arg. Finally, when no new argument is found, all the collected material is typeset with #2 wrapped around.

In case you don't want to modify your final group, you can just leave the (new) second parameter empty. The explicit braces inserted for the group replace the \begingroup ... \endgroup of your old definition.

\documentclass{article}

\newcommand{\VARIADIC}[5]{%
  \expandafter\newcommand\csname GobbleNext#1Arg\endcsname[2]{%
    \csname CheckNext#1Arg\endcsname{##1#4##2}%
    % Material moved to new argument ^^^^^^^^
  }%
  \expandafter\newcommand\csname CheckNext#1Arg\endcsname[1]{%
    \csname @ifnextchar\endcsname\bgroup{\csname GobbleNext#1Arg\endcsname{##1}}{#2{##1#5}}%
    % Wrapper macro #2 with new explicit braces ---------------------------------^-^-----^
  }%
  \expandafter\newcommand\csname #1\endcsname[2]{%
    \csname CheckNext#1Arg\endcsname{#3##1#4##2}%
    % Material moved to new argument ^^^^^^^^^^
  }%
}

\VARIADIC {List} {} {[} {;} {]}
\VARIADIC {Sum} {\textbf} {(} {+} {)}
\VARIADIC {Tuple} {\ensuremath} {\langle} {,} {\rangle}

\begin{document}
  \List {a} {b} {c} {d} {e}

  \Sum {x} {y} {z}

  \Tuple {\alpha} {\beta} {\gamma}
\end{document}

outputs

enter image description here

  • 1
    Thank you so much! :) This is brilliant. I was suspicious of the \textbf, but couldn't figure out why it worked and \ensuremath didn't. I was starting to think about adding a second parameter, but I was stuck trying to figure out how to suspend expansion of #2 till its argument has been fully expanded. Accumulating this argument and and passing it around till completion is pretty neat! – Saswat Padhi Sep 8 at 4:44
5

Macros with variable number of arguments should not be used. You can instead pass a comma separated list, which is even easier to type.

\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\variadic}{mm}
 {
  \group_begin:
  \keys_set:nn { saswat/variadic } { #2 }
  \saswat_variadic_define:nVVVV { #1 }
   \l__saswat_variadic_env_tl % optional envelope
   \l__saswat_variadic_start_tl % start
   \l__saswat_variadic_mid_tl % mid
   \l__saswat_variadic_stop_tl % stop
  \group_end:
 }

\keys_define:nn { saswat/variadic }
 {
  env   .tl_set:N  = \l__saswat_variadic_env_tl,
  start .tl_set:N  = \l__saswat_variadic_start_tl,
  mid   .tl_set:N  = \l__saswat_variadic_mid_tl,
  stop  .tl_set:N  = \l__saswat_variadic_stop_tl,
  env   .initial:n = \use:n,
 }

\seq_new:N \l__saswat_variadic_items_seq

\cs_new_protected:Nn \saswat_variadic_define:nnnnn
 {
  \cs_new_protected:cpn { #1 } ##1
   {
    \seq_set_split:Nnn \l__saswat_variadic_items_seq { , } { ##1 }
    #2 { #3 \seq_use:Nn \l__saswat_variadic_items_seq { #4 } #5 }
   }
 }
\cs_generate_variant:Nn \saswat_variadic_define:nnnnn { nVVVV }

\ExplSyntaxOff

\variadic{List}{
  start=[,
  mid=;,
  stop=],
}

\variadic{Sum}{
  env=\textbf,
  start=(,
  mid=+,
  stop=),
}

\variadic{Tuple}{
  env=\ensuremath,
  start=\langle,
  mid={,},
  stop=\rangle,
}

\begin{document}

\List{a,b,c,d,e}

\Sum{x,y,z}

\Tuple{a,b,\dots,z}

\end{document}

I chose a key-value interface, so it's not necessary to remember the precise order of the argument, for keys can be specified in any order.

The key env is for an “enveloping macro”, see the examples. In case it is not specified, it defaults to \use:n which is essentially a no-op, because it just delivers its argument.

enter image description here

  • Thank you. In this approach we are basically parsing the single argument and extracting the required values by splitting on the commas. The main reason I was using variable number of arguments was so that I wouldn't have to remember which macros were variadic ones and I have to use a different argument style, say commas in your case, as opposed to the usual style. Could you also explain why "Macros with variable number of arguments should not be used."? Or point me to any resources that discuss this. – Saswat Padhi Sep 9 at 3:59

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.