2

This is similar to the question about cells, but with whole rows, and hopefully will help me to implement an answer to this question.

Given a macro \RowMacro, how can I define the mytable environment such that I an write in my document

\begin{mytable}
a & b & c \\
d & e & f \\
g \\
h & i & j
\end{mytable}

and get the same result as if I had written

\RowMacro{a}{b}{c}
\RowMacro{d}{e}{f}
\RowMacro{g}{}{}
\RowMacro{h}{i}{j}

The maximum number of columns may have to be specified upfront.

Moriambar
  • 11,466

2 Answers2

1

Package environ to capture the contents of an environment. LaTeX3 code (package expl3) to split it into lines, cells, and more general programming tools. Rather than making an environment with 4 parameters as I did, you can hard-code some of them in the definition if it is more practical.

\documentclass{article}
\usepackage{environ}
\usepackage{expl3}
\ExplSyntaxOn
\seq_new:N \l_jb_lines_seq
\seq_new:N \l_jb_tmp_seq
\tl_new:N \l_jb_result_tl
\int_new:N \l_jb_cols_int
\int_new:N \l_jb_cell_int
\tl_new:N \l_jb_one_line_tl
\NewEnviron{mytable}[4][]
  {
    \seq_set_split:NnV \l_jb_lines_seq { \\ } \BODY
    %
    % If the user gives no indication of number of cells, find the max
    % number by reading all rows.  Otherwise, use the user-given value,
    % all extras will be thrown out silently.
    %
    \tl_if_empty:nTF {#1}
      { \jb_get_max_cells:NN \l_jb_lines_seq \l_jb_cols_int }
      { \int_set:Nn \l_jb_cols_int {#1} }
    %
    % Build the result into a token list, initially the begin-code |#3|.
    % For each line |##1|, add a call to the command |#2| with exactly
    % |\l_jb_cols_int| arguments.  Then append the end-code |#4|.
    % Finally, use the token list.
    %
    \tl_set:Nn \l_jb_result_tl {#3}
    \seq_map_inline:Nn \l_jb_lines_seq
      { \jb_append_one_line:NnNn \l_jb_result_tl {#2} \l_jb_cols_int {##1} }
    \tl_put_right:Nn \l_jb_result_tl {#4}
    \tl_use:N \l_jb_result_tl
  }
\cs_new_protected:Npn \jb_get_max_cells:NN #1#2
  {
    % Find the max number of cells over all lines.
    % Not terribly efficient way of counting |&|: split at each |&|
    % and store in a tmp sequence, then count its items.
    %
    \int_zero:N #2
    \seq_map_inline:Nn #1
      {
        \seq_set_split:Nnn \l_jb_tmp_seq { & } {##1}
        \int_set:Nn #2
          { \int_max:nn #2 { \seq_count:N \l_jb_tmp_seq } }
      }
  }
\cs_new_protected:Npn \jb_append_one_line:NnNn #1#2#3#4
  {
    \tl_set:Nn \l_jb_one_line_tl {#2}
    \seq_set_split:Nnn \l_jb_tmp_seq { & } {#4}
    \int_set:Nn \l_jb_cell_int { \seq_count:N \l_jb_tmp_seq }
    \int_compare:nNnTF \l_jb_cell_int > {#3}
      {
        \int_zero:N \l_jb_cell_int
        \seq_map_inline:Nn \l_jb_tmp_seq
          {
            \int_compare:nNnF \l_jb_cell_int < {#3} { \seq_map_break: }
            \int_incr:N \l_jb_cell_int
            \tl_put_right:Nn \l_jb_one_line_tl { {##1} }
          }
      }
      {
        \seq_map_inline:Nn \l_jb_tmp_seq
          { \tl_put_right:Nn \l_jb_one_line_tl { {##1} } }
        \tl_put_right:Nx \l_jb_one_line_tl
          { \prg_replicate:nn { #3 - \l_jb_cell_int } { { } } }
      }
    \tl_put_right:NV #1 \l_jb_one_line_tl
  }
\ExplSyntaxOff
\begin{document}
\newcommand{\RowMacro}[5]{[1: #1] & [2: #2] & [3 and 4: #3, #4] & [5: #5]\\}
\begin{mytable}[5]{\RowMacro}{\begin{tabular}{cccc}}{\end{tabular}}
  a & b & c \\
  d & e & f & g & h & extra\notused \\
  ijk & l
\end{mytable}
\end{document}
ShreevatsaR
  • 45,428
  • 10
  • 117
  • 149
  • Whoa, scary code, but might be just what I was looking for. In particular it seems to be able do do other things that I was planning on (such as measuring the cells to decide upon a particular layout). – Joachim Breitner May 22 '13 at 14:01
  • Is expl3 something that is commonly around? Or would I have to worry using it in a submission to a CS journal? – Joachim Breitner May 22 '13 at 14:03
  • @JoachimBreitner My worry is that their versions would be too old. As far as I can tell, this code requires an expl3 more recent than 2012-07-02, relatively more recent than what journals would have. – Bruno Le Floch May 22 '13 at 14:28
0

A simple solution, if you know the number of columns:

\def\RowMacroIII{{#1}&{#2}&{#3}\\}

Usage

\RowMacro{a}{b}{c}

and

\def\RowMacroIV{{#1}&{#2}&{#3}&{#4}\\}

Usage

\RowMacro{a}{b}{c}{d}
  • I think you got my question wrong: I want to write \begin{mytable}... &.. \end{mytable}, and define the behavior by defining \RowMacro. Let me clarify that. – Joachim Breitner May 22 '13 at 12:25
  • @JoachimBreitner So, if you have table with three colums, you define \RowMacro as \RowMacroIII is defined and so on, up to 9 parameters. Am I missing something? – Przemysław Scherwentke May 22 '13 at 13:34
  • Yes, I think so. How do I write the code for mytable that parses the sequnce of cells, & and \\ and passes them to \RowMacro? – Joachim Breitner May 22 '13 at 13:52