4

I have a command that needs more than 9 arguments. I stumbled over this solution: How to define a command that takes more than 9 arguments. This works fine in regular text. But I tried using this within a tabular environment, but then I get undefined control sequence errors. Here is a MWE:

\documentclass{article}
\begin{document}
\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}%
 \foocont
}
\newcommand\foocont[1]{\tempa & \tempb & \tempc & \tempd & \tempe & \tempf & \tempg & \temph & \tempi & #1 \cr}

\begin{tabular}{*{10}c}
\foo{1}{2}{3}{4}{5}{6}{7}{8}{9}{a}
\end{tabular}
\end{document}

If you remove all &, the \cr and the tabular environment, and just call \foo with 10 arguments the code compiles just fine (printing out 123456789a).

How can I make this "argument extension" work together with tabular?

  • 1
    \gdef or \xdef but I doubt this is a very good way – user31729 Jul 3 '15 at 14:27
  • 2
    why not have a more usual, and more easily extendible, syntax such as \foo{1,2,3,4,5,6,7,8,9,a,b,c,d} ? – David Carlisle Jul 3 '15 at 14:31
6

The first table cell, where \foo is executed, is also a local group. After the next & the local meanings of \tempb, \tempc, ... are lost.

There are several ways

Global definitions

\documentclass{article}
\begin{document}
\newcommand*\foo[9]{%
 \gdef\tempa{#1}%
 \gdef\tempb{#2}%
 \gdef\tempc{#3}%
 \gdef\tempd{#4}%
 \gdef\tempe{#5}%
 \gdef\tempf{#6}%
 \gdef\tempg{#7}%
 \gdef\temph{#8}%
 \gdef\tempi{#9}%
 \foocont
}
\newcommand\foocont[1]{\tempa & \tempb & \tempc & \tempd & \tempe & \tempf &
\tempg & \temph & \tempi & #1 \cr}

\begin{tabular}{*{10}c}
\foo{1}{2}{3}{4}{5}{6}{7}{8}{9}{a}
\end{tabular}
\end{document}

Result

Expansion

The following example expands the \temp... macros via \protected@edef. This supports LaTeX protection mechanism via \protect and robust commands. The \temp... macros can even be assigned locally only:

\documentclass{article}
\begin{document}
\newcommand*\foo[9]{%
  \begingroup
  \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}%
  \foocont
}
\makeatletter
\newcommand*\foocont[1]{%
  \protected@edef\process@me{%
    \endgroup
    \tempa & \tempb & \tempc & \tempd & \tempe & \tempf &
    \tempg & \temph & \tempi & #1 \cr
  }%
  \process@me
}
\makeatother

\begin{tabular}{*{10}c}
\foo{1}{2}{3}{4}{5}{6}{7}{8}{9}{a}
\end{tabular}
\end{document}

One level expansion

The use of \protect for fragile commands can be avoided, if the \temp... macros are only expanded once. The command \unexpanded of e-TeX helps. An alternative without e-TeX uses token register.

\documentclass{article}
\begin{document}
\newcommand*\foo[9]{%
  \begingroup
  \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}%
  \foocont
}
\newcommand*\foocont[1]{%
  \edef\processme{%
    \endgroup
    \unexpanded\expandafter{\tempa} &
    \unexpanded\expandafter{\tempb} &
    \unexpanded\expandafter{\tempc} &
    \unexpanded\expandafter{\tempd} &
    \unexpanded\expandafter{\tempe} &
    \unexpanded\expandafter{\tempf} &
    \unexpanded\expandafter{\tempg} &
    \unexpanded\expandafter{\temph} &
    \unexpanded\expandafter{\tempi} & #1 \cr
  }%
  \processme
}

\begin{tabular}{*{10}c}
\foo{1}{2}{3}{4}{5}{6}{7}{8}{9}{a}
\end{tabular}
\end{document}
2

As David suggests in the comments, another approach is use a comma separated syntax:

\foo{1,2,3,4,5,6,7,8,9,a}

You can then loop over the entries and put them into place. There are still some expansion issues because we are working inside a table but you can get around these using the etoolbox to build each row as you loop over the arguments using \foreach from pgffor.

The main advantage of doing it this way is that the code easily expands to an arbitrary number of arguments. Secondly, the code is shorter and easier to maintain because you do not need to treat each argument specially, as Heiko had to do. If you really want you can check that the number of arguments is correct, but this seems like overkill to me as too few rows will be clear from the output and too many will automatically give an error.

I changed the MWE to convince myself that this is working to get:

enter image description here

Here is the code:

\documentclass{article}
\usepackage{pgffor}
\usepackage{etoolbox}
\newcommand\foo[1]{%
  \global\def\sep{}% will expand to & except for the first column
  \global\let\myrow\relax% will become the row built from #1
  \foreach \arg in {#1} {% loop over #1 to build \myrow
     \xappto\myrow{\sep\arg}% global expansion
     \gdef\sep{&}% make the next separator &
  }%
  \myrow\\% print the row
}

\begin{document}

\begin{tabular}{*{10}{c|}}
  \foo{1,2,3,4,5,6,7,8,9,a}
  \foo{111,22,,444,55,{10,000},77,88,99,a}
\end{tabular}

\end{document}

(Btw, as is explained in the documentation of the booktabs package, using vertical lines to separate the columns is generally a bad idea. I have done it above only to show that the code is working.)

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.