Print

Print


Hi,

I'm attaching to this email an alternative version of l3seq, where
sequences are stored as \seq_elt:n {<item>}. Since it's a dtx,
hopefully there is enough comments in the file to follow the code, but
a quick overview. FYI, the ins file is at the bottom of this email,
and to test the package, I just make a symbolic link
l3seq.sty->seq-one-braces.sty and then \usepackage{expl3}.

- items can be any balanced text, no brace will be stripped

- I don't like the fact that \seq_(g)pop return its value locally, so
I changed which functions are provided there to \seq_pop_with:Nn <seq>
{<assignment code>}.

- The mappings are fairly optimized as far as I can tell, and should
be preferred to doing things one element at a time.

- I expect the pop_right functions to be much faster for large
sequences than simply storing the elements one by one.

- I made extensive use of breaks, and it could be useful to generalize
that a bit.

- I'd like every "..._map_function:NN" to become
"..._map_function:Nn", i.e. allow mapping several tokens at a time. It
isn't difficult to adapt the code for that, and it would be quite
practical, since among the mappings, only the "map_function" are
expandable. For that reason, "\seq_map_function:NN" is not defined in
the file I attach.


> I'm not so fussed about the edge cases with mismatched conditionals, but the
> {{{{{{a}}}}}} problem is more serious. I'll look into it.

More troubling: \quark_if_recursion_tail_stop:n {{aa}}.

> I haven't seen that catcode trick for comparing emptiness --
> is it any better than \tl_if_empty ?

It should perform identically (+/- 5%?), and behave in the same way.

> Surely the break has to come at the end so that any \seq_map_break:n puts
> its argument after all the cleanup?

In the implementation attached to this email, a breaking looks like

\seq_break:n {<code>}
<rest of the mapping>
\seq_break_point:n {<internal code>}

and executes  <internal code> before <code>. In the absence of break,
<internal code> is also run, because \seq_break_point:n is \use:n.


> This reminds me that after I implemented the break using a gobbling-up-until
> marker, I realised it would have been more in the spirit of the seq code to
> just define
>
>     \cs_set:Npn \seq_elt:w #1 \seq_elt_end: {}

Since I'd like every "map_function" to be expandable, that method
cannot be used in general. Although, it would be nice, and appears to
be just as quick.


>>>> I did a quick comparison, and it seems like the delimited-argument search
>>>> is around about 10-20 times faster than mapping, even for relatively large
>>>> seqs (several thousand (small) elements, which takes a surprisingly long
>>>> time to create using \seq_push:Nn).

Using \seq_push:Nn one element at a time is very slow: the whole
sequence is "unpacked" each time we add an element. The best way to
use sequences is to map through them. To create a large sequence, your
best bet is \prg_replicate:nn.

Regards,
Bruno
% =========== .ins file ========================
\input docstrip.tex
\askforoverwritefalse
\preamble
\endpreamble
\postamble
\endpostamble
\keepsilent
\generate{\file{seq-one-braces.sty}{\from{seq-one-braces.dtx}{package,trace}}}
\endbatchfile