7

In a nutshell: I've seen code for creating tables programmatically using tabular. Can it be done using tabularx? If so, how?

I'm a math teacher, and I'd like to create a "problem set" environment in which I can list problems that will be formatted into a table.

Additionally, I'd like the environment to have a parameter that specifies the number of columns the table should have. This bit of modularity is why I'd prefer to use tabularx.

The code below, inspired by other answers I've read here, uses tabular and almost works as I'd like. Including the tabularx package causes the code below to fail with an Illegal pream-token error (update: that's a true statement, although the problem is in array, which is used by tabularx.)

I'd like help with any of the following:

(1) Can I use tabularx instead of tabular? What would I need to change to do so?

(2) If not, I'd like the table to be the same width (\textwidth) no matter how many columns. This code doesn't behave in that way. What am I doing wrong in my current tabular environment?

(3) Unrelated to table creation, but also needs fixing: My scheme for numbering these items is to use a counter and \int_use:N, but the result is that the typeset output always has the last value of the counter. Like I said, I'm new to expl3 programming; to me, this code looks like it like it should work.

(4) Of course, I'm also open to suggestions for better ways to implement this.

Thanks, all!

The code:

\documentclass{article}
\usepackage{xparse}
\usepackage{environ}
\usepackage{lipsum}
\usepackage{tabularx}
\ExplSyntaxOn
\NewEnviron{problemset}[1]{\jermer_pset:V \BODY #1}
\seq_new:N \l_jermer_items_seq
\int_new:N \l_jermer_ncol_int
\int_new:N \l_jermer_ctr_int
\tl_new:N \l_jermer_tblpreamble_tl
\tl_new:N \l_jermer_tblcontent_tl
\cs_new_protected:Npn \jermer_pset:n #1 #2
{
\seq_set_split:Nnn \l_jermer_items_seq { \item } { #1 }
\seq_pop_left:NN \l_jermer_items_seq \l_tmpa_tl % remove empty element
\int_set:Nn \l_jermer_ncol_int { #2 }
\int_zero:N \l_jermer_ctr_int
\tl_clear:N \l_jermer_tblpreamble_tl
\tl_clear:N \l_jermer_tblcontent_tl

\seq_map_inline:Nn \l_jermer_items_seq
{   
    \int_incr:N \l_jermer_ctr_int
    \tl_put_right:Nn \l_jermer_tblcontent_tl { \makebox[1.5em][r]{\__int_value:w \l_jermer_ctr_int}.~ ##1} % ??
    \int_compare:nNnTF {\int_mod:nn {\l_jermer_ctr_int} {\l_jermer_ncol_int}} = {0}
    {\tl_put_right:Nn \l_jermer_tblcontent_tl { \\ }}
    {\tl_put_right:Nn \l_jermer_tblcontent_tl { & }}
}
\int_zero:N \l_jermer_ctr_int
\fp_new:N \l_jermer_wfrac_fp
\fp_set:Nn \l_jermer_wfrac_fp {1 / \l_jermer_ncol_int}
\tl_put_right:Nn \l_jermer_tblpreamble_tl { | }
\int_while_do:nn {\l_jermer_ctr_int < \l_jermer_ncol_int}
{
    \tl_put_right:Nn \l_jermer_tblpreamble_tl { p{ \fp_to_decimal:N \l_jermer_wfrac_fp \textwidth} | } % Even col widths?
%       \tl_put_right:Nn \l_jermer_tblpreamble_tl { X } % Would prefer this!

    \int_incr:N \l_jermer_ctr_int
}
\jermer_draw_table:
}
\cs_generate_variant:Nn \jermer_pset:n { V }
\cs_new:Npn \jermer_draw_table:
{
\begin{center}
% Would prefer to use tabularx here.
\begin{tabular}{\l_jermer_tblpreamble_tl}
\tl_use:N \l_jermer_tblcontent_tl
\end{tabular}
\end{center}
}
\ExplSyntaxOff
\begin{document}
\noindent
\lipsum[1]
\begin{problemset}{3}
    \item Alpha
    \item Beta
    \item Gamma
    \item Delta
    \item Epsilon
    \item Zeta
    \item Eta
    \item Theta
    \item Iota
\end{problemset}
\noindent
\lipsum[1]
\end{document}
0

2 Answers 2

8

Here's a solution that also fixes the issue with the item numbers:

\documentclass{article}
\usepackage{xparse}
\usepackage{environ}
\usepackage{lipsum}
\usepackage{tabularx}

\ExplSyntaxOn
\NewEnviron{problemset}[1]{\jermer_pset:Vn \BODY {#1}}

\seq_new:N \l_jermer_items_seq
\int_new:N \l_jermer_ncol_int
\int_new:N \l_jermer_ctr_int
\tl_new:N \l_jermer_tblpreamble_tl
\tl_new:N \l_jermer_tblcontent_tl

\cs_new_protected:Npn \jermer_pset:nn #1 #2
 {
  \seq_set_split:Nnn \l_jermer_items_seq { \item } { #1 }
  \seq_pop_left:NN \l_jermer_items_seq \l_tmpa_tl % remove empty element
  \int_set:Nn \l_jermer_ncol_int { #2 }
  \int_zero:N \l_jermer_ctr_int
  \tl_clear:N \l_jermer_tblpreamble_tl
  \tl_clear:N \l_jermer_tblcontent_tl

  \seq_map_inline:Nn \l_jermer_items_seq
   {   
    \int_incr:N \l_jermer_ctr_int
    \tl_put_right:Nx \l_jermer_tblcontent_tl
     {
      \exp_not:N \makebox[1.5em][r]{\int_to_arabic:n { \l_jermer_ctr_int }}.~\exp_not:n {##1}
     }
    \int_compare:nTF {\int_mod:nn {\l_jermer_ctr_int} {\l_jermer_ncol_int} = 0 }
     {\tl_put_right:Nn \l_jermer_tblcontent_tl { \\ }}
     {\tl_put_right:Nn \l_jermer_tblcontent_tl { & }}
   }
  \tl_put_right:Nn \l_jermer_tblpreamble_tl { | }
  \prg_replicate:nn { #2 } {  \tl_put_right:Nn \l_jermer_tblpreamble_tl { X| } }
  \jermer_draw_table:
 }
\cs_generate_variant:Nn \jermer_pset:nn { V }
\cs_new:Npn \jermer_draw_table:
 {
  \begin{center}
  % Would prefer to use tabularx here.
  \use:x { \exp_not:n {\begin{tabularx}{\textwidth}}{\l_jermer_tblpreamble_tl}}
  \tl_show:N \l_jermer_tblcontent_tl
  \tl_use:N \l_jermer_tblcontent_tl
  \end{tabularx}
  \end{center}
 }
\ExplSyntaxOff

\begin{document}
\noindent
\lipsum[1]
\begin{problemset}{3}
    \item Alpha
    \item Beta
    \item Gamma
    \item Delta
    \item Epsilon
    \item Zeta
    \item Eta
    \item Theta
    \item Iota
\end{problemset}
\noindent
\lipsum[1]
\end{document}

Note that \jermer_pset:nn has two arguments. I removed some parts that were specific to the computation of the column width, since using tabularx is now possible (the trick, as explained by David, is to expand the table preamble).

enter image description here

2
  • 2
    +1 for reading to the end of the question and noticing the additional question about numbering Commented Mar 10, 2014 at 17:21
  • With xparse released 2019-03-05 or later, the package environ is no longer necessary and the definition of the problemset environment becomes \NewDocumentEnvironment{problemset}{m+b}{\jermer_pset:nn {#2} {#1}}{}
    – egreg
    Commented Mar 9, 2019 at 15:20
6

Don't blame me! :-)

The issue isn't loading tabularx it is loading array (used by tabularx) and array prevents premature expansion of the tabular preamble (so that macros in @ or >{...}` are expanded when used in the cell, not while building the preamble.

So you need to expand your macro one level when calling the table, either using latex3 utilities, or the old way:

% Would prefer to use tabularx here.
\def\tmp{\begin{tabular}}%
\expandafter\tmp\expandafter{\l_jermer_tblpreamble_tl}
2
  • Should that be \def\tmp{\begin{tabularx}{\textwidth}}\expandafter\tmp\expandafter{\l_jermer_tblpreamble_tl} since the OP is interested in the tabularx environment?
    – Mico
    Commented Mar 10, 2014 at 16:04
  • @Mico yes in the end, but (just) that change is what is required to fix the error reported in the supplied MWE Commented Mar 10, 2014 at 17:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .