6

I can't find the problem in this code

\documentclass{article}

\newcount\mt
\newcount\mtt
\newcommand{\mttab}[2]{%
    \mt=0
    \loop
        \advance\mt by 1
        \mtt=1
        {\loop
           \advance\mtt by 1
           &
          \ifnum\mtt<#2
    \repeat}
    \\
    \ifnum\mt<#1
    \repeat
}
\begin{document}

\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3  \\
\hline
\mttab{15}{3}
\end{tabular}

\end{document}

Edit

the aim of this question is the simplest way to create an empty tabular

  • 1
    & begins/ends a group, and you can't straddle conditionals across it. You'll have to accumulate the row construction in a macro and then execute it. – Werner Mar 11 '15 at 6:50
  • 1
    It has been asked several times, I believe. Look at tex.stackexchange.com/search?q=%2Bloop+%2Btabular and choose your duplicate. – egreg Mar 11 '15 at 8:01
  • It is too bad this question has been closed. The OP's code had some problems which could be explained and fixed. – user4686 Mar 11 '15 at 9:39
  • 2
    @egreg The vastitude of answers your search provides is an obstacle. I think when a question comes many times it deserves many answers. For example: why do we live and die? – user4686 Mar 11 '15 at 9:42
  • 1
    @jfbu You can also drop one answer here tex.stackexchange.com/questions/227084/… – percusse Mar 11 '15 at 11:06
11

Before answering How, which does not have a unique answer (your solution is a fine working answer), I will try to document Why your initial attempt failed. This knowledge could be handy to analyse more complicated situations.

First of all I recall that the crucial point is that you are trying to expand some macro from inside the tabular.

  1. tabular cells create groups, hence all assignments should be made global. I would thus recommend here to use LaTeX counters as in LaTeX they are always managed globally.

  2. it is not possible from inside a tabular cell to use a pre-defined TeX delimited macro (having a definition such as \def\foo #1\repeat{stuff with #1}) if #1 will get a visible &: \foo A & B\repeat C, except if \foo is the very first thing in the cell (meaning that nothing non-expandable like a \relax or a \def or a \setcounter appeared before).

Fixes:

  1. if you need to make definitions or assignments which will influence further cells, you must make them global,

  2. if you need to fetch the & as a part of a delimited macro it must be somehow hidden, typically inside a braced group, or written as \TAB with a prior \def\TAB{&} or in something like \firstofone{&} (\def\firstofone #1{#1}).

If we were to fix your original code according to these requirements, would it solve the problem?

Not yet, because the LaTeX loop construct does a local \def:

$ latexdef loop

\loop:
\long macro:#1\repeat ->\def \iterate {#1\relax \expandafter \iterate \fi }\iterate \let \iterate \relax 

Your own answer gives a correct way out of this : you use all of your things in a single cell, the preparation of the macro with the tokens to be inserted being done entirely via \g@addto@macro. This solves efficiently your problem, and you can use \count's rather than counters. What about point 2. above? well the & is appropriately hidden in the \emttok{&} (this was not necessary for the external loop, but the inner \loop without it would have seen the naked &).

Let me briefly discuss another way, which does not proceed by construction of a \mttok, and tries to work row after row, cell after cell. The most elegant is some expandable loop, but let's stick first with LaTeX's \loop and see what can be done.

First we need to let it to a global definition of \iterate. But immediately we hit against the problem of nesting. With this approach, the only way is to define two distinct loop macros, for example \gloop and \ggloop. Before doing so, there is more problem to mention.

There is an issue with the \relax after the #1 in the definition of \iterate. For example this will do \ifnum 1=1\relax\expandafter\iterate\fi. But this is catastropic because the \relax will properly terminate the test but it will stay in the token stream, and after a \\ this forces the start of a new row. Thus there will be a problem with an empty partial row, at the bottom of the tabular. We must get rid of the \relax. We could replace it by a space to be certain to terminate an \ifnum test, or we make sure we always use \ifnum with self-terminating numbers, moving the constraint on the caller side. Doing all this we end up with this approach:

\documentclass{article}

\newcommand*\TAB{&}

% alternative to LaTeX's loop, for use in contexts were definitions
% must be made global to survive

% Thus we do global things, and we use distinct terminators to allow nesting.
% Careful that tests at the end of #1 must be self-terminating

\long\def\gloop #1\grepeat 
    {\gdef \giterate {#1\expandafter \giterate \grepeat }\giterate  }

\long\def\ggloop #1\ggrepeat 
    {\gdef \ggiterate {#1\expandafter \ggiterate \ggrepeat }\ggiterate  }

\let\grepeat\fi
\let\ggrepeat\fi

\newcounter{mt}
\newcounter{mtt}

\newcommand{\mttab}[2]{%
    \setcounter{mt}{0}%
    \gloop
      \stepcounter{mt}% careful with spaces from line endings
      \setcounter{mtt}{1}%
      (\themt)     % to see what happens
        \ggloop 
           \themtt % again to see where we are
           \stepcounter{mtt}%
           \TAB
        \ifnum #1>\value{mtt}%
        \ggrepeat 
      \themtt
      \\
    \ifnum #2>\value{mt}%
    \grepeat
}

\begin{document}
\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3\\
\hline
\mttab{3}{15}
\hline
\end{tabular}

\end{document}

This produces (I know you asked for empty cells, but I added contents to visualize things):

enter image description here

The yet more elegant way is proceed with an expandable loop. LaTeX has one: \@whilesw. This answer explained how to use it. Here it would give the following approach:

\documentclass{article}

\newcounter{mt}
\newcounter{mtt}

% recycling the \@whilesw of LaTeX into a num test
% \ifnum tests must be properly self-terminating
% I define the \xwhilenum slightly differently from my earlier use of it
% https://tex.stackexchange.com/a/142562/4686

\makeatletter
  \newcommand*{\xwhilenum}[1]{\@whilesw{\ifnum #1}\fi }
\makeatother

% for reference, the LaTeX kernel definitions:
%\@whilesw:
%\long macro:#1\fi #2->#1#2\@iwhilesw {#1#2}\fi \fi 
%\@iwhilesw:
%\long macro:#1\fi ->#1\expandafter \@iwhilesw \else \@gobbletwo \fi {#1}\fi

\newcommand{\mttab}[2]{%
    \setcounter{mt}{#2}%
    \xwhilenum{\value{mt}>0 }% space after 0 is important
    {% must use braces here
      \addtocounter{mt}{-1}%
      \setcounter{mtt}{#1}%
      (\themt)     % to see where we are
      \xwhilenum{\value{mtt}>1 }% careful, N cells = N-1 &
      {%
         \themtt % again to see where we are
         \addtocounter{mtt}{-1}%
          &
      }% end of inner \xwhilenum
      \themtt
      \\
    }% end of external \xwhilenum
}

\begin{document}
\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3\\
\hline
\mttab{3}{15}
\hline
\end{tabular}
\end{document}

It gives (for a change I counted downwards):

enter image description here

A yet more elegant way perhaps is to dispense completely from using counters: one can do purely expandable loops thanks to \numexpr. But my answer is already a bit long, thus I will stop here.

6

Producing rows of empty cells given the headers is quite easy with expl3: just nest two \prg_replicate:nn cycles.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\emptytab}{mm}
 {% #1 is the comma separated list of headers, #2 the number of empty rows
  \touhami_emptytab:nn { #1 } { #2 }
 }

\int_new:N \l_touhami_emptytab_cols_int
\clist_new:N \l_touhami_emptytab_headers_clist

\cs_new_protected:Npn \touhami_emptytab:nn #1 #2
 {
  % store the headers in a clist
  \clist_set:Nn \l_touhami_emptytab_headers_clist { #1 }
  % count the number of headers
  \int_set:Nn \l_touhami_emptytab_cols_int { \clist_count:n { #1 } }
  % make a tabular
  \begin{tabular}{|*{\l_touhami_emptytab_cols_int}{c|}}
  \hline
  \clist_use:Nn \l_touhami_emptytab_headers_clist { & } \\ \hline
  \prg_replicate:nn { #2 } % repeat #2 times
   {
    % produce <number of cols - 1> cell separators
    \prg_replicate:nn { \l_touhami_emptytab_cols_int - 1 } { & }
    \\ \hline
   }
  \end{tabular}
 }
\ExplSyntaxOff

\begin{document}

\emptytab{col1,col2,col3}{15}\qquad
\emptytab{col1,col2}{3}

\end{document}

enter image description here

4

Here is a solution

\documentclass{article}
\newcommand{\mttok}{}
\newcount\mt
\newcount\mtt
\makeatletter
\newcommand{\emttok}[1]{\g@addto@macro{\mttok}{#1}}
\makeatother
\newcommand{\mttab}[2]{%
\renewcommand{\mttok}{}
    \mt=0
    \loop
        \advance\mt by 1
        \mtt=1
        {\loop
           \advance\mtt by 1
           \emttok{&}%
          \ifnum\mtt<#2
    \repeat}%
    \emttok{\\\hline}%
    \ifnum\mt<#1
    \repeat\mttok
}
\begin{document}

\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3  \\
\hline
\mttab{15}{3}
\end{tabular}

\end{document}
3

If you need to create an empty table with text only in the first line you needn't to use tabular environment (alias \halign) because the dimensions of the cells can be pre-calculated when processing the first line. My example shown below does measurement of the words in the first line and uses the most wide word as the \tmpdim width and \tmpdim+1em will be the width of all cells. The positive effect: all cells have the same width even thought the words have different width. And no tabular, no \halign but only \hbox/\vbox are used.

\newdimen\tmpdim
\newcount\tmpnum
\newbox\tabline

\def\struttab{\lower4pt\vbox to15pt{}}
\def\emptytab#1#2{\tmpdim=0pt \emptytabA#1,,\emptytabB\emptytabC#1,,\emptytabD{#2}}
\def\emptytabA#1,{\ifx,#1,\advance\tmpdim by1em \else
   \setbox0=\hbox{#1}%
   \ifdim\tmpdim<\wd0 \tmpdim=\wd0 \fi
   \expandafter\emptytabA\fi
}
\def\emptytabB{\vbox\bgroup\hrule \setbox\tabline=\hbox{}\hbox\bgroup\struttab}
\def\emptytabC#1,{\ifx,#1,\vrule\egroup\hrule \else
   \vrule\hbox to\tmpdim{\hfil#1\hfil}%
   \global\setbox\tabline=\hbox{\unhbox\tabline \vrule\hbox to\tmpdim{}}%
   \expandafter\emptytabC\fi
}
\def\emptytabD#1{\tmpnum=0
   \loop
      \ifnum\tmpnum<#1
         \advance\tmpnum by1
         \hbox{\struttab\unhcopy\tabline\vrule}\hrule
         \repeat
   \egroup
}

\emptytab{first,second,third,four}{15}

A good rule: if you are creating a table with fixed width of cells then you needn't to use tabular/\halign tools becuase \hbox to is sufficient.

  • i will see this later today – touhami May 14 '15 at 7:21
2

Here's a slightly different approach. I stick with your \loop approach, though I do rely on the etex macro \unexpanded.

\documentclass{article}

\makeatletter

\newcommand\mttab[2]{%%
  \ae@build@\ae@table@row{#2}{}{&}{\noexpand\\\noexpand\hline}%%
  \ae@build@\ae@table{#1}{\unexpanded\expandafter{\ae@table@row}}{}{}%%
  \ae@table
}

%% |------------------------------------------|
%% | "item" refers to either "column" or "row"|
%% |------------------------------------------|
%% | #1=commmand sequence to save row/table   |
%% | #2=number of interations to pass through |
%% | #3=content for each item                 |
%% | #4=inter-item content                    |
%% | #5=wrap up content                       |
%% |------------------------------------------|
\newcount\ae@build@cnt
\def\ae@build@#1#2#3#4#5{%%
  \let#1\relax
  \ae@build@cnt=0
  \loop
    \advance\ae@build@cnt by 1
    \ifx#1\relax
     \xdef#1{#3}%%
    \else
      \xdef#1{\unexpanded\expandafter{#1}#4#3}%%
    \fi
  \ifnum\ae@build@cnt<#2
  \repeat
  \xdef#1{\unexpanded\expandafter{#1}#5}%%
}

\makeatother

\begin{document}

\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3  \\
\hline
\mttab{15}{3}
\end{tabular}

\end{document}

What's nice about this approach is the same macro handles both the create of the rows and the building of the remainder of the table: no nested loops necessary, except implicitly.

Here's a rather different version here you pass the preamble for the tabular environment, the first row, and the number of rows. You do not need to specify the number of columns.

\documentclass{article}

\makeatletter

%% #1=preamble for tabular: should be simple "l", "r", or "c" separated only by "|" 
%% #2=formatting for the first row of the table                                     
%% #3=number of empty rows to generate                                              
\newcommand\mttab[3]{%%
  \ae@column@cnt=0 %%
  \ae@count@columns#1\relax%%
  \ae@build@\ae@table@row{\the\ae@column@cnt}{}{&}{\noexpand\\\noexpand\hline}%%
  \ae@build@\ae@table{#3}{\unexpanded\expandafter{\ae@table@row}}{}{}%%
  \ae@prepend@begin@table@\ae@table{#1}{#2}%%
  \ae@append@end@table\ae@table
  \ae@table
}

%% |-------------------------------------------|
%% | "item" refers to either "column" or "row" |
%% |-------------------------------------------|
%% | #1=commmand sequence to save row/table    |
%% | #2=number of interations to pass through  |
%% | #3=content for each item                  |
%% | #4=inter-item content                     |
%% | #5=wrap up content                        |
%% |-------------------------------------------|
\newcount\ae@build@cnt
\def\ae@build@#1#2#3#4#5{%%
  \let#1\relax
  \ae@build@cnt=0
  \loop
    \advance\ae@build@cnt by 1 %%
    \ifx#1\relax
     \xdef#1{#3}%%
    \else
      \xdef#1{\unexpanded\expandafter{#1}#4#3}%%
    \fi
  \ifnum\ae@build@cnt<#2
  \repeat
  \xdef#1{\unexpanded\expandafter{#1}#5}%%
}

\def\ae@prepend@begin@table@#1#2#3{%%
  \xdef#1{\noexpand\begin{tabular}{#2}%%
          \unexpanded{#3}%%
          \unexpanded\expandafter{#1}}}

\def\ae@append@end@table#1{%%
  \xdef#1{\unexpanded\expandafter{#1}\noexpand\end{tabular}}%%
  }

%% This macro moves through the preable for the tabular
%% and counts the number of tokens which are not "|".
\newcount\ae@column@cnt
\def\ae@count@columns#1{%%
  \def\ae@continue{}%%
  \ifx#1\relax
  \else
    \let\ae@continue\ae@count@columns
    \ifx#1|
    \else
      \advance\ae@column@cnt by 1 %%
    \fi
  \fi
  \ae@continue}

\makeatother

\begin{document}

\mttab{|c|c|c|}{\hline col1 & col2 & col3 \\\hline}{15}

\end{document}

The key to this succeeding is the macro which counts the number of columns. It assumes that other than the column specifiers l, r, or c, only | occur in the tabular environment's preamble.

2

Every time that one needs to automate a table, it is more flexible and opens more possibilities not to use a tabular, but to build the cells and rows from more elementary functions.

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\parindent0pt
\cs_set:Npn \make_table:nnn #1#2 #3 {
    \cs_set:Npn \cell_func:n ##1 {\framebox[2em][c]{\strut ##1 }~}
    \cs_set:Npn \head_func:n ##1 {\framebox[2em][c]{\strut c##1 }~}
    \int_step_function:nnnN {#1} {#2} {#3} {\head_func:n}\\
    \cs_set:Npn \make_rows:n ##1 { \int_step_function:nnnN {#1} {#2} {#3} { \cell_func:n}\\   }
    \int_step_function:nnnN {#1} {#2} {#3} {\make_rows:n}\\
}    
\make_table:nnn {1}{1}{10}
\ExplSyntaxOff
\end{document}

LaTeX3 provides numerous looping structures buried in the l3int documentation.

enter image description here

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.