1

Here is how I act on a tabular like content. I would like to know if there is a better way than using \bool_while_do like I do ?

The following MWE is the starting point to do what I was asked for in this post (the hardest has been done from my point of view).

\documentclass{article}

% Sources
%   * https://tex.stackexchange.com/a/475291/6880
%   * https://tex.stackexchange.com/a/558343/6880

\usepackage{xparse}

\ExplSyntaxOn

\seq_new:N \l__tnscalc_splittab_seq
\seq_new:N \l__tnscalc_subseq_seq
\int_new:N \l__tnscalc_nbline_int
\int_new:N \l__tnscalc_numcol_int
\tl_new:N \l__tnscalc_xline_temp_tl
\tl_new:N \l__tnscalc_pline_temp_tl

% #1 : line separator
% #2 : cell separator
% #3 : content
\NewDocumentCommand{\splittab}{m m +m}
 {
  \tnscalc_splittab:nnn{#1}{#2}{#3}
 }

% The internal version of the general purpose macro
\cs_new_protected:Nn \tnscalc_splittab:nnn
 {
  % #1 : line separator
  % #2 : cell separator
  % #3 : content
  % A group allows nesting
  \group_begin:
  % Split into parts
  \seq_set_split:Nnn \l__tnscalc_splittab_seq { #1 } { #3 }

  \int_set:Nn \l__tnscalc_nbline_int { \seq_count:N \l__tnscalc_splittab_seq } % why?
  
  % First column
  \seq_pop_left:NN \l__tnscalc_splittab_seq \l__tnscalc_xline_temp_tl
  \seq_set_split:NnV \l__tnscalc_x_seq { #2 } \l__tnscalc_xline_temp_tl

  \seq_pop_left:NN \l__tnscalc_splittab_seq \l__tnscalc_pline_temp_tl
  \seq_set_split:NnV \l__tnscalc_p_seq { #2 } \l__tnscalc_pline_temp_tl
  
  \int_set:Nn \l__tnscalc_numcol_int { \seq_count:N \l__tnscalc_x_seq }
  
  % Pop the column by column.
  \bool_while_do:nn { \int_compare_p:nNn \l__tnscalc_numcol_int > 0 }{
    \seq_pop_left:NN \l__tnscalc_x_seq \l__tnscalc_x_tl
    \seq_pop_left:NN \l__tnscalc_p_seq \l__tnscalc_y_tl
  
    (\int_use:N \l__tnscalc_numcol_int :: \l__tnscalc_x_tl ; \l__tnscalc_y_tl)
  
    \int_add:Nn \l__tnscalc_numcol_int {-1}
  }
  \group_end:
 } 
\ExplSyntaxOff


\begin{document}

\splittab{\\}{&}{ a & b & c \\ 1 & 2 & 3}

\end{document}
2

It's a bit tricky if you don't have a fixed number of items to process, but doable of course. The implementation of \tnscalc_splittab:nnn below will split the sequence twice, at the first then at the second delimiter, then restructure it as:

\__tnscalc_item:nw {a}{b}{c}\q_recursion_tail
\__tnscalc_item:nw {1}{2}{3}\q_recursion_tail
\__tnscalc_item:nw {\q_nil }\q_stop \q_recursion_stop

Then every \__tnscalc_item:nw will collect the item after it, except for the end marker \q_nil, and pass the collected items to \__tnscalc_do:n. After a few expansion steps you will have:

\__tnscalc_do:n {{a}{1}}
\__tnscalc_item:nw {b}{c}\q_recursion_tail
\__tnscalc_item:nw {2}{3}\q_recursion_tail
\__tnscalc_item:nw {\q_nil }\q_stop \q_recursion_stop

with the first batch of items removed. After a few more, you'll have:

\__tnscalc_do:n {{a}{1}} % already executed
\__tnscalc_do:n {{b}{2}}
\__tnscalc_item:nw {c}\q_recursion_tail
\__tnscalc_item:nw {3}\q_recursion_tail
\__tnscalc_item:nw {\q_nil }\q_stop \q_recursion_stop

then the last batch will be used and the \q_recursion_tail markers will signal the end of the list and you'll have:

\__tnscalc_do:n {{a}{1}} % already executed
\__tnscalc_do:n {{b}{2}} % already executed
\__tnscalc_do:n {{c}{3}} % already executed

Since the number of items is variable, each column is passed to \__tnscalc_do:n with all items braced, so in \__tnscalc_do:n you have to figure out what is the number of items (\tl_count:n {#1} might help) and process them accordingly. You can also map to every item in the column using \tl_map_inline:nn {#1} { <code with ##1> }.

The mapping makes sure that \__tnscalc_do:n receives always the same number of items. Any incomplete column is ignored.

To get the output from your code you can define \__tnscalc_do:n as:

\cs_new_protected:Npn \__tnscalc_do:n #1
  {
    (\int_use:N \l__tnscalc_numcol_int :: \use_i:nn #1 ; \use_ii:nn #1 )
    \int_decr:N \l__tnscalc_numcol_int
  }

(note that the usage of \use_i:nn and \use_ii:nn assumes that there are only two \\-separated items; if the number is different you can't use those two anymore!)

enter image description here

Changing the definition of \__tnscalc_do:n to:

\cs_new_protected:Npn \__tnscalc_do:n #1
  {
    Column~\int_use:N \l__tnscalc_numcol_int :~
    \tl_map_inline:nn {#1}
      { (##1) }
    \par
    \int_decr:N \l__tnscalc_numcol_int
  }

produces, for the same input:

enter image description here

You can make the definition of \__tnscalc_do:n an argument to \splittab, but I'll leave that as an exercise.

Here's the code:

\documentclass{article}
\RequirePackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand \splittab { m m +m }
  { \tnscalc_splittab:nnn {#1} {#2} {#3} }
% Variables
\int_new:N \l__tnscalc_numcol_int
\seq_new:N \l__tnscalc_tmp_seq
\tl_new:N \l__tnscalc_items_tl
% Main function
\cs_new_protected:Nn \tnscalc_splittab:nnn
  {
    \group_begin:
      \tl_clear:N \l__tnscalc_items_tl
      \int_set_eq:NN \l__tnscalc_numcol_int \c_max_int
      \seq_set_split:Nnn \l__tnscalc_tmp_seq {#1} {#3}
      \seq_map_inline:Nn \l__tnscalc_tmp_seq
        {
          \seq_set_split:Nnn \l__tnscalc_tmp_seq {#2} {##1}
          \__tnscalc_seq_set_map:NNn \l__tnscalc_tmp_seq
            \l__tnscalc_tmp_seq { {####1} }
          \int_set:Nn \l__tnscalc_numcol_int
            {
              \int_min:nn { \l__tnscalc_numcol_int }
                { \seq_count:N \l__tnscalc_tmp_seq }
            }
          \tl_put_right:Nx \l__tnscalc_items_tl
            {
              \exp_not:N \__tnscalc_item:nw
              \seq_use:Nn \l__tnscalc_tmp_seq { }
              \exp_not:N \q_recursion_tail
            }
        }
      \tl_put_right:Nn \l__tnscalc_items_tl
        { \__tnscalc_item:nw { \q_nil } \q_stop }
      \tl_use:N \l__tnscalc_items_tl \q_recursion_stop
    \group_end:
  }
\cs_new_protected:Npn \__tnscalc_item:nw
  { \__tnscalc_iterate_collect:nnw { } }
\cs_new_protected:Npn \__tnscalc_iterate_collect:nnw #1 #2
  {
    \quark_if_recursion_tail_stop:n {#2}
    \quark_if_nil:nTF {#2}
      { \__tnscalc_iterate_collect_end:nw {#1} }
      { \__tnscalc_iterate_collect_more:nw { #1{#2} } }
  }
\cs_new_protected:Npn \__tnscalc_iterate_collect_more:nw #1 #2
    \__tnscalc_item:nw #3 \q_stop
  { \__tnscalc_iterate_collect:nnw {#1} #3 \__tnscalc_item:nw #2 \q_stop }
\cs_new_protected:Npn \__tnscalc_iterate_collect_end:nw #1
    \__tnscalc_item:nw #2 \q_stop
  {
    \__tnscalc_do:n {#1}
    \__tnscalc_item:nw #2 \__tnscalc_item:nw { \q_nil } \q_stop
  }
% Compatibility for older expl3
\cs_if_exist:NTF \seq_set_map_x:NNn
  { \cs_new_eq:NN \__tnscalc_seq_set_map:NNn \seq_set_map:NNn } % newer expl3
  {
    \cs_new_protected:Npn \__tnscalc_seq_set_map:NNn #1 #2 #3
      { \seq_set_map:NNn #1 #2 { \exp_not:n {#3} } } % older expl3
  }
%
% In this macro, #1 will have as many items as
% there are \\-separated items in your list.
%
% You can iterate over those items with \tl_map_inline:nn
% or you can have some other macro process them.
\cs_new_protected:Npn \__tnscalc_do:n #1
  {
    (\int_use:N \l__tnscalc_numcol_int :: \use_i:nn #1 ; \use_ii:nn #1 )
    \int_decr:N \l__tnscalc_numcol_int
    % Column~\int_use:N \l__tnscalc_numcol_int :~
    % \tl_map_inline:nn {#1}
    %   { (##1) }
    % \par
    % \int_decr:N \l__tnscalc_numcol_int
  }
\ExplSyntaxOff

\begin{document}
\splittab{\\}{&}{ a & b & c \\ 1 & 2 & 3 }
\end{document}
1
  • Thanks for sharing.
    – projetmbc
    Aug 12 '20 at 9:40

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.