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}

Bernard
  • 271,350
projetmbc
  • 13,315

1 Answers1

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}