5

This question is based on this answer.
It is a continuation of my previous qestion regarding list of values in latex.

According to first link there is a limitation on the number of arguments I have faced with.

I tried to use specified approach and it works well.

But what if I want to have more than 18 arguments?

Is it possible to use such approach recursively?

For example I want to define a function with 25 arguments. Is it possible to use next:

\newcommand\ParseOptMenuItemStoreA[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}%
    \ParseOptMenuItemStoreB
}

\newcommand\ParseOptMenuItemStoreB[9]{%
    \def\tempj{#1}%
    \def\tempk{#2}%
    \def\templ{#3}%
    \def\tempm{#4}%
    \def\tempn{#5}%
    \def\tempo{#6}%
    \def\tempp{#7}%
    \def\tempq{#8}%
    \def\tempr{#9}%
    \ParseOptMenuItem
}

\newcommand\ParseOptMenuItem[7]{%
    % Access to arguments #1-#18 via \tempa-\tempr and to argument #19-#25 via #1-#7.
}

I tried to compile it but I got an error:

! Missing number, treated as zero.
<to be read again>
               \protect
l.40 }  

So what I do wrong?

UPDATE 19.08.2015:

I have used next construction to achieve my goals:

\newcommand{\ParseOptMenuItemList}[1]
{
\def\tmplist{#1}%
\@tempcnta=\z@
\@for\tmp:=\tmplist\do{\advance\@tempcnta\@ne
\expandafter\let\csname temp\@roman\@tempcnta\endcsname\tmp
}%
\makebox[\linewidth][r]{%
    \begin{tabular}{lc}
        Possible values: \ifthenelse{\equal{\tempi}{}}{}{& \tempi\\}
        \ifthenelse{\equal{\tempii}{}}{}{& \tempii\\}
        \ifthenelse{\equal{\tempiii}{}}{}{& \tempiii\\}
        \ifthenelse{\equal{\tempiv}{}}{}{& \tempiv\\}
        \ifthenelse{\equal{\tempv}{}}{}{& \tempv\\}
        \ifthenelse{\equal{\tempvi}{}}{}{& \tempvi\\}
        \ifthenelse{\equal{\tempvii}{}}{}{& \tempvii\\}
        \ifthenelse{\equal{\tempviii}{}}{}{& \tempviii\\}
        \ifthenelse{\equal{\tempix}{}}{}{& \tempix\\}
        \ifthenelse{\equal{\tempx}{}}{}{& \tempx\\}
        \ifthenelse{\equal{\tempxi}{}}{}{& \tempxi\\}
        \ifthenelse{\equal{\tempxii}{}}{}{& \tempxii\\}
        \ifthenelse{\equal{\tempxiii}{}}{}{& \tempxiii\\}
        \ifthenelse{\equal{\tempxiv}{}}{}{& \tempxiv\\}
        \ifthenelse{\equal{\tempxv}{}}{}{& \tempxv\\}
        \ifthenelse{\equal{\tempxvi}{}}{}{& \tempxvi\\}
        \ifthenelse{\equal{\tempxvii}{}}{}{& \tempxvii\\}
        \ifthenelse{\equal{\tempxviii}{}}{}{& \tempxviii\\}
      Default value: & \tempxix\\
    \end{tabular}
}
}

\ParseOptMenuItemList takes values as a comma separated list.

4
  • 5
    Regardless if this possible (I think so): Don't do such weird things. Even commands with just three or four arguments become hard to remember which argument stands for which purpose. Use a keyvalue - syntax rather! – user31729 Jul 8 '15 at 13:30
  • 4
    Please make your code compilable (if possible), or at least complete it with \documentclass{...}, the required \usepackage's, \begin{document}, and \end{document}. That may seem tedious to you, but think of the extra work it represents for TeX.SX users willing to give you a hand. Help them help you: remove that one hurdle between you and a solution to your problem. – Paul Gaborit Jul 8 '15 at 13:33
  • 4
    You really should not define a command that takes more than 2 or three arguments, use a comma separated list \foo{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q} – David Carlisle Jul 8 '15 at 13:33
  • Your approach works by recursively specifying macros that are nested within one another. As such, the answer to your main question is still the same as in the linked post: How to define a command that takes more than 9 arguments. As such, I'm voting to close it as a duplicate. – Werner Aug 19 '15 at 22:38
5

Using multiple {} makes for a very hard to use interface different from all other latex commands, latex has standard facilities for handling arguments as comma separated lists (\usepackage{array,bm,graphics} for example).

The following produces

argument 24 is: [x]

on the terminal confirming that argument 24 (at least:-) has been captured.

\makeatletter
\newcommand\foo[1]{%
\def\tmplist{#1}%
\@tempcnta=\z@
\@for\tmp:=\tmplist\do{\advance\@tempcnta\@ne
\expandafter\let\csname temp\@roman\@tempcnta\endcsname\tmp
}%
\typeout{argument 24 is: [\tempxxiv]}%
}


\makeatother


\foo{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}

\stop
1
  • In spirit I agree with your advice. But still after years of using LaTeX, your code looks like black magic to me, and I'd rather use lots of braces instead of it. – Turion Feb 17 '18 at 13:22
5

Here's another one, this one you have to call \definemanyarguments{24} and the next 24 arguments will be “saved”, then you use them with \argument{12}.

Here's a full document.

\documentclass{scrartcl}

\newcounter{manyarguments}
\providecommand\gobbleone[1]{}
\providecommand\useone[1]{#1}
\newcommand*\definemanyarguments[1]
  {\setcounter{manyarguments}{0}%
   \def\nextargument{\stepcounter{manyarguments}%
     \ifnum\value{manyarguments}>#1 \expandafter\gobbleone
     \else\expandafter\useone\fi
     {\afterassignment\nextargument
      \csdefignorespaces{argumentnumber\arabic{manyarguments}}}}%
   \nextargument}
\newcommand\csdefignorespaces[2]{\expandafter\def\csname#1\endcsname{#2}}
\newcommand*\argument[1]{\csname argumentnumber#1\endcsname}

\begin{document}

\definemanyarguments{25}
  {1}{2}{3}{4}{5}{6}{7}{8}{9}{a}{b}{c}{d}{e}{f}{g}{h}{i}
  {A}{B}{C}{D}{E}{F}{last argument}

[\argument{2}]
[\argument{12}]
[\argument{23}]
[\argument{24}]
[\argument{25}]

\end{document}
2
  • If you don't want this interface, and want indeed something like defining a \macrowithtwentyfivearguments{<1st>}..{<25th>} that directly acts I can change it. Whay I wrote first defines the arguments and then lets you use them, but you tell me, OP. – Manuel Jul 8 '15 at 14:39
  • Not op but this is great! It doesn't seem to work in tables tho; is there a specific reason as to why? – generallyconfuzzled Mar 2 at 15:51
5

I can see no sensible usage of macros with more than three or four arguments. However, here's an implementation of \newextracommand that accepts any number of arguments (limited by TeX's arithmetic capability, so not more than 230–1 and, of course, by memory).

\documentclass{article}

\newcommand\newextracommand[3]{%
  \newcounter{extra\string#1}%
  \newcommand{#1}{\extrarecurse{#1}{#2}{#3}}%
}

\newcommand\extrarecurse[3]{%
  \setcounter{extra\string#1}{1}%
  \grabarg{#1}{#2}{#3}%
}

\makeatletter
\newcommand{\grabarg}[4]{%
  \ifnum#2<\value{extra\string#1}\relax
    \expandafter\@secondoftwo
  \else
    \expandafter\@firstoftwo
  \fi
  {%
   \@namedef{arg@\romannumeral\value{extra\string#1}}{#4}
   \stepcounter{extra\string#1}%
   \grabarg{#1}{#2}{#3}
  }
  {#3}%
}
\newcommand\doarg[1]{\csname arg@\romannumeral#1\endcsname}
\makeatother

\newextracommand{\foo}{20}{%
  \doarg{1}--\doarg{2}--\doarg{3}--\doarg{4}--%
  \doarg{5}--\doarg{6}--\doarg{7}--\doarg{8}--%
  \doarg{9}--\doarg{10}--\doarg{11}--\doarg{12}--%
  \doarg{13}--\doarg{14}--\doarg{15}--\doarg{16}--%
  \doarg{17}--\doarg{18}--\doarg{19}--\doarg{20}%
}

\begin{document}

\foo{a}{b}{c}{d}{e}{f}{g}{h}{i}{j}{k}{l}{m}{n}{o}{p}{q}{r}{s}{t}

\end{document}

Arguments in the body of the definition are represented by \doarg{<number>}.

enter image description here

Don't try using such commands in a table cell and don't try nesting calls to the same command so defined.

2
  • 6
    I hope, that 2^30 - 1 arguments are sufficient then. I recently thought of defining one with 2^30 args... ;-) – user31729 Jul 8 '15 at 14:20
  • I see you did something similar to what I intended (but faster, as always). – Manuel Jul 8 '15 at 14:39
3

This is an approach with xkeyval, providing 25 keys named keyA to keyY. This has the advantage, that it's not necessary the position of th argument.

\documentclass{article}

\usepackage{etoolbox}
\usepackage{xkeyval}

\makeatletter

\newcommand{\defkey}[1]{%
\define@key{myargs}{key#1}{%
    \expandafter\gdef\csname KVMacroKey#1\endcsname{##1}%
  }
}
\defkey{A}
\defkey{B}
\defkey{C}
\defkey{D}
\defkey{E}
\defkey{F}
\defkey{G}
\defkey{H}
\defkey{I}
\defkey{J}
\defkey{K}
\defkey{L}
\defkey{M}
\defkey{N}
\defkey{O}
\defkey{P}
\defkey{Q}
\defkey{R}
\defkey{S}
\defkey{T}
\defkey{U}
\defkey{V}
\defkey{X}
\defkey{Y}





\makeatother

\newcommand{\foo}[1][]{%
  \setkeys{myargs}{#1}%
  \KVMacroKeyA

  \KVMacroKeyB

  \KVMacroKeyC
}


\begin{document}

\foo[keyA=Hello,keyB=World,keyC={How are you}]

\foo[keyC={How are you},keyA=Hello,keyB=World]

\end{document}
1

It seems to work for me:

\documentclass{article}

\newcommand\ParseOptMenuItemStoreA[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}%
    \ParseOptMenuItemStoreB
}

\newcommand\ParseOptMenuItemStoreB[9]{%
    \def\tempj{#1}%
    \def\tempk{#2}%
    \def\templ{#3}%
    \def\tempm{#4}%
    \def\tempn{#5}%
    \def\tempo{#6}%
    \def\tempp{#7}%
    \def\tempq{#8}%
    \def\tempr{#9}%
    \ParseOptMenuItem
}

\newcommand\ParseOptMenuItem[7]{%
    % Access to arguments #1-#18 via \tempa-\tempr and to argument #19-#25 via #1-#7.
    before

    #7

    tempj: \tempj

    tempi: \tempi

    after
}

\begin{document}
\ParseOptMenuItemStoreA{1}{2}{3}{4}{5}{6}{7}{8}{9}{a}{b}{c}{d}{e}{f}{g}{h}{i}{A}{B}{C}{D}{E}{F}{last argument}
\end{document}

But you probably shouldn't be doing this anyway for the reasons already given by David Carlisle and Christian Hupfer.

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