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