83

I have a mathematical transformation that takes 16 parameters (grouped into 3+8+5) and would like to make a latex command for it, so that I can easily change the notation for it if the need arises.

As far as I know, both \def and \newcommand take a maximum of 9 arguments, is there any (recommended) way to extend this?

3
  • 3
    Perhaps you might show us the detail of what is wanted. This sounds like a question where the best answer will be to think carefully about the input you really require.
    – Joseph Wright
    Aug 21, 2010 at 6:43
  • I edited the question to make it clear the parameters are not programmatic, but rather, an unavoidable part of the the maths that I'm using.
    – Simon
    Aug 21, 2010 at 7:02
  • 1
    I wonder if there's a magic solution involving Currying.
    – Seamus
    Feb 21, 2013 at 16:19

10 Answers 10

61

You are going to have to parse the arguments some at a time and store them into temporary registers or macros. For example

\newcommand\foo[9]{%
    \def\tempa{#1}%
    \def\tempb{#2}%
    \def\tempc{#3}%
    \def\tempd{#4}%
    \def\tempe{#5}%
    \def\tempf{#6}%
    \def\tempg{#7}%
    \def\temph{#8}%
    \def\tempi{#9}%
    \foocontinued
}
\newcommand\foocontinued[7]{%
    % Do whatever you want with your 9+7 arguments here.
}
6
  • Thanks TH - that's the same solution as supplied in the "black TeX magic" link provided by mindcorrosive. I think that I'll use the xargs package, since it will make my code clearer and I like the simple default arguments.
    – Simon
    Aug 21, 2010 at 6:55
  • 9
    Can you give an example of how to declare and access/use the variables?
    – 7heViking
    Oct 15, 2015 at 15:10
  • I ask the same question as @FireFly3000 . I did as you said above and called the two functions as follows: \pointscolors{100}{red}; \myarc{0}{0}{0}{0}{3}{2.645751311064591}{0}{-3}{2.645751311064591}; (sorry, I do not know how to format this), with 2 arguments and the next with 9 arguments. then I get this error: Argument of \myarc has an extra }. <inserted text> \par l.90 ...myarc{0}{0}{0}{0}{3}{2.645751311064591}{0} {-3}{2.645751311064591}; ? Nov 2, 2015 at 23:01
  • 1
    I removed the "\foocontinued" from the first block (named "\foo") and my error disappeared. I think that line is in error because is called without parameters. Nov 2, 2015 at 23:32
  • 3
    Within the definition-text of \foocontinued you can use the macros \tempa..\tempi and the arguments #1..#7, so within the definition-text of \foocontinued the first nine arguments are addressable via macro-calls and the following seven nine arguments are addressable via #<digit>-syntax. Oct 21, 2018 at 17:47
36

There's the xargs package, and there's also some black TeX magic. As for myself, being conditioned in Python, I prefer the key-value parameter syntax provided by keyval/xkeyval packages.

On an unrelated note, if I find myself needing more than 9 parameters, that usually means that my macro/def/code organization is not very good, and I'd try to improve that first. But of course, there are legitimate situations where 9 parameters are perfectly okay --- especially if you try to build a definition with a lot of knobs and tweaks.

4
  • Thanks, I don't know how my googling did not turn up the first option you gave. The 16 parameters define a nonlinear transformation - they're not options in the macro.
    – Simon
    Aug 21, 2010 at 6:51
  • 6
    Actually, xargs does not allow more than 9 arguments - it only gives a neat interface for optional arguments. I'll have to use the TeX hack.
    – Simon
    Aug 21, 2010 at 7:10
  • 3
    That's correct. Until you clarified what you need so much parameters for, I assumed it's for a macro, and you'd use the keyval interface. But of course in that case it's better with plain TeX. Aug 21, 2010 at 7:15
  • Actually you made me reconsider if I needed all those arguments and I ended up with 6 arguments in stead of 10 :)
    – 7heViking
    Oct 15, 2015 at 16:05
25

NEW ANSWER

The listofitems package used here is preferable to my original answers below, because \readlist does not expand the argument when capturing it, nor does it rely on inconvenient roman-numeral syntax.

\documentclass{article}
\usepackage{listofitems}
\begin{document}
\setsepchar{ }
\readlist\arg{1 2 3 4 5 6 7 8 9 10 11 12 FinalArgument}
There are \arglen{} arguments.  The thirteenth is \arg[13]
\end{document}

ORIGINAL ANSWER

In a response to How to use variables inside a command when generating a table? I mention how the stringstrings package has a \getargs command that will parse large numbers of arguments that are passed within a single { }. To recap that reply,

\documentclass{article}
\usepackage{stringstrings}
\begin{document}
\getargs{1 2 3 4 5 6 7 8 9 10 11 12 FinalArgument}
There are \narg~arguments.  The thirteenth is \argxiii
\end{document}

The result to this example is:

There are 13 arguments. The thirteenth is FinalArgument


EDIT: A much more efficient version of \getargs is available in the readarray package and called \getargsC (in deference to David Carlisle's help). Thus, the same task can be accomplished more quickly with

\documentclass{article}
\usepackage{readarray}
\begin{document}
\getargsC{1 2 3 4 5 6 7 8 9 10 11 12 FinalArgument}
There are \narg~arguments.  The thirteenth is \argxiii
\end{document}
3
  • Could you please edit your answer to show how these techniques can be used to define a command taking several arguments?
    – Psychonaut
    Feb 9, 2017 at 14:03
  • @Psychonaut when you say several arguments, do you mean several arguments within a single set of braces? For example, the listofitems answer at the end of this answer: tex.stackexchange.com/questions/348366/… Feb 9, 2017 at 14:44
  • 1
    It's also worth mentioning that the stringstrings package has some unwanted side effects due to definitions of \t et al, that are commonly used by latex users as shortcuts. BTW, the readarray package doesn't do that and seems a better alternative. Aug 11, 2017 at 1:11
12

Since it's a different technique, I also present the following: local macro definitions.

\documentclass{article}

\def\NineteenArgs#1#2#3#4#5#6#7#8#9{%
    \def\ArgsTenAndFurther##1##2##3##4##5##6##7##8##9{%
        \def\ArgNineteen####1{%
            ####1##9##8##7##6##5##4##3##2##1#9#8#7#6#5#4#3#2#1%
        }%
        \ArgNineteen%
    }%
    \ArgsTenAndFurther%
}

\begin{document}
                 %1234567890123456789
    \NineteenArgs abcdefghijklmnopqrs
\end{document}
2
  • 2
    This is beautiful, in a horrifying sort of way. I wanted \matr 1110 1110 1110 0000, and \newcommand{\matr}[9]{\def\matrMore##1##2##3##4##5##6##7{\begin{bmatrix}#1&#2&#3&#4\\#5&#6&#7&#8\\#9&##1&##2&##3\\##4&##5&##6&##7\end{bmatrix}}\matrMore} did the trick perfectly! Mar 26, 2017 at 2:03
  • Glad it helped. I trust you like the transparency of the argument naming scheme:-)
    – user10274
    Mar 26, 2017 at 15:25
7

First, we define the command \foo. It contains a nested command definition for parameters extension. Note it is declared using \neworrenewcommand, which defines a command if not defined, or redefine it if it is already defined.*

Parameters with one dash are bound to the first command, while parameters with two dashes are bound to the second command. MWE:

\documentclass{article}

% Provide a way to declare and renew a command in one command
\newcommand{\neworrenewcommand}[1]{\providecommand{#1}{}\renewcommand{#1}}

\newcommand{\foo}[9]{
    \neworrenewcommand{\ffoo}[1]{
        #1 #2 #3 #4 #5 #6 #7 #8 #9 ##1
    }
    \ffoo
}

\begin{document}
    \foo{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}

    \foo{a}{b}{c}{d}{e}{f}{g}{h}{i}{j}
\end{document}

Preview of the result:

preview

*This is necessary, since the calling \foo multiple times redefines the nested command. The \neworrenewcommand trick is further explained here.

7
  • 1
    Welcome to TeX.SX! This is more or less the same as the answer up above: tex.stackexchange.com/a/165324/134574 (using \newcommand instead of \def). Sep 25, 2019 at 19:04
  • 1
    Hey! Thank you! Yes, I was wondering if that was possible with \newcommand as well and it was. I felt in the mood for documenting it here. :)
    – Jämes
    Sep 25, 2019 at 19:14
  • This gives an error if one wants to use the command consecutively, e.g two times. Error message: Command *** already defined. For a working solution look at diligar's answer from above and at Ulrich Diez's comment on it. tex.stackexchange.com/a/418343/76400
    – Philipp H
    Apr 13, 2020 at 10:11
  • @PhilippH This is because the subcommand (ffoo) is renewed when called multiple times. I changed its definition using providecommand, so it works with multiple invocations.
    – Jämes
    Jun 4, 2020 at 13:54
  • @Teepeemm \providecommand defines a new command if it isn't already defined. Therefore, the first time the nested command is created and subsequent calls will not redefine it.
    – Jämes
    Jun 8, 2020 at 8:01
5

This assumes no argument of a weird command contains a weird command. Here for “weird command” I mean one defined with the proposed \NewWeirdCommand below.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\NewWeirdCommand}{mmm}
 {% #1 = command to define, #2 = number of arguments, #3 = replacement text
  \cs_new:Npn #1
   {
    \tl_set:Nn \l__simon_parse_args_tl { #3 }
    \__simon_parse_args:n { #2 }
   }
 }
\NewDocumentCommand{\Arg}{m}
 {
  \seq_item:Nn \l__simon_parse_args_seq { #1 }
 }

\tl_new:N \l__simon_parse_args_tl
\seq_new:N \l__simon_parse_args_seq
\int_new:N \l__simon_parse_args_int

\cs_new_protected:Nn \__simon_parse_args:n
 {
  \seq_clear:N \l__simon_parse_args_seq
  \int_zero:N \l__simon_parse_args_int
  \__simon_parse_arg:n { #1 }
 }
\cs_new_protected:Nn \__simon_parse_arg:n
 {
  \int_incr:N \l__simon_parse_args_int
  \int_compare:nNnTF { \l__simon_parse_args_int } > { #1 }
   {
    \tl_use:N \l__simon_parse_args_tl
   }
   {
    \__simon_parse_arg_aux:nn { #1 }
   }
 }
\cs_new_protected:Nn \__simon_parse_arg_aux:nn
 {
  \seq_put_right:Nn \l__simon_parse_args_seq { #2 }
  \__simon_parse_arg:n { #1 }
 }
\ExplSyntaxOff

\begin{document}

\NewWeirdCommand{\foo}{4}{\Arg{1}-\Arg{2}-\Arg{3}-\Arg{4}END}
\NewWeirdCommand{\baz}{16}{%
  \Arg{16}-%
  \Arg{15}-%
  \Arg{14}-%
  \Arg{13}-%
  \Arg{12}-%
  \Arg{11}-%
  \Arg{10}-%
  \Arg{9}-%
  \Arg{8}-%
  \Arg{7}-%
  \Arg{6}-%
  \Arg{5}-%
  \Arg{4}-%
  \Arg{3}-%
  \Arg{2}-%
  \Arg{1}-%
  END%
}

\foo{a}{b}{c}{d}XXXX

\baz{a1}{b1}{c1}{d1}{a2}{b2}{c2}{d2}{a3}{b3}{c3}{d3}{a4}{b4}{c4}{d4}XXXX

\end{document}

In the replacement text, \Arg{<number>} denotes the corresponding collected argument.

No check is done, so if you define a weird command to have 15 arguments, \Arg{16} in the replacement text will output nothing.

enter image description here

If you are willing to use a slightly different syntax, with all arguments in an additional pair of braces, it's simpler:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\NewWeirdCommand}{mm}
 {% #1 = command to define, #2 = replacement text
  \cs_new:Npn #1 ##1
   {
    \tl_set:Nn \l__simon_args_tl { ##1 }
    #2
   }
 }
\NewDocumentCommand{\Arg}{m}
 {
  \tl_item:Nn \l__simon_args_tl { #1 }
 }

\tl_new:N \l__simon_parse_args_tl
\ExplSyntaxOff

\begin{document}

\NewWeirdCommand{\foo}{\Arg{1}-\Arg{2}-\Arg{3}-\Arg{4}END}
\NewWeirdCommand{\baz}{%
  \Arg{16}-%
  \Arg{15}-%
  \Arg{14}-%
  \Arg{13}-%
  \Arg{12}-%
  \Arg{11}-%
  \Arg{10}-%
  \Arg{9}-%
  \Arg{8}-%
  \Arg{7}-%
  \Arg{6}-%
  \Arg{5}-%
  \Arg{4}-%
  \Arg{3}-%
  \Arg{2}-%
  \Arg{1}-%
  END%
}

\foo{{a}{b}{c}{d}}XXXX

\baz{{a1}{b1}{c1}{d1}{a2}{b2}{c2}{d2}{a3}{b3}{c3}{d3}{a4}{b4}{c4}{d4}}XXXX

\end{document}
1
  • Hehe, the weird command. I like it. :P
    – manooooh
    Oct 22, 2018 at 0:19
3

I show something similar as ergreg's solution, but without usage of any special macro language. Only pure TeX language is used.

\newcount\argnum
\def\argsdef#1#2{\def#1{\argnum=1\def\useargs{#2}\inargs}}
\def\inargs{\futurelet\next\inargsA}
\def\inargsA{\ifx\next\bgroup \expandafter\inargsB \else \expandafter\useargs \fi}
\def\inargsB#1{\expandafter\def\csname Arg:\the\argnum\endcsname{#1}%
   \advance\argnum by1 
   \inargs}
\def\arg#1{\csname Arg:#1\endcsname}

\argsdef\a{\arg{1}:\arg{3}:\arg{5}::\arg{11}:\arg{13}}
\argsdef\b{\arg{6}-\arg{5}-\arg{4}-\arg{3}-\arg{2}-\arg{1}}

\a{A}{B}{C}{D}{E}{F}{G}{H}{I}{J}{K}{L}{M}

\b{a}{b}{c}{d}{e}{f}
3

I can offer a macro \KeepKthOfLArguments which picks the K-th undelimited argument of a list of L undelimited arguments.

When doing macro-programming, you can implement a "mechanism" where the L arguments are gathered and accumulated within a single macro argument which in turn holds the list of L arguments and which in turn is passed to an internal macro which in turn does things in terms of \KeepKthOfLArguments.

I don't recommend using this for macros at user-level.

At user-level I recommend interfaces that do list-processing, e.g., with keyval-syntax. (keyval-syntax implies processing a comma-separated list of key=value-pairs.)

Things like \pgfplotstableread of the package pgfplots and the facilities of the package datatool for iterating on databases and comma-lists and property-lists of expl3 might be of interest as well.

But sometimes I use such things with macros that both generate and process large lists of undelimited arguments automatically without the need of the user passing/typing these arguments.

\documentclass{article}

\makeatletter
%% Code for \KeepKthOfLArguments: 
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond, 
%%    \UD@stopromannumeral, \UD@CheckWhetherNull
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
  \expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%=============================================================================
%% Keep only the K-th of L consecutive undelimited arguments.
%%   ( IF K < 1 OR K > L just remove L consecutive undelimited arguments. )
%%=============================================================================
%% \KeepKthOfLArguments{<integer number K>}%
%%                     {<integer number L>}%
%%                     <sequence of L consecutive undelimited arguments>
%%
%% If L < 1 yields nothing.
%% Else:
%%   If K >= 1 and K < L  yields:
%%     <K-th undelimited argument from <sequence of L consecutive undelimited 
%%      arguments>>
%%   If K < 1 or K > L
%%     (-> there is no K-th argument in the
%%         <sequence of L consecutive undelimited arguments> )
%%   yields nothing  but removal of <sequence of L consecutive 
%%          undelimited arguments>
\newcommand\KeepKthOfLArguments[2]{%
  \romannumeral
  % #1: <integer number K>
  % #2: <integer number L>
  \expandafter\UD@KeepKthOfLArgumentsKSmallerOneFork
  \expandafter{\romannumeral\number\number#1 000\expandafter}%
  \expandafter{\romannumeral\number\number#2 000}%
}%
%%-----------------------------------------------------------------------------
\newcommand\UD@KeepKthOfLArgumentsKSmallerOneFork[2]{%
  % #1: <K letters m>
  % #2: <L letters m >
  \UD@CheckWhetherNull{#1}{% K is smaller than one:
    \UD@KeepKthOfLArgumentsRemoveNArguments{#2}{\UD@stopromannumeral}{}%
  }{% K is not smaller than one:
    \expandafter\UD@PassFirstToSecond
    \expandafter{%
      \UD@firstoftwo{}#1%
    }{%
      \UD@KeepKthOfLArgumentsEvaluateLMinusKDifferenceLoop{#1}{#2}%
    }{#2}%
  }%
}%
%%-----------------------------------------------------------------------------
\newcommand\UD@KeepKthOfLArgumentsEvaluateLMinusKDifferenceLoop[4]{%
  % #1: <K letters m>  
  % #2: <L letters m>
  % (For detecting whether K>L or K<=L, during the loop letters m will
  %  be removed both from #1 and #2 until at least one of these arguments 
  %  is empty.
  %  When the loop terminates with 0<K<=L, #1 will be empty and #2
  %  will hold an amount of letters m corresponding to the the 
  %  difference L-K.
  %  When the loop terminates with K>L, #1 will not be empty and #2
  %  will be empty.
  % )
  % #3: <K-1 letters m>
  % #4: <L letters m>
  % (#3 and #4 will be left untouched during the loop so they can be 
  %  used for performing appropriate action when loop terminates as
  %  it is known whether K>L.)
  \UD@CheckWhetherNull{#1}{% We have K<=L:
     \UD@KeepKthOfLArgumentsRemoveNArguments{%
       #3%
      }{%
       \UD@KeepKthOfLArgumentsRemoveNArguments{#2}{\UD@stopromannumeral}%
      }{}%
  }{%
    \UD@CheckWhetherNull{#2}{% We have K>L:
      \UD@KeepKthOfLArgumentsRemoveNArguments{#4}{\UD@stopromannumeral}{}%
    }{% We don't know yet whether K<=L or K>L, thus remove letters m and 
      % do another iteration:
      \expandafter\UD@PassFirstToSecond
      \expandafter{%
        \UD@firstoftwo{}#2%
      }{%
        \expandafter\UD@KeepKthOfLArgumentsEvaluateLMinusKDifferenceLoop
        \expandafter{%
          \UD@firstoftwo{}#1%
        }%
      }{#3}{#4}%
    }%
  }%
}%
%%-----------------------------------------------------------------------------
%% \UD@KeepKthOfLArgumentsRemoveNArguments{<N letters m>}%
%%                                        {<argument 1>}%
%%                                        {<argument 2>}%
%%                                        <sequence of consecutive 
%%                                         undelimited arguments>
%%.............................................................................
%% Removes the first N undelimited arguments from the <sequence of 
%% consecutive undelimited arguments>, then inserts  
%% <argument 1><argument 2>
%%
%% On the one hand when providing <argument 2> empty, you can use 
%% <argument 1> for nesting calls to \UD@KeepKthOfLArgumentsRemoveNArguments.
%% On the other hand you can provide a <space token> for stopping
%% \romannumeral-expansion as  <argument 1> and have the
%% macro grab the <K-th undelimited argument> from the <sequence of L 
%% consecutive undelimited arguments> as <argument 2>.
%%
\newcommand\UD@KeepKthOfLArgumentsRemoveNArguments[3]{%
  %% #1: <N letters m>  
  %% #2: <Argument 1>   
  %% #3: <Argument 2>
  \UD@CheckWhetherNull{#1}{#2#3}{%
    \UD@firstoftwo{%
      \expandafter\UD@KeepKthOfLArgumentsRemoveNArguments
      \expandafter{%
        \UD@firstoftwo{}#1%
      }{#2}{#3}%
    }%
  }%
}%
%%-----------------------------------------------------------------------------
%% End of code for \KeepKthOfLArguments.
\makeatother

\newcommand\MacroWithThirtyNestedParameters[1]{%
  This is paramter 1: \textbf{\KeepKthOfLArguments{1}{30}#1}\\
  This is paramter 2: \textbf{\KeepKthOfLArguments{2}{30}#1}\\
  This is paramter 3: \textbf{\KeepKthOfLArguments{3}{30}#1}\\
  This is paramter 4: \textbf{\KeepKthOfLArguments{4}{30}#1}\\
  This is paramter 5: \textbf{\KeepKthOfLArguments{5}{30}#1}\\
  This is paramter 6: \textbf{\KeepKthOfLArguments{6}{30}#1}\\
  This is paramter 7: \textbf{\KeepKthOfLArguments{7}{30}#1}\\
  This is paramter 8: \textbf{\KeepKthOfLArguments{8}{30}#1}\\
  This is paramter 9: \textbf{\KeepKthOfLArguments{9}{30}#1}\\
  This is paramter 10: \textbf{\KeepKthOfLArguments{10}{30}#1}\\
  This is paramter 11: \textbf{\KeepKthOfLArguments{11}{30}#1}\\
  This is paramter 12: \textbf{\KeepKthOfLArguments{12}{30}#1}\\
  This is paramter 13: \textbf{\KeepKthOfLArguments{13}{30}#1}\\
  This is paramter 14: \textbf{\KeepKthOfLArguments{14}{30}#1}\\
  This is paramter 15: \textbf{\KeepKthOfLArguments{15}{30}#1}\\
  This is paramter 16: \textbf{\KeepKthOfLArguments{16}{30}#1}\\
  This is paramter 17: \textbf{\KeepKthOfLArguments{17}{30}#1}\\
  This is paramter 18: \textbf{\KeepKthOfLArguments{18}{30}#1}\\
  This is paramter 19: \textbf{\KeepKthOfLArguments{19}{30}#1}\\
  This is paramter 20: \textbf{\KeepKthOfLArguments{20}{30}#1}\\
  This is paramter 21: \textbf{\KeepKthOfLArguments{21}{30}#1}\\
  This is paramter 22: \textbf{\KeepKthOfLArguments{22}{30}#1}\\
  This is paramter 23: \textbf{\KeepKthOfLArguments{23}{30}#1}\\
  This is paramter 24: \textbf{\KeepKthOfLArguments{24}{30}#1}\\
  This is paramter 25: \textbf{\KeepKthOfLArguments{25}{30}#1}\\
  This is paramter 26: \textbf{\KeepKthOfLArguments{26}{30}#1}\\
  This is paramter 27: \textbf{\KeepKthOfLArguments{27}{30}#1}\\
  This is paramter 28: \textbf{\KeepKthOfLArguments{28}{30}#1}\\
  This is paramter 29: \textbf{\KeepKthOfLArguments{29}{30}#1}\\
  This is paramter 30: \textbf{\KeepKthOfLArguments{30}{30}#1}%
}%

\begin{document}

\noindent
\MacroWithThirtyNestedParameters{%
  {Word 1}%
  {Word 2}%
  {Word 3}%
  {Word 4}%
  {Word 5}%
  {Word 6}%
  {Word 7}%
  {Word 8}%
  {Word 9}%
  {Word 10}%
  {Word 11}%
  {Word 12}%
  {Word 13}%
  {Word 14}%
  {Word 15}%
  {Word 16}%
  {Word 17}%
  {Word 18}%
  {Word 19}%
  {Word 20}%
  {Word 21}%
  {Word 22}%
  {Word 23}%
  {Word 24}%
  {Word 25}%
  {Word 26}%
  {Word 27}%
  {Word 28}%
  {Word 29}%
  {Word 30}%
}%

\vfill

\noindent\verb|\KeepKthOfLArguments| does deliver the result after two
expansion-steps:

\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \KeepKthOfLArguments{3}{11}{A}{B}{C}{D}{E}{F}{G}{H}{I}{J}{K}%
}%
\end{verbatim}

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \KeepKthOfLArguments{3}{11}{A}{B}{C}{D}{E}{F}{G}{H}{I}{J}{K}%
}%

\noindent\texttt{\string\test: \meaning\test}%

\vfill\vfill

\end{document}

enter image description here


In case you don't like to specify the amount of undelimited arguments but only which one to pick, if present, I can offer a macro \ExtractKthArg.

When doing macro-programming, you can pass an arbitrary sequence of undelimited arguments via nesting that sequence into a single undelimited macro argument and apply \ExtractKthArg on that single argument for extracting a specific element from the sequence.

\documentclass{article}

\makeatletter
%% Code for \ExtractKthArg
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond, \UD@Exchange,
%%    \UD@stopromannumeral, \UD@CheckWhetherNull
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
  \expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%=============================================================================
%% Extract K-th inner undelimited argument:
%%
%% \ExtractKthArg{<integer K>}%
%%               {<tokens in case list of undelimited args doesn't have a k-th argumnent>}%
%%               {<list of undelimited args>} %
%% 
%% In case there is no K-th argument in <list of indelimited args> : 
%%   Does deliver <tokens in case list of undelimited args doesn't have a k-th argumnent.
%% In case there is a K-th argument in <list of indelimited args> : 
%%   Does deliver that K-th argument with one level of braces removed.
%%
%% Examples:
%%
%%   \ExtractKthArg{0}{not available}{ABCDE} yields: not available
%%
%%   \ExtractKthArg{3}{not available}{ABCDE} yields:  C
%%
%%   \ExtractKthArg{3}{not available}{AB{CD}E} yields:  CD
%%
%%   \ExtractKthArg{4}{not available}{{001}{002}{003}{004}{005}} yields: 004
%%
%%   \ExtractKthArg{6}{not available}{{001}{002}{003}} yields: not available 
%% 
%%=============================================================================
\newcommand\ExtractKthArg[2]{%
  \romannumeral%
  % #1: <integer number K>
  % #2: <action if there is no K-th argument>
  \expandafter\UD@ExtractKthArgCheck
  \expandafter{\romannumeral\number\number#1 000}{#2}%
}%
\newcommand\UD@ExtractKthArgCheck[3]{%
  \UD@CheckWhetherNull{#1}{\UD@stopromannumeral#2}{% empty
    \expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}%
  }%
}%
\begingroup
\def\UD@ExtractFirstArgLoop#1{%
  \endgroup
  \@ifdefinable\UD@RemoveTillFrozenrelax{%
    \long\def\UD@RemoveTillFrozenrelax##1##2#1{{##1}}%
  }%
  \newcommand\UD@ExtractKthArgLoop[3]{%
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo##3{}.}{\UD@stopromannumeral##2}{%
      \UD@CheckWhetherNull{##1}{%
        \UD@ExtractFirstArgLoop{##3#1}%
      }{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}##3}%
        {\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}##1}{##2}}%
      }%
    }%
  }%
}%
\expandafter\expandafter\expandafter\UD@ExtractFirstArgLoop
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\ifnum0=0\fi}%
%% Usage of frozen-\relax as delimiter is for speeding things up by reducing the
%% amount of iterations needed. I chose frozen-\relax because David Carlisle 
%% pointed out in   <https://tex.stackexchange.com/a/578877>
%% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be
%% affected by \uppercase/\lowercase.
%%
%% \UD@ExtractFirstArg's argument may contain frozen-\relax:
%% The only effect is that internally more iterations are needed for
%% obtaining the result.
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\expandafter\UD@stopromannumeral\UD@firstoftwo#1{}}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}%
}%
%% End of code for \ExtractKthArg.
\makeatother

\newcommand\MacroWithThirtyNestedParameters[1]{%
  This is paramter 1: \textbf{\ExtractKthArg{1}{not available}{#1}}\\
  This is paramter 2: \textbf{\ExtractKthArg{2}{not available}{#1}}\\
  This is paramter 3: \textbf{\ExtractKthArg{3}{not available}{#1}}\\
  This is paramter 4: \textbf{\ExtractKthArg{4}{not available}{#1}}\\
  This is paramter 5: \textbf{\ExtractKthArg{5}{not available}{#1}}\\
  This is paramter 6: \textbf{\ExtractKthArg{6}{not available}{#1}}\\
  This is paramter 7: \textbf{\ExtractKthArg{7}{not available}{#1}}\\
  This is paramter 8: \textbf{\ExtractKthArg{8}{not available}{#1}}\\
  This is paramter 9: \textbf{\ExtractKthArg{9}{not available}{#1}}\\
  This is paramter 10: \textbf{\ExtractKthArg{10}{not available}{#1}}\\
  This is paramter 11: \textbf{\ExtractKthArg{11}{not available}{#1}}\\
  This is paramter 12: \textbf{\ExtractKthArg{12}{not available}{#1}}\\
  This is paramter 13: \textbf{\ExtractKthArg{13}{not available}{#1}}\\
  This is paramter 14: \textbf{\ExtractKthArg{14}{not available}{#1}}\\
  This is paramter 15: \textbf{\ExtractKthArg{15}{not available}{#1}}\\
  This is paramter 16: \textbf{\ExtractKthArg{16}{not available}{#1}}\\
  This is paramter 17: \textbf{\ExtractKthArg{17}{not available}{#1}}\\
  This is paramter 18: \textbf{\ExtractKthArg{18}{not available}{#1}}\\
  This is paramter 19: \textbf{\ExtractKthArg{19}{not available}{#1}}\\
  This is paramter 20: \textbf{\ExtractKthArg{20}{not available}{#1}}\\
  This is paramter 21: \textbf{\ExtractKthArg{21}{not available}{#1}}\\
  This is paramter 22: \textbf{\ExtractKthArg{22}{not available}{#1}}\\
  This is paramter 23: \textbf{\ExtractKthArg{23}{not available}{#1}}\\
  This is paramter 24: \textbf{\ExtractKthArg{24}{not available}{#1}}\\
  This is paramter 25: \textbf{\ExtractKthArg{25}{not available}{#1}}\\
  This is paramter 26: \textbf{\ExtractKthArg{26}{not available}{#1}}\\
  This is paramter 27: \textbf{\ExtractKthArg{27}{not available}{#1}}\\
  This is paramter 28: \textbf{\ExtractKthArg{28}{not available}{#1}}\\
  This is paramter 29: \textbf{\ExtractKthArg{29}{not available}{#1}}\\
  This is paramter 30: \textbf{\ExtractKthArg{30}{not available}{#1}}%
}%

\pagestyle{empty}

\begin{document}

\vspace*{-1in}\enlargethispage{1in}

\noindent\verb|\ExtractKthArg{-1}{not available}{ABCDE}|: \ExtractKthArg{-1}{not available}{ABCDE}\\
\verb|\ExtractKthArg{0}{not available}{ABCDE}|: \ExtractKthArg{0}{not available}{ABCDE}\\
\verb|\ExtractKthArg{1}{not available}{ABCDE}|: \ExtractKthArg{1}{not available}{ABCDE}\\
\verb|\ExtractKthArg{2}{not available}{ABCDE}|: \ExtractKthArg{2}{not available}{ABCDE}\\
\verb|\ExtractKthArg{3}{not available}{ABCDE}|: \ExtractKthArg{3}{not available}{ABCDE}\\
\verb|\ExtractKthArg{4}{not available}{ABCDE}|: \ExtractKthArg{4}{not available}{ABCDE}\\
\verb|\ExtractKthArg{5}{not available}{ABCDE}|: \ExtractKthArg{5}{not available}{ABCDE}\\
\verb|\ExtractKthArg{6}{not available}{ABCDE}|: \ExtractKthArg{6}{not available}{ABCDE}

\vfill

\noindent
\MacroWithThirtyNestedParameters{%
  {Word 1}%
  {Word 2}%
  {Word 3}%
  {Word 4}%
  {Word 5}%
  {Word 6}%
  {Word 7}%
  {Word 8}%
  {Word 9}%
  {Word 10}%
  {Word 11}%
  {Word 12}%
  {Word 13}%
  {Word 14}%
  {Word 15}%
  {Word 16}%
  {Word 17}%
  {Word 18}%
  {Word 19}%
  {Word 20}%
  {Word 21}%
  {Word 22}%
  {Word 23}%
  {Word 24}%
  {Word 25}%
  {Word 26}%
  {Word 27}%
  {Word 28}%
  {Word 29}%
  {Word 30}%
}%

\vfill

\noindent\verb|\ExtractKthArg| does deliver the result after two
expansion-steps:

\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \ExtractKthArg{3}{not available}{{A}{B}{C}{D}{E}{F}{G}{H}{I}{J}{K}}%
}%
\end{verbatim}

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \ExtractKthArg{3}{not available}{{A}{B}{C}{D}{E}{F}{G}{H}{I}{J}{K}}%
}%

\noindent\texttt{\string\test: \meaning\test}%

\vfill\vfill

\end{document}

enter image description here


With both examples everything works by means of expansion only. No temporary macros get defined from arguments. This implies that the arguments themselves can contain hashes. E.g., things like #1 don't yield error-messages as would be the case when "naively" defining temporary macros in terms of \def from such arguments. This also implies that arguments can contain consecutive hashes, e.g., ##, without them being reduced to a single hash # as would be the case at the time of expanding temporary macros defined from such arguments.


I recommend to use such things at best when list-processing-interfaces, e.g. key=value-interfaces, and processing of databases/data tables by means of pgfplots or datatool are not possible, and when furthermore the use of the many arguments is so interwoven that it is not possible to split the thing into several macros each of which grabs/processes only a subset of the arguments consisting of less than 10 arguments.
In practice, I have never encountered the latter not being possible.

1

Better way should be:

\newcommand\foo[9]{%
\def\tempa{#1}%
\def\tempb{#2}%
\def\tempc{#3}%
\def\tempd{#4}%
\def\tempe{#5}%
\def\tempf{#6}%
\def\tempg{#7}%
\def\temph{#8}%
\foocontinued%#9 <- remove it
}

\newcommand\foocontinued[7]{%
% Do whatever you want with your 8+7 arguments here.
}

Then later

\foo{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}{13}{14}{15}

e.g. NO doble bracket there.

But this solution is not elegant at all, but working.

2
  • 3
    What does this add to an already existing answer?
    – egreg
    Jun 13, 2020 at 21:18
  • Defining macros \tempa...\temph from the first eight arguments implies the need of expanding these macros. During expansion two consecutive hashes from a macro's definition text will collapse into one hash. In order to avoid/compensate this you can use \edef\tempa{\unexpanded{#1}}% instead of \def\tempa{#1}%, etc. Jul 30, 2020 at 23:03
0

I was having the same error as @Herman-Jaramillo, so here is my correction of @TH's answer:

\newcommand\foo[9]{%
    \def\tempa{#1}%
    \def\tempb{#2}%
    \def\tempc{#3}%
    \def\tempd{#4}%
    \def\tempe{#5}%
    \def\tempf{#6}%
    \def\tempg{#7}%
    \def\temph{#8}%
    \foocontinued#9
}
\newcommand\foocontinued[7]{%
    % Do whatever you want with your 8+7 arguments here.
}

Then later:

\foo{1}{2}{3}{4}{5}{6}{7}{8}{{9}{10}{11}{12}{13}{14}{15}}

Notice that 9 through 15 are themselves in brackets. This solution is not perfect, since you cannot use 15 sets of brackets next to each other as cleanly as one might desire, but it worked for me.

1
  • Defining macros \tempa...\temph from the first eight arguments implies the need of expanding these macros. During expansion two consecutive hashes from a macro's definition text will collapse into one hash. In order to avoid/compensate this you can use \edef\tempa{\unexpanded{#1}}% instead of \def\tempa{#1}%, etc. Jul 30, 2020 at 23:26

You must log in to answer this question.

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