1

I attempted to make a generic variable argument macro based off of what I saw here that can prepends text before each argument, append text after each argument, and adds text after all arguments have gone through.

It seems to work in general. Though, you can't put any kind of text after it because it will mess things up:

% Allows using an @ in a macro name so we can use \@ifnextchar
\makeatletter

% Generic macro for doing things to variable arguments
%    #1: What to prepend to the current item
%    #2: What to append to the current item
%    #3: What to put after all is done
\newcommand{\checknextarg}[3]{\@ifnextchar\bgroup{\@gobblenextarg{#1}{#2}{#3}}{#3}}

% Helper function for \checknextarg that controls the printing/displaying
%    #1: What to prepend to the current item
%    #2: What to append to the current item
%    #3: What to put after all is done
%    #4: The current item
\newcommand{\@gobblenextarg}[4]{#1#4#2 \checknextarg{#1}{#2}{#3}}

% Restores the @ symbol back to normal
\makeatother

So, let us say we have some arbitrary macro called func that is displayed with some variable number of arguments. So, if I called \func{a_1}{a_2}{a_3}, it would display something like

func picture

Let's say that I want func to require at least 2 parameters. So, I attempt to add the function like such:

\newcommand{\func}[2]{\text{func}(#1, #2\checknextarg{, }{}{)}}

The first argument to \checknextarg is ", " since I wish for each parameter to be comma separated. The second is simply nothing because I don't desire to append anything to each parameter. The third is ")" to close off the function.

This works as intended for 2 parameters or greater. However, LaTeX will attempt to read the text after the call to the \func macro if there are less than 2 commands put in because of the nature of \@ifnextchar. So, I was wondering if there were any ideas on how to avoid taking in arguments in cases like this.

2
  • You should probably explain in a short and clear way what you want to do, instead of how you intend to use this trick you saw on the web. We've already discussed this approach on this site (at least here and here) and, well... this doesn't fit very well with the LaTeX way of doing things (there is a risk of picking an unwanted argument coming next; better use comma lists or wrap the brace groups in variable number inside one braced argument, so that the beginning and end are clearly marked).
    – frougon
    Apr 9, 2020 at 10:09
  • @frougon Fair enough. I will look into better methods such as the one Ulrich suggested below. However, the main point of the question (minimum parameter size requirement) still will probably be relevant to my fixed macros. Apr 10, 2020 at 6:06

1 Answer 1

4

I don't like this kind of syntax at all for reasons that have already been discussed several times in the past. One of them has to do with "expandability" of macros.

Be that as it may.

Issue 1:

Besides a recursive mechanism (\checknextarg/\@checknextarg/\@gobblenextarg) for prepending/appending things to arguments you may need another mechanism for recursively collecting and accumulating brace-nested arguments. (\CollectAnotherArg/\@CollectAnotherArg/\@@CollectAnotherArg/\RemoveBracesFromTwoArgs in the example below.)

Issue 2:

\@ifnextchar as defined in the LaTeX 2ε-kernel disobeys and silently discards space-tokens when doing the "lookahead at the next character". In case the next character isn't a {-character, you probably don't want space-tokens before that character to be removed. A mechanism for accumulating space-tokens might be useful in case things are used when in horizontal mode/when not in mathmode.

Putting the pieces together, you might get something like this:

\documentclass{article}
\usepackage{amsmath}

\makeatletter

%..............................................................................
% Mechanism for accumulating space-tokens and passing them towards something else:
%..............................................................................
\newcommand\@ifnextspace[2]{%
  \def\reserved@a{#1}\def\reserved@b{#2}\futurelet\@let@token\@@ifnextspace
}%
\newcommand\@@ifnextspace{%
  \ifx\@let@token\@sptoken\expandafter\reserved@a\else\expandafter\reserved@b\fi
}%
\newcommand\accumulatespacetokens[2]{%
  % #1 Token-sequence where accumulated space-tokens are to be appended as another argument.
  % #2 Space-tokens accumulated so far.
   \@ifnextspace{\@gobblenextspacetoken{#1}{#2}}{#1{#2}}%
}%
\@ifdefinable\@gobblenextspacetoken{%
  \long\def\@gobblenextspacetoken#1#2 {\accumulatespacetokens{#1}{#2 }}%
}%

%..............................................................................
% Generic macro for doing things to a variable amount of arguments:
%..............................................................................
\newcommand{\checknextarg}[3]{%
  % #1: What to prepend to the current item.
  % #2: What to append to the current item.
  % #3: What to put after all is done.
  % Let's accumulate space-tokens and append them to the third argument of the
  % call to \kernel@ifnextchar.
  \accumulatespacetokens{\@checknextarg{#1}{#2}{#3}}{}%
}%
\newcommand{\@checknextarg}[4]{%
  % #1: What to prepend to the current item.
  % #2: What to append to the current item.
  % #3: What to put after all is done.
  % #4: Space-tokens accumulated so far.
  \kernel@ifnextchar\bgroup{\@gobblenextarg{#1}{#2}{#3}}{#3#4}%
}%
%
% Helper macro for \checknextarg that controls the printing/displaying:
%
\newcommand{\@gobblenextarg}[4]{%
  % #1: What to prepend to the current item.
  % #2: What to append to the current item.
  % #3: What to put after all is done.
  % #4: The current item.
  #1#4#2\checknextarg{#1}{#2}{#3}%
}%

%..............................................................................
% Mechanism for accumulating brace-nested arguments and passing them into 
% something else:
%..............................................................................
\newcommand\CollectAnotherArg[3]{%
  %  #1: What to do with list of brace-nested arguments accumulated so far in case there is no more brace-nested argument.
  %  #2: What to do with list of brace-nested arguments accumulated so far in case there is another brace-nested argument.
   % #3: List of brace-nested arguments accumulated so far.
  \accumulatespacetokens{\@CollectAnotherArg{#1}{#2}{#3}}{}%
}%
\newcommand\@CollectAnotherArg[4]{%
  %  #1: What to do with list of brace-nested arguments accumulated so far in case there is no more brace-nested argument.
  %  #2: What to do with list of brace-nested arguments accumulated so far in case there is another brace-nested argument.
   % #3: List of brace-nested arguments accumulated so far.
   % #4: Spaces accumulated so far.
   \kernel@ifnextchar\bgroup{\@@CollectAnotherArg{#2}{#3}{#4}}{#1{#3#4}}%
}%
\newcommand\@@CollectAnotherArg[4]{%
  %  #1: What to do with list of brace-nested arguments accumulated so far in case there is another brace-nested argument.
   % #2: List of brace-nested arguments accumulated so far.
   % #3: Spaces accumulated so far.
   % #4: Next brace-nested argument.
   #1{#2#3{#4}}%
}%
\newcommand\RemoveBracesFromTwoArgs[2]{#1#2}%

%..............................................................................
% The macro \func:
%..............................................................................
\newcommand{\func}{%
  % Nest as many  
  %   \CollectAnotherArg{\@firstofone}{%
  %       ...
  %   }
  % as you need.
  \CollectAnotherArg{\@firstofone}{%  <- at least one brace-grouped argument
    \CollectAnotherArg{\@firstofone}{%  <- at least two brace-grouped arguments
      %\CollectAnotherArg{\@firstofone}{%  <- at least three brace-grouped arguments
        %\CollectAnotherArg{\@firstofone}{%  <- at least four brace-grouped arguments
          \text{func}(%
          \RemoveBracesFromTwoArgs{%
            \romannumeral0\expandafter\@gobble\@gobblenextarg{, }{}{)}%
          }%
        %}%
      %}%
    }%
  }{}%
}%

\makeatother

\begin{document}

\verb*|\func ABCD|: \func ABCD

\verb*|\func {A}BCD|: \func {A}BCD

\verb*|\func {A} BCD|: \func {A} BCD

\verb*|\func {A}{B}CD|: \func {A}{B}CD

\verb*|\func {A}{B} CD|: \func {A}{B} CD

\verb*|\func {A} {B} CD|: \func {A} {B} CD

\verb*|\func {A}{B}{C}D|: \func {A}{B}{C}D

\verb*|\func {A} {B}{C}D|: \func {A} {B}{C}D

\verb*|\func {A}{B} {C} D|: \func {A}{B} {C} D

\verb*|\func {A} {B} {C} D|: \func {A} {B} {C} D

\verb*|\func {A}{B}{C}{D}|: \func {A}{B}{C}{D}

\verb*|$\func {a_1}{a_2}{a_3}$|: $\func {a_1}{a_2}{a_3}$

\end{document}

enter image description here

2
  • Edit: I missed some stuff. Never mind. Thank you very much! :) Apr 10, 2020 at 6:14
  • 1
    @GigiBayte2 "Edit:" ??? What did you edit? Did you edit your comment? If so, I didn't see the initial version of your comment. If you have questions about how the code works, don't hesitate to ask. If you like, you can also start new questions for doing so - this way, more people are reached who can push forward the explanation process. ;-) Apr 10, 2020 at 8:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .