13

The documentation for l3seq states

LaTeX3 implements a “sequence” data type, which contain an ordered list of entries which may contain any balanced text .

but for many programming tasks it would be very handy to be able to nest data structures such as sequences. How can I work with sequences of sequences in LaTeX3?

12

The short-term answer is: use token lists nested in a sequence.

\RequirePackage{expl3}
\ExplSyntaxOn
\seq_new:N \l_lines_seq
\cs_new:Npn \jb_wrap:n #1 { \exp_not:n { {#1} } }
\seq_clear:N \l_lines_seq
\seq_set_split:Nnn \l_tmpa_seq { \\ } { a & b \\ c & d }
\seq_map_inline:Nn \l_tmpa_seq
  {
    \seq_set_split:Nnn \l_tmpb_seq { & } {#1} % use ##1 if in def
    \seq_put_right:Nx \l_lines_seq
      { \seq_map_function:NN \l_tmpb_seq \jb_wrap:n }
  }
% Use it, for instance this shows a, b, next, c, d, next on different lines
\seq_map_inline:Nn \l_lines_seq
  {
    \tl_map_inline:nn {#1} { \iow_term:n {##1} }
    \iow_term:n { next }
  }
\stop

The function \seq_set_map:NNn can also be useful for some of your tasks, but I didn't use it above because was added to expl3 somewhat recently. The functions above are all stable, I think.


A "historical" note: the l3seq module was rewritten a couple of years ago to use an internal form that would allow nested sequences in principle (since sequence items can be anything). Namely, a sequence is a token list variable of the form \__seq_item:n {...} \__seq_item:n {...} ... (don't rely on it, it may change without notice). This means that one can safely store the contents of a sequence variable as an item in another sequence variable:

\seq_set_split:Nnn \l_a_seq { , } { a , b , c }
\seq_new:N \l_b_seq
\seq_put_right:NV \l_b_seq \l_a_seq
\seq_show:N \l_b_seq

Retrieving items from the second sequence variable, either through \seq_map_.... functions or through \seq_get_..., will give you an explicit list of tokens \__seq_item:n {...} ..., and there is no function in expl3 to manipulate those. I have to admit that I only realized this unfortunate state of affairs much later.

One way to deal with this would be that we add in expl3 some functions which expect n-type sequence data, both storage into a sequence variable (enough for any non-expandable application), and all expandable functions we currently propose for N-type ones.

% Extra functions that would be enough for all seq purposes,
% I think. Here '...' denotes `\__seq_item:n ...` material.
\seq_set:Nn <seq> { ... }
\seq_map_function:nN { ... } <func>
\seq_item:nn { ... } { <intexpr> }
\seq_mapthread_function:nnN { ... } { ... } <func>
%
% other useful functions (can be implemented in terms of the above)
%
\seq_if_empty:nTF { ... }
\seq_count:n { ... }

For consistency, something similar would have to be done with property lists, adding

% Minimum set of extra functions for props.  Here, `...`
% denotes some `\s__prop <key> \s__prop { <value> }` material.
%
\prop_set:Nn <prop> { ... }
\prop_map_tokens:nn { ... } { <tokens> }
%
% other useful functions (can be implemented in terms of the above)
%
\prop_if_empty:nTF { ... }
\prop_if_in:nnTF { ... } { <key> }
\prop_map_function:nN { ... } <func>
\prop_get:nn { ... } { <key> }

I cannot predict whether we will add those functions for nested sequences/props to expl3, but I think that we need a way to nest those essential data structures. Perhaps Joseph Wright's l3dt is a better approach.

| improve this answer | |
  • Any news here? l3dt does not seem to exists anymore (at least I could not find it in the repository). Are there currently plans to implement the n type functions you mentioned? – schtandard Jul 14 '19 at 8:57
9

The way that sequences are implemented means that at that level they can be nested (the nested sequences will be distinct from the outer one). However, what is not available is an interface to store or recover the whole of a sequence.

\seq_get:NN \l_my_main_seq \l_tmpa_tl % Available
\seq_set:NV \l_my_daughter_seq \l_tmpa_tl % Not available.

Similarly, we've not got a function available to do a 'second level' mapping (map to each sequence stored in a a sequence).

Sequences are built on top of token lists, so you could use functions from the tl module to do this, but that is not really encouraged.

The result is that I'd want to know the detail of the 'task' intended before saying what is best. The team have discussed the 'nested sequence' business, but the feeling to date has been that there is usually a better approach (remember we have an overhead to implementing sequences, etc. in TeX, and for complex structures that can be important).

| improve this answer | |
  • The task at hand (as you might guess from my question yesterday) would be to parse a table once before traversing, analyzing and printing it multiple times (e.g. first count columns, then analyze the cell sizes, then print the table). I’d expect storing a sequence of sequences might be more efficient (or at least more readable) than splitting each lines over and over. – Joachim Breitner May 23 '13 at 7:50
  • 1
    @JoachimBreitner I've also been wondering about tables. For that, I've written some code for 'data tables', but the efficiency problem bites (see experimental module l3dt). I'm also concerned that something like a sequence fails to provide hooks for for example merged cells or visual changes (rules, etc.). – Joseph Wright May 23 '13 at 7:52

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.