[Tex/LaTex] How to define a new command with variable number of parameters

loopsmacrosparameters

I would like to create new command with functionality like this (pseudo code):

\newcommand{\myCommand}[numOfParameters]  { 
  for (i = 0; i < numOfParameters / 3; i++) {
     \somecommand1{#(i*3 + 1)}
     \somecommand1{#(i*3 + 2)}
     \somecommand1{#(i*3 + 3)}
  }
}

Is it possible to implement such like this in LaTeX ?

Here is example of usage (by request):

Here is a macros to show 2 images per row …

\newcommand{\twoimages}[6] {
    \begin{figure}[htb!]
        \begin{minipage}[h]{0.49\linewidth}
            \begin{center}
                \includegraphics[width=#3\linewidth]{#1} \\ 
                \caption{#2} 
                \label{ris:#1}
            \end{center}
        \end{minipage}
        \hfill
        \begin{minipage}[h]{0.49\linewidth}
            \begin{center}
                \includegraphics[width=#6\linewidth]{#4} \\ 
                \caption{#5}
                \label{ris:#4}
            \end{center}
        \end{minipage}
    \end{figure}
}

I'd like to make macros for N (user-defined number) images

Best Answer

You can define recursive macros that call themselves until some termination is found; a solution to your problem can be as follows:

\makeatletter % we need to use kernel commands
\newcommand{\twoimages}{%
  \begin{figure}[!htb]
  \@twoimagesi
}
\newcommand\@twoimagesi{\@ifnextchar\stopimages{\@twoimagesend}{\@twoimagesii}}

\newcommand\@twoimagesii[6]{%
  \@twoimagesiii{#1}{#2}{#3}\hfill
  \@twoimagesiii{#4}{#5}{#6}\\[\bigskipamount]
  \@twoimagesi % restart the recursion
}
\newcommand\@twoimagesiii[3]{%
  \begin{minipage}{0.49\linewidth}
    \centering
    \includegraphics[width=#3\linewidth]{#1}
    \caption{#2}\label{ris:#1}
  \end{minipage}}
\newcommand\@twoimagesend[1]{% The argument is \stopimages
  \vspace*{-\bigskipamount}
  \end{figure}}
\makeatother

The macro \twoimages really has no argument; it simply starts the business by opening the figure environment and calls the real command, \@twoimagesi. This command checks whether it's followed by \stopimages; if it is, it calls \@twoimagesend that ends the environment; otherwise it executes \@twoimagesii that has the job of printing a row, after having absorbed its six arguments. It does so in an indirect way, so that we can write only once the same minipage, using the first three arguments and the last three.

Then \@twoimagesii restarts the recursion by calling \@twoimagesi again.


Let's see a complete example (I use the demo option to graphicx that draws a black blob instead of requiring external files).

\documentclass{article}
\usepackage[demo]{graphicx}

\makeatletter % we need to use kernel commands
\newcommand{\twoimages}{%
  \begin{figure}[!htb]
  \@twoimagesi
}
\newcommand\@twoimagesi{\@ifnextchar\stopimages{\@twoimagesend}{\@twoimagesii}}

\newcommand\@twoimagesii[6]{%
  \@twoimagesiii{#1}{#2}{#3}\hfill
  \@twoimagesiii{#4}{#5}{#6}\\[\bigskipamount]
  \@twoimagesi % restart the recursion
}
\newcommand\@twoimagesiii[3]{%
  \begin{minipage}{0.49\linewidth}
    \centering
    \includegraphics[width=#3\linewidth]{#1}
    \caption{#2}\label{ris:#1}
  \end{minipage}}
\newcommand\@twoimagesend[1]{% The argument is \stopimages
  \vspace*{-\bigskipamount}
  \end{figure}}
\makeatother

\begin{document}
\twoimages
  {a}{Caption left}{.5}
  {b}{Caption right}{.5}
  {c}{Caption left}{.5}
  {d}{Caption right}{.5}
\stopimages
\end{document}

enter image description here


A quite different approach is via the expl3 macros. Also the syntax will be different; I'll present the example, which is almost self-explanatory: the main macro splits its argument at the commas and each chunk is processed in a very similar way as before.

\documentclass{article}
\usepackage[demo]{graphicx}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\twoimages}{ >{ \SplitList {,} } m }
 {
  \begin{figure}[!htb]
  \centering
  \ProcessList { #1 } { \davs__twoimages_row:n }
  \vspace{-\bigskipamount}
  \end{figure}
 }
\cs_new_protected:Nn \davs__twoimages_row:n
 {
  \davs__twoimages_row:nnnnnn #1
 }
\cs_new_protected:Nn \davs__twoimages_row:nnnnnn
 {
  \davs__twoimages_do:nnn { #1 } { #2 } { #3 } 
  \hfill
  \davs__twoimages_do:nnn { #4 } { #5 } { #6 }
  \\[\bigskipamount]
 }
\cs_new_protected:Nn \davs__twoimages_do:nnn
 {
  \begin{minipage}{0.49\linewidth}
    \centering
    \includegraphics[width=#3\linewidth]{#1}
    \caption{#2}\label{ris:#1}
  \end{minipage}
 }
\ExplSyntaxOff
\begin{document}

\twoimages{
  {a}{Caption left}{.5}
  {b}{Caption right}{.5},
  {c}{Caption left}{.5}
  {d}{Caption right}{.5}
}

\end{document}

The result is exactly the same as before. There can be any number of sixtuples of arguments, separated by a comma.


Just for completeness, a version that accepts any number of images; now each triple should be separated by a comma; if the number of images is odd, the last one will be centered.

\documentclass{article}
\usepackage[demo]{graphicx}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\twoimages}{ >{ \SplitList {,} } m }
 {
  \begin{figure}[!htb]
  \centering
  \leftskip=\fill \rightskip=\fill
  \lineskip=\baselineskip % to compensate for the minipages
  \ProcessList { #1 } { \__davs_process_argument:n }
  \end{figure}
 }
\cs_new_protected:Nn \__davs_process_argument:n
 {
  \__davs_image:nnn #1
 }
\cs_new_protected:Nn \__davs_image:nnn
 {
  \hspace*{\fill}
  \begin{minipage}{0.49\linewidth}
    \centering
    \includegraphics[width=#3\linewidth]{#1}
    \caption{#2}\label{ris:#1}
  \end{minipage}
  \hspace*{\fill}
  \penalty0 % we provide a break point
 }
\ExplSyntaxOff
\begin{document}

\twoimages{
  {a}{Caption left}{.5},
  {b}{Caption right}{.5},
  {c}{Caption left}{.5},
  {d}{Caption right}{.5},
  {e}{Caption center}{.5}
}

\end{document}