diff options
Diffstat (limited to 'macros/latex-dev/required/l3kernel/l3seq.dtx')
-rw-r--r-- | macros/latex-dev/required/l3kernel/l3seq.dtx | 2681 |
1 files changed, 2681 insertions, 0 deletions
diff --git a/macros/latex-dev/required/l3kernel/l3seq.dtx b/macros/latex-dev/required/l3kernel/l3seq.dtx new file mode 100644 index 0000000000..6be32f7a83 --- /dev/null +++ b/macros/latex-dev/required/l3kernel/l3seq.dtx @@ -0,0 +1,2681 @@ +% \iffalse meta-comment +% +%% File: l3seq.dtx +% +% Copyright (C) 1990-2024 The LaTeX Project +% +% It may be distributed and/or modified under the conditions of the +% LaTeX Project Public License (LPPL), either version 1.3c of this +% license or (at your option) any later version. The latest version +% of this license is in the file +% +% https://www.latex-project.org/lppl.txt +% +% This file is part of the "l3kernel bundle" (The Work in LPPL) +% and all files in that bundle must be distributed together. +% +% ----------------------------------------------------------------------- +% +% The development version of the bundle can be found at +% +% https://github.com/latex3/latex3 +% +% for those people who are interested. +% +%<*driver> +\documentclass[full,kernel]{l3doc} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% \title{^^A +% The \pkg{l3seq} module\\ Sequences and stacks^^A +% } +% +% \author{^^A +% The \LaTeX{} Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \date{Released 2024-04-11} +% +% \maketitle +% +% \begin{documentation} +% +% \LaTeX3 implements a \enquote{sequence} data type, which contain +% an ordered list of entries which may contain any \meta{balanced text}. +% It is possible to map functions to sequences such that the function +% is applied to every item in the sequence. +% +% Sequences are also used to implement stack functions in \LaTeX3. This +% is achieved using a number of dedicated stack functions. +% +% \section{Creating and initialising sequences} +% +% \begin{function}{\seq_new:N, \seq_new:c} +% \begin{syntax} +% \cs{seq_new:N} \meta{seq~var} +% \end{syntax} +% Creates a new \meta{seq~var} or raises an error if the name is +% already taken. The declaration is global. The \meta{seq~var} +% initially contains no items. +% \end{function} +% +% \begin{function}{\seq_clear:N, \seq_clear:c, \seq_gclear:N, \seq_gclear:c} +% \begin{syntax} +% \cs{seq_clear:N} \meta{seq~var} +% \end{syntax} +% Clears all items from the \meta{seq~var}. +% \end{function} +% +% \begin{function} +% {\seq_clear_new:N, \seq_clear_new:c, \seq_gclear_new:N, \seq_gclear_new:c} +% \begin{syntax} +% \cs{seq_clear_new:N} \meta{seq~var} +% \end{syntax} +% Ensures that the \meta{seq~var} exists globally by applying +% \cs{seq_new:N} if necessary, then applies +% \cs[index=seq_clear:N]{seq_(g)clear:N} to leave +% the \meta{seq~var} empty. +% \end{function} +% +% \begin{function} +% { +% \seq_set_eq:NN, \seq_set_eq:cN, \seq_set_eq:Nc, \seq_set_eq:cc, +% \seq_gset_eq:NN, \seq_gset_eq:cN, \seq_gset_eq:Nc, \seq_gset_eq:cc +% } +% \begin{syntax} +% \cs{seq_set_eq:NN} \meta{seq~var_1} \meta{seq~var_2} +% \end{syntax} +% Sets the content of \meta{seq~var_1} equal to that of +% \meta{seq~var_2}. +% \end{function} +% +% \begin{function}[added = 2014-07-17] +% { +% \seq_set_from_clist:NN, \seq_set_from_clist:cN, +% \seq_set_from_clist:Nc, \seq_set_from_clist:cc, +% \seq_set_from_clist:Nn, \seq_set_from_clist:cn, +% \seq_gset_from_clist:NN, \seq_gset_from_clist:cN, +% \seq_gset_from_clist:Nc, \seq_gset_from_clist:cc, +% \seq_gset_from_clist:Nn, \seq_gset_from_clist:cn +% } +% \begin{syntax} +% \cs{seq_set_from_clist:NN} \meta{seq~var} \meta{comma-list} +% \end{syntax} +% Converts the data in the \meta{comma list} into a \meta{seq~var}: +% the original \meta{comma list} is unchanged. +% \end{function} +% +% \begin{function}[added = 2017-11-28] +% {\seq_const_from_clist:Nn, \seq_const_from_clist:cn} +% \begin{syntax} +% \cs{seq_const_from_clist:Nn} \meta{seq~var} \Arg{comma-list} +% \end{syntax} +% Creates a new constant \meta{seq~var} or raises an error if the name +% is already taken. The \meta{seq~var} is set globally to contain the +% items in the \meta{comma list}. +% \end{function} +% +% \begin{function}[added = 2011-08-15, updated = 2012-07-02] +% { +% \seq_set_split:Nnn , +% \seq_set_split:NVn , \seq_set_split:NnV , \seq_set_split:NVV , +% \seq_set_split:Nne , \seq_set_split:Nee , +% \seq_gset_split:Nnn, +% \seq_gset_split:NVn , \seq_gset_split:NnV, \seq_gset_split:NVV, +% \seq_gset_split:Nne , \seq_gset_split:Nee +% } +% \begin{syntax} +% \cs{seq_set_split:Nnn} \meta{seq~var} \Arg{delimiter} \Arg{token list} +% \end{syntax} +% Splits the \meta{token list} into \meta{items} separated +% by \meta{delimiter}, and assigns the result to the \meta{seq~var}. +% Spaces on both sides of each \meta{item} are ignored, +% then one set of outer braces is removed (if any); +% this space trimming behaviour is identical to that of +% \pkg{l3clist} functions. Empty \meta{items} are preserved by +% \cs{seq_set_split:Nnn}, and can be removed afterwards using +% \cs{seq_remove_all:Nn} \meta{seq~var} |{}|. +% The \meta{delimiter} may not contain |{|, |}| or |#| +% (assuming \TeX{}'s normal category code r\'egime). +% If the \meta{delimiter} is empty, the \meta{token list} is split +% into \meta{items} as a \meta{token list}. +% See also \cs{seq_set_split_keep_spaces:Nnn}, which omits space stripping. +% \end{function} +% +% \begin{function}[added = 2021-03-24] +% { +% \seq_set_split_keep_spaces:Nnn , \seq_set_split_keep_spaces:NnV , +% \seq_gset_split_keep_spaces:Nnn, \seq_gset_split_keep_spaces:NnV +% } +% \begin{syntax} +% \cs{seq_set_split_keep_spaces:Nnn} \meta{seq~var} \Arg{delimiter} \Arg{token list} +% \end{syntax} +% Splits the \meta{token list} into \meta{items} separated +% by \meta{delimiter}, and assigns the result to the \meta{seq~var}. +% One set of outer braces is removed (if any) but any surrounding spaces +% are retained: any braces \emph{inside} one or more spaces are +% therefore kept. Empty \meta{items} are preserved by +% \cs{seq_set_split_keep_spaces:Nnn}, and can be removed afterwards using +% \cs{seq_remove_all:Nn} \meta{seq~var} |{}|. +% The \meta{delimiter} may not contain |{|, |}| or |#| +% (assuming \TeX{}'s normal category code r\'egime). +% If the \meta{delimiter} is empty, the \meta{token list} is split +% into \meta{items} as a \meta{token list}. +% See also \cs{seq_set_split:Nnn}, which removes spaces around the delimiters. +% \end{function} +% +% \begin{function}[added = 2012-06-15] +% {\seq_set_filter:NNn, \seq_gset_filter:NNn} +% \begin{syntax} +% \cs{seq_set_filter:NNn} \meta{seq~var_1} \meta{seq~var_2} \Arg{inline boolexpr} +% \end{syntax} +% Evaluates the \meta{inline boolexpr} for every \meta{item} stored +% within the \meta{seq~var_2}. The \meta{inline boolexpr} +% receives the \meta{item} as |#1|. The sequence of all \meta{items} +% for which the \meta{inline boolexpr} evaluated to \texttt{true} +% is assigned to \meta{seq~var_1}. +% \begin{texnote} +% Contrarily to other mapping functions, \cs{seq_map_break:} cannot +% be used in this function, and would lead to low-level \TeX{} errors. +% \end{texnote} +% \end{function} +% +% \begin{function} +% {\seq_concat:NNN, \seq_concat:ccc, \seq_gconcat:NNN, \seq_gconcat:ccc} +% \begin{syntax} +% \cs{seq_concat:NNN} \meta{seq~var_1} \meta{seq~var_2} \meta{seq~var_3} +% \end{syntax} +% Concatenates the content of \meta{seq~var_2} and \meta{seq~var_3} +% together and saves the result in \meta{seq~var_1}. The items in +% \meta{seq~var_2} are placed at the left side of the new sequence. +% \end{function} +% +% \begin{function}[EXP, pTF, added=2012-03-03] +% {\seq_if_exist:N, \seq_if_exist:c} +% \begin{syntax} +% \cs{seq_if_exist_p:N} \meta{seq~var} +% \cs{seq_if_exist:NTF} \meta{seq~var} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests whether the \meta{seq~var} is currently defined. This does not +% check that the \meta{seq~var} really is a sequence variable. +% \end{function} +% +% \section{Appending data to sequences} +% +% \begin{function}{ +% \seq_put_left:Nn, \seq_put_left:NV, \seq_put_left:Nv, \seq_put_left:Ne, +% \seq_put_left:No, +% \seq_put_left:cn, \seq_put_left:cV, \seq_put_left:cv, \seq_put_left:ce, +% \seq_put_left:co, +% \seq_gput_left:Nn, \seq_gput_left:NV, \seq_gput_left:Nv, \seq_gput_left:Ne, +% \seq_gput_left:No, +% \seq_gput_left:cn, \seq_gput_left:cV, \seq_gput_left:cv, \seq_gput_left:ce, +% \seq_gput_left:co +% } +% \begin{syntax} +% \cs{seq_put_left:Nn} \meta{seq~var} \Arg{item} +% \end{syntax} +% Appends the \meta{item} to the left of the \meta{seq~var}. +% \end{function} +% +% \begin{function}{ +% \seq_put_right:Nn, \seq_put_right:NV, \seq_put_right:Nv, \seq_put_right:Ne, +% \seq_put_right:No, +% \seq_put_right:cn, \seq_put_right:cV, \seq_put_right:cv, \seq_put_right:ce, +% \seq_put_right:co, +% \seq_gput_right:Nn, \seq_gput_right:NV, \seq_gput_right:Nv, \seq_gput_right:Ne, +% \seq_gput_right:No, +% \seq_gput_right:cn, \seq_gput_right:cV, \seq_gput_right:cv, \seq_gput_right:ce, +% \seq_gput_right:co, +% } +% \begin{syntax} +% \cs{seq_put_right:Nn} \meta{seq~var} \Arg{item} +% \end{syntax} +% Appends the \meta{item} to the right of the \meta{seq~var}. +% \end{function} +% +% \section{Recovering items from sequences} +% +% Items can be recovered from either the left or the right of sequences. +% For implementation reasons, the actions at the left of the sequence are +% faster than those acting on the right. These functions all assign the +% recovered material locally, \emph{i.e.}~setting the +% \meta{token list variable} used with \cs{tl_set:Nn} and \emph{never} +% \cs{tl_gset:Nn}. +% +% \begin{function}[updated = 2012-05-14]{\seq_get_left:NN, \seq_get_left:cN} +% \begin{syntax} +% \cs{seq_get_left:NN} \meta{seq~var} \meta{token list variable} +% \end{syntax} +% Stores the left-most item from a \meta{seq~var} in the +% \meta{token list variable} without removing it from the +% \meta{seq~var}. The \meta{token list variable} is assigned locally. +% If \meta{seq~var} is empty the \meta{token list variable} +% is set to the special marker \cs{q_no_value}. +% \end{function} +% +% \begin{function}[updated = 2012-05-19]{\seq_get_right:NN, \seq_get_right:cN} +% \begin{syntax} +% \cs{seq_get_right:NN} \meta{seq~var} \meta{token list variable} +% \end{syntax} +% Stores the right-most item from a \meta{seq~var} in the +% \meta{token list variable} without removing it from the +% \meta{seq~var}. The \meta{token list variable} is assigned locally. +% If \meta{seq~var} is empty the \meta{token list variable} +% is set to the special marker \cs{q_no_value}. +% \end{function} +% +% \begin{function}[updated = 2012-05-14]{\seq_pop_left:NN, \seq_pop_left:cN} +% \begin{syntax} +% \cs{seq_pop_left:NN} \meta{seq~var} \meta{token list variable} +% \end{syntax} +% Pops the left-most item from a \meta{seq~var} into the +% \meta{token list variable}, \emph{i.e.}~removes the item from the +% sequence and stores it in the \meta{token list variable}. +% Both of the variables are assigned locally. If \meta{seq~var} is +% empty the \meta{token list variable} is set to +% the special marker \cs{q_no_value}. +% \end{function} +% +% \begin{function}[updated = 2012-05-14]{\seq_gpop_left:NN, \seq_gpop_left:cN} +% \begin{syntax} +% \cs{seq_gpop_left:NN} \meta{seq~var} \meta{token list variable} +% \end{syntax} +% Pops the left-most item from a \meta{seq~var} into the +% \meta{token list variable}, \emph{i.e.}~removes the item from the +% sequence and stores it in the \meta{token list variable}. +% The \meta{seq~var} is modified globally, while the assignment of +% the \meta{token list variable} is local. +% If \meta{seq~var} is empty the \meta{token list variable} is set to +% the special marker \cs{q_no_value}. +% \end{function} +% +% \begin{function}[updated = 2012-05-19]{\seq_pop_right:NN, \seq_pop_right:cN} +% \begin{syntax} +% \cs{seq_pop_right:NN} \meta{seq~var} \meta{token list variable} +% \end{syntax} +% Pops the right-most item from a \meta{seq~var} into the +% \meta{token list variable}, \emph{i.e.}~removes the item from the +% sequence and stores it in the \meta{token list variable}. +% Both of the variables are assigned locally. If \meta{seq~var} is +% empty the \meta{token list variable} is set to +% the special marker \cs{q_no_value}. +% \end{function} +% +% \begin{function}[updated = 2012-05-19]{\seq_gpop_right:NN, \seq_gpop_right:cN} +% \begin{syntax} +% \cs{seq_gpop_right:NN} \meta{seq~var} \meta{token list variable} +% \end{syntax} +% Pops the right-most item from a \meta{seq~var} into the +% \meta{token list variable}, \emph{i.e.}~removes the item from the +% sequence and stores it in the \meta{token list variable}. +% The \meta{seq~var} is modified globally, while the assignment of +% the \meta{token list variable} is local. +% If \meta{seq~var} is empty the \meta{token list variable} is set to +% the special marker \cs{q_no_value}. +% \end{function} +% +% \begin{function}[added = 2014-07-17, EXP] +% { +% \seq_item:Nn, \seq_item:NV, \seq_item:Ne, +% \seq_item:cn, \seq_item:cV, \seq_item:ce +% } +% \begin{syntax} +% \cs{seq_item:Nn} \meta{seq~var} \Arg{integer expression} +% \end{syntax} +% Indexing items in the \meta{seq~var} from~$1$ at the top (left), this +% function evaluates the \meta{integer expression} and leaves the +% appropriate item from the sequence in the input stream. If the +% \meta{integer expression} is negative, indexing occurs from the +% bottom (right) of the sequence. If the \meta{integer expression} +% is larger than the number of items in the \meta{seq~var} (as +% calculated by \cs{seq_count:N}) then the function expands to +% nothing. +% \begin{texnote} +% The result is returned within the \tn{unexpanded} +% primitive (\cs{exp_not:n}), which means that the \meta{item} +% does not expand further when appearing in an \texttt{e}-type +% or \texttt{x}-type argument expansion. +% \end{texnote} +% \end{function} +% +% \begin{function}[EXP, added = 2016-12-06]{\seq_rand_item:N, \seq_rand_item:c} +% \begin{syntax} +% \cs{seq_rand_item:N} \meta{seq~var} +% \end{syntax} +% Selects a pseudo-random item of the \meta{seq~var}. If the +% \meta{seq~var} is empty the result is empty. +% This is not available in older versions of \XeTeX{}. +% \begin{texnote} +% The result is returned within the \tn{unexpanded} +% primitive (\cs{exp_not:n}), which means that the \meta{item} +% does not expand further when appearing in an \texttt{e}-type +% or \texttt{x}-type argument expansion. +% \end{texnote} +% \end{function} +% +% \section{Recovering values from sequences with branching} +% +% The functions in this section combine tests for non-empty sequences +% with recovery of an item from the sequence. They offer increased readability +% and performance over separate testing and recovery phases. +% +% \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19] +% {\seq_get_left:NN, \seq_get_left:cN} +% \begin{syntax} +% \cs{seq_get_left:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code} +% \end{syntax} +% If the \meta{seq~var} is empty, leaves the \meta{false code} in the +% input stream. The value of the \meta{token list variable} is +% not defined in this case and should not be relied upon. If the +% \meta{seq~var} is non-empty, stores the left-most item from the +% \meta{seq~var} +% in the \meta{token list variable} without removing it from the +% \meta{seq~var}, then leaves the \meta{true code} in the input stream. +% The \meta{token list variable} is assigned locally. +% \end{function} +% +% \begin{function}[TF, added = 2012-05-19] +% {\seq_get_right:NN, \seq_get_right:cN} +% \begin{syntax} +% \cs{seq_get_right:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code} +% \end{syntax} +% If the \meta{seq~var} is empty, leaves the \meta{false code} in the +% input stream. The value of the \meta{token list variable} is +% not defined in this case and should not be relied upon. If the +% \meta{seq~var} is non-empty, stores the right-most item from the +% \meta{seq~var} +% in the \meta{token list variable} without removing it from the +% \meta{seq~var}, then leaves the \meta{true code} in the input stream. +% The \meta{token list variable} is assigned locally. +% \end{function} +% +% \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19] +% {\seq_pop_left:NN, \seq_pop_left:cN} +% \begin{syntax} +% \cs{seq_pop_left:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code} +% \end{syntax} +% If the \meta{seq~var} is empty, leaves the \meta{false code} in the +% input stream. The value of the \meta{token list variable} is +% not defined in this case and should not be relied upon. If the +% \meta{seq~var} is non-empty, pops the left-most item from the +% \meta{seq~var} +% in the \meta{token list variable}, \emph{i.e.}~removes the item from the +% \meta{seq~var}, then leaves the \meta{true code} in the input stream. +% Both the \meta{seq~var} and the \meta{token list variable} are assigned +% locally. +% \end{function} +% +% \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19] +% {\seq_gpop_left:NN, \seq_gpop_left:cN} +% \begin{syntax} +% \cs{seq_gpop_left:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code} +% \end{syntax} +% If the \meta{seq~var} is empty, leaves the \meta{false code} in the +% input stream. The value of the \meta{token list variable} is +% not defined in this case and should not be relied upon. If the +% \meta{seq~var} is non-empty, pops the left-most item from the \meta{seq~var} +% in the \meta{token list variable}, \emph{i.e.}~removes the item from the +% \meta{seq~var}, then leaves the \meta{true code} in the input stream. +% The \meta{seq~var} is modified globally, while the \meta{token list variable} +% is assigned locally. +% \end{function} +% +% \begin{function}[TF, added = 2012-05-19] +% {\seq_pop_right:NN, \seq_pop_right:cN} +% \begin{syntax} +% \cs{seq_pop_right:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code} +% \end{syntax} +% If the \meta{seq~var} is empty, leaves the \meta{false code} in the +% input stream. The value of the \meta{token list variable} is +% not defined in this case and should not be relied upon. If the +% \meta{seq~var} is non-empty, pops the right-most item from the \meta{seq~var} +% in the \meta{token list variable}, \emph{i.e.}~removes the item from the +% \meta{seq~var}, then leaves the \meta{true code} in the input stream. +% Both the \meta{seq~var} and the \meta{token list variable} are assigned +% locally. +% \end{function} +% +% \begin{function}[TF, added = 2012-05-19] +% {\seq_gpop_right:NN, \seq_gpop_right:cN} +% \begin{syntax} +% \cs{seq_gpop_right:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code} +% \end{syntax} +% If the \meta{seq~var} is empty, leaves the \meta{false code} in the +% input stream. The value of the \meta{token list variable} is +% not defined in this case and should not be relied upon. If the +% \meta{seq~var} is non-empty, pops the right-most item from the \meta{seq~var} +% in the \meta{token list variable}, \emph{i.e.}~removes the item from the +% \meta{seq~var}, then leaves the \meta{true code} in the input stream. +% The \meta{seq~var} is modified globally, while the +% \meta{token list variable} is assigned locally. +% \end{function} +% +% \section{Modifying sequences} +% +% While sequences are normally used as ordered lists, it may be +% necessary to modify the content. The functions here may be used +% to update sequences, while retaining the order of the unaffected +% entries. +% +% \begin{function} +% { +% \seq_remove_duplicates:N, \seq_remove_duplicates:c, +% \seq_gremove_duplicates:N, \seq_gremove_duplicates:c +% } +% \begin{syntax} +% \cs{seq_remove_duplicates:N} \meta{seq~var} +% \end{syntax} +% Removes duplicate items from the \meta{seq~var}, leaving the +% left most copy of each item in the \meta{seq~var}. The \meta{item} +% comparison takes place on a token basis, as for \cs{tl_if_eq:nnTF}. +% \begin{texnote} +% This function iterates through every item in the \meta{seq~var} and +% does a comparison with the \meta{items} already checked. It is therefore +% relatively slow with large sequences. +% \end{texnote} +% \end{function} +% +% \begin{function} +% { +% \seq_remove_all:Nn, \seq_remove_all:NV, \seq_remove_all:Ne, +% \seq_remove_all:cn, \seq_remove_all:cV, \seq_remove_all:ce, +% \seq_gremove_all:Nn, \seq_gremove_all:NV, \seq_gremove_all:Ne, +% \seq_gremove_all:cn, \seq_gremove_all:cV, \seq_gremove_all:ce +% } +% \begin{syntax} +% \cs{seq_remove_all:Nn} \meta{seq~var} \Arg{item} +% \end{syntax} +% Removes every occurrence of \meta{item} from the \meta{seq~var}. +% The \meta{item} comparison takes place on a token basis, as for +% \cs{tl_if_eq:nnTF}. +% \end{function} +% +% \begin{function}[added = 2021-04-29, noTF] +% {\seq_set_item:Nnn, \seq_set_item:cnn, \seq_gset_item:Nnn, \seq_gset_item:cnn} +% \begin{syntax} +% \cs{seq_set_item:Nnn} \meta{seq~var} \Arg{int expr} \Arg{item} +% \cs{seq_set_item:NnnTF} \meta{seq~var} \Arg{int expr} \Arg{item} \Arg{true code} \Arg{false code} +% \end{syntax} +% Removes the item of \meta{seq~var} at the position given by +% evaluating the \meta{int expr} and replaces it by +% \meta{item}. Items are indexed from $1$ on the left/top of the +% \meta{seq~var}, or from $-1$ on the right/bottom. If the +% \meta{int expr} is zero or is larger (in absolute value) +% than the number of items in the sequence, the \meta{seq~var} is not +% modified. In these cases, \cs{seq_set_item:Nnn} raises an error +% while \cs{seq_set_item:NnnTF} runs the \meta{false code}. In cases +% where the assignment was successful, \meta{true code} is run +% afterwards. +% \end{function} +% +% \begin{function}[added = 2014-07-18] +% { +% \seq_reverse:N, \seq_reverse:c, +% \seq_greverse:N, \seq_greverse:c +% } +% \begin{syntax} +% \cs{seq_reverse:N} \meta{seq~var} +% \end{syntax} +% Reverses the order of the items stored in the \meta{seq~var}. +% \end{function} +% +% \begin{function}[added = 2017-02-06] +% {\seq_sort:Nn, \seq_sort:cn, \seq_gsort:Nn, \seq_gsort:cn} +% \begin{syntax} +% \cs{seq_sort:Nn} \meta{seq~var} \Arg{comparison code} +% \end{syntax} +% Sorts the items in the \meta{seq~var} according to the +% \meta{comparison code}, and assigns the result to +% \meta{seq~var}. The details of sorting comparison are +% described in Section~\ref{sec:l3sort:mech}. +% \end{function} +% +% \begin{function}[added = 2018-04-29] +% {\seq_shuffle:N, \seq_shuffle:c, \seq_gshuffle:N, \seq_gshuffle:c} +% \begin{syntax} +% \cs{seq_shuffle:N} \meta{seq~var} +% \end{syntax} +% Sets the \meta{seq~var} to the result of placing the items of the +% \meta{seq~var} in a random order. Each item is (roughly) as likely +% to end up in any given position. +% \begin{texnote} +% For sequences with more than $13$ items or so, only a small +% proportion of all possible permutations can be reached, because +% the random seed \cs{sys_rand_seed:} only has $28$-bits. The use +% of \tn{toks} internally means that sequences with more than +% $32767$ or $65535$ items (depending on the engine) cannot be +% shuffled. +% \end{texnote} +% \end{function} +% +% \section{Sequence conditionals} +% +% \begin{function}[EXP,pTF]{\seq_if_empty:N, \seq_if_empty:c} +% \begin{syntax} +% \cs{seq_if_empty_p:N} \meta{seq~var} +% \cs{seq_if_empty:NTF} \meta{seq~var} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if the \meta{seq~var} is empty (containing no items). +% \end{function} +% +% \begin{function}[TF] +% { +% \seq_if_in:Nn, \seq_if_in:NV, \seq_if_in:Nv, \seq_if_in:Ne, +% \seq_if_in:No, +% \seq_if_in:cn, \seq_if_in:cV, \seq_if_in:cv, \seq_if_in:ce, +% \seq_if_in:co, +% } +% \begin{syntax} +% \cs{seq_if_in:NnTF} \meta{seq~var} \Arg{item} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if the \meta{item} is present in the \meta{seq~var}. +% \end{function} +% +% \section{Mapping over sequences} +% +% All mappings are done at the current group level, \emph{i.e.}~any +% local assignments made by the \meta{function} or \meta{code} discussed +% below remain in effect after the loop. +% +% \begin{function}[rEXP, updated = 2012-06-29] +% {\seq_map_function:NN, \seq_map_function:cN} +% \begin{syntax} +% \cs{seq_map_function:NN} \meta{seq~var} \meta{function} +% \end{syntax} +% Applies \meta{function} to every \meta{item} stored in the +% \meta{seq~var}. The \meta{function} will receive one argument for +% each iteration. The \meta{items} are returned from left to right. +% To pass further arguments to the \meta{function}, see +% \cs{seq_map_tokens:Nn}. +% The function \cs{seq_map_inline:Nn} is faster than +% \cs{seq_map_function:NN} for sequences with more than about~$10$ +% items. +% \end{function} +% +% \begin{function}[updated = 2012-06-29] +% {\seq_map_inline:Nn, \seq_map_inline:cn} +% \begin{syntax} +% \cs{seq_map_inline:Nn} \meta{seq~var} \Arg{inline function} +% \end{syntax} +% Applies \meta{inline function} to every \meta{item} stored +% within the \meta{seq~var}. The \meta{inline function} should +% consist of code which will receive the \meta{item} as |#1|. +% The \meta{items} are returned from left to right. +% \end{function} +% +% \begin{function}[rEXP, added = 2019-08-30] +% {\seq_map_tokens:Nn, \seq_map_tokens:cn} +% \begin{syntax} +% \cs{seq_map_tokens:Nn} \meta{seq~var} \Arg{code} +% \end{syntax} +% Analogue of \cs{seq_map_function:NN} which maps several tokens +% instead of a single function. The \meta{code} receives each item in +% the \meta{seq~var} as a trailing brace group. For instance, +% \begin{verbatim} +% \seq_map_tokens:Nn \l_my_seq { \prg_replicate:nn { 2 } } +% \end{verbatim} +% expands to twice each item in the \meta{seq~var}: for each item in +% |\l_my_seq| the function \cs{prg_replicate:nn} receives |2| and +% \meta{item} as its two arguments. The function +% \cs{seq_map_inline:Nn} is typically faster but it is not expandable. +% \end{function} +% +% \begin{function}[updated = 2012-06-29] +% { +% \seq_map_variable:NNn, \seq_map_variable:Ncn, +% \seq_map_variable:cNn, \seq_map_variable:ccn +% } +% \begin{syntax} +% \cs{seq_map_variable:NNn} \meta{seq~var} \meta{variable} \Arg{code} +% \end{syntax} +% Stores each \meta{item} of the \meta{seq~var} in turn in the (token +% list) \meta{variable} and applies the \meta{code}. The \meta{code} +% will usually make use of the \meta{variable}, but this is not +% enforced. The assignments to the \meta{variable} are local. Its +% value after the loop is the last \meta{item} in the \meta{seq~var}, +% or its original value if the \meta{seq~var} is empty. The +% \meta{items} are returned from left to right. +% \end{function} +% +% \begin{function}[rEXP,added = 2018-05-03]{\seq_map_indexed_function:NN} +% \begin{syntax} +% \cs{seq_map_indexed_function:NN} \meta{seq~var} \meta{function} +% \end{syntax} +% Applies \meta{function} to every entry in the \meta{seq~var}. +% The \meta{function} should have signature |:nn|. It +% receives two arguments for each iteration: the \meta{index} (namely +% |1| for the first entry, then |2| and so on) and the \meta{item}. +% \end{function} +% +% \begin{function}[added = 2018-05-03]{\seq_map_indexed_inline:Nn} +% \begin{syntax} +% \cs{seq_map_indexed_inline:Nn} \meta{seq~var} \Arg{inline function} +% \end{syntax} +% Applies \meta{inline function} to every entry in the \meta{seq~var}. +% The \meta{inline function} should consist of code which +% receives the \meta{index} (namely |1| for the first entry, then |2| +% and so on) as~|#1| and the \meta{item} as~|#2|. +% \end{function} +% +% \begin{function}[rEXP, added = 2023-05-10] +% { +% \seq_map_pairwise_function:NNN, \seq_map_pairwise_function:NcN, +% \seq_map_pairwise_function:cNN, \seq_map_pairwise_function:ccN +% } +% \begin{syntax} +% \cs{seq_map_pairwise_function:NNN} \meta{seq_1} \meta{seq_2} \meta{function} +% \end{syntax} +% Applies \meta{function} to every pair of items +% \meta{seq_1-item}--\meta{seq_2-item} from the two sequences, returning +% items from both sequences from left to right. The \meta{function} +% receives two \texttt{n}-type arguments for each iteration. The mapping +% terminates when +% the end of either sequence is reached (\emph{i.e.}~whichever sequence has +% fewer items determines how many iterations +% occur). +% \end{function} +% +% \begin{function}[rEXP, updated = 2012-06-29]{\seq_map_break:} +% \begin{syntax} +% \cs{seq_map_break:} +% \end{syntax} +% Used to terminate a \cs[no-index]{seq_map_\ldots} function before all +% entries in the \meta{seq~var} have been processed. This +% normally takes place within a conditional statement, for example +% \begin{verbatim} +% \seq_map_inline:Nn \l_my_seq +% { +% \str_if_eq:nnTF { #1 } { bingo } +% { \seq_map_break: } +% { +% % Do something useful +% } +% } +% \end{verbatim} +% Use outside of a \cs[no-index]{seq_map_\ldots} scenario leads to low +% level \TeX{} errors. +% \begin{texnote} +% When the mapping is broken, additional tokens may be inserted +% before further items are taken +% from the input stream. This depends on the design of the mapping +% function. +% \end{texnote} +% \end{function} +% +% \begin{function}[rEXP, updated = 2012-06-29]{\seq_map_break:n} +% \begin{syntax} +% \cs{seq_map_break:n} \Arg{code} +% \end{syntax} +% Used to terminate a \cs[no-index]{seq_map_\ldots} function before all +% entries in the \meta{seq~var} have been processed, inserting +% the \meta{code} after the mapping has ended. This +% normally takes place within a conditional statement, for example +% \begin{verbatim} +% \seq_map_inline:Nn \l_my_seq +% { +% \str_if_eq:nnTF { #1 } { bingo } +% { \seq_map_break:n { <code> } } +% { +% % Do something useful +% } +% } +% \end{verbatim} +% Use outside of a \cs[no-index]{seq_map_\ldots} scenario leads to low +% level \TeX{} errors. +% \begin{texnote} +% When the mapping is broken, additional tokens may be inserted +% before the \meta{code} is +% inserted into the input stream. +% This depends on the design of the mapping function. +% \end{texnote} +% \end{function} +% +% \begin{function}[added = 2011-12-22, updated = 2020-07-16] +% {\seq_set_map:NNn, \seq_gset_map:NNn} +% \begin{syntax} +% \cs{seq_set_map:NNn} \meta{seq~var_1} \meta{seq~var_2} \Arg{inline function} +% \end{syntax} +% Applies \meta{inline function} to every \meta{item} stored +% within the \meta{seq~var_2}. The \meta{inline function} should +% consist of code which will receive the \meta{item} as |#1|. +% The sequence resulting applying \meta{inline function} to each +% \meta{item} is assigned to \meta{seq~var_1}. +% \begin{texnote} +% Contrarily to other mapping functions, \cs{seq_map_break:} cannot +% be used in this function, and would lead to low-level \TeX{} errors. +% \end{texnote} +% \end{function} +% +% \begin{function}[added = 2020-07-16, updated = 2023-10-26] +% {\seq_set_map_e:NNn, \seq_gset_map_e:NNn} +% \begin{syntax} +% \cs{seq_set_map_e:NNn} \meta{seq~var_1} \meta{seq~var_2} \Arg{inline function} +% \end{syntax} +% Applies \meta{inline function} to every \meta{item} stored +% within the \meta{seq~var_2}. The \meta{inline function} should +% consist of code which will receive the \meta{item} as |#1|. +% The sequence resulting from \texttt{e}-expanding +% \meta{inline function} applied to each \meta{item} +% is assigned to \meta{seq~var_1}. As such, the code +% in \meta{inline function} should be expandable. +% \begin{texnote} +% Contrarily to other mapping functions, \cs{seq_map_break:} cannot +% be used in this function, and would lead to low-level \TeX{} errors. +% \end{texnote} +% \end{function} +% +% \begin{function}[EXP, added = 2012-07-13]{\seq_count:N, \seq_count:c} +% \begin{syntax} +% \cs{seq_count:N} \meta{seq~var} +% \end{syntax} +% Leaves the number of items in the \meta{seq~var} in the input +% stream as an \meta{integer denotation}. The total number of items +% in a \meta{seq~var} includes those which are empty and duplicates, +% \emph{i.e.}~every item in a \meta{seq~var} is unique. +% \end{function} +% +% \section{Using the content of sequences directly} +% +% \begin{function}[EXP, added = 2013-05-26]{\seq_use:Nnnn, \seq_use:cnnn} +% \begin{syntax} +% \cs{seq_use:Nnnn} \meta{seq~var} \Arg{separator~between~two} \Arg{separator~between~more~than~two} \Arg{separator~between~final~two} +% \end{syntax} +% Places the contents of the \meta{seq~var} in the input stream, with +% the appropriate \meta{separator} between the items. Namely, if the +% sequence has more than two items, the \meta{separator between more +% than two} is placed between each pair of items except the last, +% for which the \meta{separator between final two} is used. If the +% sequence has exactly two items, then they are placed in the input stream +% separated by the \meta{separator between two}. If the sequence has +% a single item, it is placed in the input stream, and an empty sequence +% produces no output. An error is raised if the variable does +% not exist or if it is invalid. +% +% For example, +% \begin{verbatim} +% \seq_set_split:Nnn \l_tmpa_seq { | } { a | b | c | {de} | f } +% \seq_use:Nnnn \l_tmpa_seq { ~and~ } { ,~ } { ,~and~ } +% \end{verbatim} +% inserts \enquote{\texttt{a, b, c, de, and f}} in the input +% stream. The first separator argument is not used in this case +% because the sequence has more than $2$ items. +% \begin{texnote} +% The result is returned within the \tn{unexpanded} +% primitive (\cs{exp_not:n}), which means that the \meta{items} +% do not expand further when appearing in an \texttt{e}-type +% or \texttt{x}-type argument expansion. +% \end{texnote} +% \end{function} +% +% \begin{function}[EXP, added = 2013-05-26]{\seq_use:Nn, \seq_use:cn} +% \begin{syntax} +% \cs{seq_use:Nn} \meta{seq~var} \Arg{separator} +% \end{syntax} +% Places the contents of the \meta{seq~var} in the input stream, with +% the \meta{separator} between the items. If the sequence has +% a single item, it is placed in the input stream with no \meta{separator}, +% and an empty sequence produces no output. An error is raised if +% the variable does not exist or if it is invalid. +% +% For example, +% \begin{verbatim} +% \seq_set_split:Nnn \l_tmpa_seq { | } { a | b | c | {de} | f } +% \seq_use:Nn \l_tmpa_seq { ~and~ } +% \end{verbatim} +% inserts \enquote{\texttt{a and b and c and de and f}} in the input +% stream. +% \begin{texnote} +% The result is returned within the \tn{unexpanded} +% primitive (\cs{exp_not:n}), which means that the \meta{items} +% do not expand further when appearing in an \texttt{e}-type +% or \texttt{x}-type argument expansion. +% \end{texnote} +% \end{function} +% +% \section{Sequences as stacks} +% +% Sequences can be used as stacks, where data is pushed to and popped +% from the top of the sequence. (The left of a sequence is the top, for +% performance reasons.) The stack functions for sequences are not +% intended to be mixed with the general ordered data functions detailed +% in the previous section: a sequence should either be used as an +% ordered data type or as a stack, but not in both ways. +% +% \begin{function}[updated = 2012-05-14]{\seq_get:NN, \seq_get:cN} +% \begin{syntax} +% \cs{seq_get:NN} \meta{seq~var} \meta{token list variable} +% \end{syntax} +% Reads the top item from a \meta{seq~var} into the +% \meta{token list variable} without removing it from the +% \meta{seq~var}. The \meta{token list variable} is assigned locally. +% If \meta{seq~var} is empty the \meta{token list variable} is set to +% the special marker \cs{q_no_value}. +% \end{function} +% +% \begin{function}[updated = 2012-05-14]{\seq_pop:NN, \seq_pop:cN} +% \begin{syntax} +% \cs{seq_pop:NN} \meta{seq~var} \meta{token list variable} +% \end{syntax} +% Pops the top item from a \meta{seq~var} into the +% \meta{token list variable}. Both of the variables are assigned +% locally. If \meta{seq~var} is empty the \meta{token list variable} +% is set to the special marker \cs{q_no_value}. +% \end{function} +% +% \begin{function}[updated = 2012-05-14]{\seq_gpop:NN, \seq_gpop:cN} +% \begin{syntax} +% \cs{seq_gpop:NN} \meta{seq~var} \meta{token list variable} +% \end{syntax} +% Pops the top item from a \meta{seq~var} into the +% \meta{token list variable}. The \meta{seq~var} is modified globally, +% while the \meta{token list variable} is assigned locally. If +% \meta{seq~var} is empty the \meta{token list variable} is set to +% the special marker \cs{q_no_value}. +% \end{function} +% +% \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19]{\seq_get:NN, \seq_get:cN} +% \begin{syntax} +% \cs{seq_get:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code} +% \end{syntax} +% If the \meta{seq~var} is empty, leaves the \meta{false code} in the +% input stream. The value of the \meta{token list variable} is +% not defined in this case and should not be relied upon. If the +% \meta{seq~var} is non-empty, stores the top item from a +% \meta{seq~var} in the \meta{token list variable} without removing it from +% the \meta{seq~var}. The \meta{token list variable} is assigned locally. +% \end{function} +% +% \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19]{\seq_pop:NN, \seq_pop:cN} +% \begin{syntax} +% \cs{seq_pop:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code} +% \end{syntax} +% If the \meta{seq~var} is empty, leaves the \meta{false code} in the +% input stream. The value of the \meta{token list variable} is +% not defined in this case and should not be relied upon. If the +% \meta{seq~var} is non-empty, pops the top item from the +% \meta{seq~var} in the \meta{token list variable}, \emph{i.e.}~removes the +% item from the \meta{seq~var}. Both the \meta{seq~var} and the +% \meta{token list variable} are assigned locally. +% \end{function} +% +% \begin{function}[TF, added = 2012-05-14, updated = 2012-05-19]{\seq_gpop:NN, \seq_gpop:cN} +% \begin{syntax} +% \cs{seq_gpop:NNTF} \meta{seq~var} \meta{token list variable} \Arg{true code} \Arg{false code} +% \end{syntax} +% If the \meta{seq~var} is empty, leaves the \meta{false code} in the +% input stream. The value of the \meta{token list variable} is +% not defined in this case and should not be relied upon. If the +% \meta{seq~var} is non-empty, pops the top item from the \meta{seq~var} +% in the \meta{token list variable}, \emph{i.e.}~removes the item from the +% \meta{seq~var}. The \meta{seq~var} is modified globally, while the +% \meta{token list variable} is assigned locally. +% \end{function} +% +% \begin{function} +% { +% \seq_push:Nn, \seq_push:NV, \seq_push:Nv, \seq_push:Ne, +% \seq_push:No, +% \seq_push:cn, \seq_push:cV, \seq_push:cv, \seq_push:ce, +% \seq_push:co, +% \seq_gpush:Nn, \seq_gpush:NV, \seq_gpush:Nv, \seq_gpush:Ne, +% \seq_gpush:No, +% \seq_gpush:cn, \seq_gpush:cV, \seq_gpush:cv, \seq_gpush:ce, +% \seq_gpush:co +% } +% \begin{syntax} +% \cs{seq_push:Nn} \meta{seq~var} \Arg{item} +% \end{syntax} +% Adds the \Arg{item} to the top of the \meta{seq~var}. +% \end{function} +% +% \section{Sequences as sets} +% +% Sequences can also be used as sets, such that all of their items are +% distinct. Usage of sequences as sets is not currently widespread, +% hence no specific set function is provided. Instead, it is explained +% here how common set operations can be performed by combining several +% functions described in earlier sections. When using sequences to +% implement sets, one should be careful not to rely on the order of +% items in the sequence representing the set. +% +% Sets should not contain several occurrences of a given item. To make +% sure that a \meta{seq~var} only has distinct items, use +% \cs{seq_remove_duplicates:N} \meta{seq~var}. This function +% is relatively slow, and to avoid performance issues one should only +% use it when necessary. +% +% Some operations on a set \meta{seq~var} are straightforward. For +% instance, \cs{seq_count:N} \meta{seq~var} expands to the number of +% items, while \cs{seq_if_in:NnTF} \meta{seq~var} \Arg{item} tests if +% the \meta{item} is in the set. +% +% Adding an \meta{item} to a set \meta{seq~var} can be done by appending +% it to the \meta{seq~var} if it is not already in the \meta{seq~var}: +% \begin{quote}\ttfamily\parskip=0pt\obeylines +% \cs{seq_if_in:NnF} \meta{seq~var} \Arg{item} +% | |\{ \cs{seq_put_right:Nn} \meta{seq~var} \Arg{item} \} +% \end{quote} +% Removing an \meta{item} from a set \meta{seq~var} can be done using +% \cs{seq_remove_all:Nn}, +% \begin{quote}\ttfamily +% \cs{seq_remove_all:Nn} \meta{seq~var} \Arg{item} +% \end{quote} +% +% The intersection of two sets \meta{seq~var_1} and \meta{seq~var_2} can +% be stored into \meta{seq~var_3} by collecting items of +% \meta{seq~var_1} which are in \meta{seq~var_2}. +% \begin{quote}\ttfamily\parskip=0pt\obeylines +% \cs{seq_clear:N} \meta{seq~var_3} +% \cs{seq_map_inline:Nn} \meta{seq~var_1} +% | |\{ +% | |\cs{seq_if_in:NnT} \meta{seq~var_2} \{\#1\} +% | |\{ \cs{seq_put_right:Nn} \meta{seq~var_3} \{\#1\} \} +% | |\} +% \end{quote} +% The code as written here only works if \meta{seq~var_3} is different +% from the other two sequence variables. To cover all cases, items +% should first be collected in a sequence +% |\l__|\meta{pkg}|_internal_seq|, then \meta{seq~var_3} should be set +% equal to this internal sequence. The same remark applies to other set +% functions. +% +% The union of two sets \meta{seq~var_1} and \meta{seq~var_2} can be +% stored into \meta{seq~var_3} through +% \begin{quote}\ttfamily +% \cs{seq_concat:NNN} \meta{seq~var_3} \meta{seq~var_1} \meta{seq~var_2} \\ +% \cs{seq_remove_duplicates:N} \meta{seq~var_3} +% \end{quote} +% or by adding items to (a copy of) \meta{seq~var_1} one by one +% \begin{quote}\ttfamily\parskip=0pt\obeylines +% \cs{seq_set_eq:NN} \meta{seq~var_3} \meta{seq~var_1} +% \cs{seq_map_inline:Nn} \meta{seq~var_2} +% | |\{ +% | |\cs{seq_if_in:NnF} \meta{seq~var_3} \{\#1\} +% | |\{ \cs{seq_put_right:Nn} \meta{seq~var_3} \{\#1\} \} +% | |\} +% \end{quote} +% The second approach is faster than the first when the \meta{seq~var_2} +% is short compared to \meta{seq~var_1}. +% +% The difference of two sets \meta{seq~var_1} and \meta{seq~var_2} can +% be stored into \meta{seq~var_3} by removing items of the +% \meta{seq~var_2} from (a copy of) the \meta{seq~var_1} one by one. +% \begin{quote}\ttfamily\parskip=0pt\obeylines +% \cs{seq_set_eq:NN} \meta{seq~var_3} \meta{seq~var_1} +% \cs{seq_map_inline:Nn} \meta{seq~var_2} +% | |\{ \cs{seq_remove_all:Nn} \meta{seq~var_3} \{\#1\} \} +% \end{quote} +% +% The symmetric difference of two sets \meta{seq~var_1} and +% \meta{seq~var_2} can be stored into \meta{seq~var_3} by computing the +% difference between \meta{seq~var_1} and \meta{seq~var_2} and storing +% the result as |\l__|\meta{pkg}|_internal_seq|, then the difference +% between \meta{seq~var_2} and \meta{seq~var_1}, and finally +% concatenating the two differences to get the symmetric differences. +% \begin{quote}\ttfamily\parskip=0pt\obeylines +% \cs{seq_set_eq:NN} |\l__|\meta{pkg}|_internal_seq| \meta{seq~var_1} +% \cs{seq_map_inline:Nn} \meta{seq~var_2} +% | |\{ \cs{seq_remove_all:Nn} |\l__|\meta{pkg}|_internal_seq| \{\#1\} \} +% \cs{seq_set_eq:NN} \meta{seq~var_3} \meta{seq~var_2} +% \cs{seq_map_inline:Nn} \meta{seq~var_1} +% | |\{ \cs{seq_remove_all:Nn} \meta{seq~var_3} \{\#1\} \} +% \cs{seq_concat:NNN} \meta{seq~var_3} \meta{seq~var_3} |\l__|\meta{pkg}|_internal_seq| +% \end{quote} +% +% \section{Constant and scratch sequences} +% +% \begin{variable}[added = 2012-07-02]{\c_empty_seq} +% Constant that is always empty. +% \end{variable} +% +% \begin{variable}[added = 2012-04-26]{\l_tmpa_seq, \l_tmpb_seq} +% Scratch sequences for local assignment. These are never used by +% the kernel code, and so are safe for use with any \LaTeX3-defined +% function. However, they may be overwritten by other non-kernel +% code and so should only be used for short-term storage. +% \end{variable} +% +% \begin{variable}[added = 2012-04-26]{\g_tmpa_seq, \g_tmpb_seq} +% Scratch sequences for global assignment. These are never used by +% the kernel code, and so are safe for use with any \LaTeX3-defined +% function. However, they may be overwritten by other non-kernel +% code and so should only be used for short-term storage. +% \end{variable} +% +% \section{Viewing sequences} +% +% \begin{function}[updated = 2021-04-29]{\seq_show:N, \seq_show:c} +% \begin{syntax} +% \cs{seq_show:N} \meta{seq~var} +% \end{syntax} +% Displays the entries in the \meta{seq~var} in the terminal. +% \end{function} +% +% \begin{function}[added = 2014-08-12, updated = 2021-04-29]{\seq_log:N, \seq_log:c} +% \begin{syntax} +% \cs{seq_log:N} \meta{seq~var} +% \end{syntax} +% Writes the entries in the \meta{seq~var} in the log file. +% \end{function} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{l3seq} implementation} +% +% \TestFiles{m3seq002,m3seq003} +% +% \begin{macrocode} +%<*package> +% \end{macrocode} +% +% \begin{macrocode} +%<@@=seq> +% \end{macrocode} +% +% A sequence is a control sequence whose top-level expansion is of the +% form \enquote{\cs{s_@@} \cs{@@_item:n} \marg{item_1} \ldots +% \cs{@@_item:n} \marg{item_n}}, with a leading scan +% mark followed by $n$~items of the same form. An +% earlier implementation used the structure \enquote{\cs{seq_elt:w} +% \meta{item_1} \cs{seq_elt_end:} \ldots \cs{seq_elt:w} \meta{item_n} +% \cs{seq_elt_end:}}. This allowed rapid searching using a delimited +% function, but was not suitable for items containing |{|, |}| and |#| +% tokens, and also lead to the loss of surrounding braces around items +% +% \begin{function}[EXP]{\@@_item:n} +% \begin{syntax} +% \cs{@@_item:n} \Arg{item} +% \end{syntax} +% The internal token used to begin each sequence entry. If expanded +% outside of a mapping or manipulation function, an error is +% raised. The definition should always be set globally. +% \end{function} +% +% \begin{function}{\@@_push_item_def:n, \@@_push_item_def:e} +% \begin{syntax} +% \cs{@@_push_item_def:n} \Arg{code} +% \end{syntax} +% Saves the definition of \cs{@@_item:n} and redefines it to +% accept one parameter and expand to \meta{code}. This function +% should always be balanced by use of \cs{@@_pop_item_def:}. +% \end{function} +% +% \begin{function}{\@@_pop_item_def:} +% \begin{syntax} +% \cs{@@_pop_item_def:} +% \end{syntax} +% Restores the definition of \cs{@@_item:n} most recently saved by +% \cs{@@_push_item_def:n}. This function should always be used in +% a balanced pair with \cs{@@_push_item_def:n}. +% \end{function} +% +% \begin{variable}{\s_@@} +% This private scan mark. +% \begin{macrocode} +\scan_new:N \s_@@ +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\s_@@_mark,\s_@@_stop} +% Private scan marks. +% \begin{macrocode} +\scan_new:N \s_@@_mark +\scan_new:N \s_@@_stop +% \end{macrocode} +% \end{variable} +% +% \begin{macro}[EXP]{\@@_item:n} +% The delimiter is always defined, but when used incorrectly simply +% removes its argument and hits an undefined control sequence to +% raise an error. +% \begin{macrocode} +\cs_new:Npn \@@_item:n + { + \msg_expandable_error:nn { seq } { misused } + \use_none:n + } +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\l_@@_internal_a_tl, \l_@@_internal_b_tl} +% Scratch space for various internal uses. +% \begin{macrocode} +\tl_new:N \l_@@_internal_a_tl +\tl_new:N \l_@@_internal_b_tl +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\@@_tmp:w} +% Scratch function for internal use. +% \begin{macrocode} +\cs_new_eq:NN \@@_tmp:w ? +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\c_empty_seq} +% A sequence with no item, following the structure mentioned above. +% \begin{macrocode} +\tl_const:Nn \c_empty_seq { \s_@@ } +% \end{macrocode} +% \end{variable} +% +% \subsection{Allocation and initialisation} +% +% \begin{macro}{\seq_new:N, \seq_new:c} +% \UnitTested +% Sequences are initialized to \cs{c_empty_seq}. +% \begin{macrocode} +\cs_new_protected:Npn \seq_new:N #1 + { + \__kernel_chk_if_free_cs:N #1 + \cs_gset_eq:NN #1 \c_empty_seq + } +\cs_generate_variant:Nn \seq_new:N { c } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\seq_clear:N, \seq_clear:c} +% \UnitTested +% \begin{macro}{\seq_gclear:N, \seq_gclear:c} +% \UnitTested +% Clearing a sequence is similar to setting it equal to the empty one. +% \begin{macrocode} +\cs_new_protected:Npn \seq_clear:N #1 + { \seq_set_eq:NN #1 \c_empty_seq } +\cs_generate_variant:Nn \seq_clear:N { c } +\cs_new_protected:Npn \seq_gclear:N #1 + { \seq_gset_eq:NN #1 \c_empty_seq } +\cs_generate_variant:Nn \seq_gclear:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_clear_new:N, \seq_clear_new:c} +% \UnitTested +% \begin{macro}{\seq_gclear_new:N, \seq_gclear_new:c} +% \UnitTested +% Once again we copy code from the token list functions. +% \begin{macrocode} +\cs_new_protected:Npn \seq_clear_new:N #1 + { \seq_if_exist:NTF #1 { \seq_clear:N #1 } { \seq_new:N #1 } } +\cs_generate_variant:Nn \seq_clear_new:N { c } +\cs_new_protected:Npn \seq_gclear_new:N #1 + { \seq_if_exist:NTF #1 { \seq_gclear:N #1 } { \seq_new:N #1 } } +\cs_generate_variant:Nn \seq_gclear_new:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_set_eq:NN, \seq_set_eq:cN, \seq_set_eq:Nc, \seq_set_eq:cc} +% \UnitTested +% \begin{macro} +% {\seq_gset_eq:NN, \seq_gset_eq:cN, \seq_gset_eq:Nc, \seq_gset_eq:cc} +% \UnitTested +% Copying a sequence is the same as copying the underlying token list. +% \begin{macrocode} +\cs_new_eq:NN \seq_set_eq:NN \tl_set_eq:NN +\cs_new_eq:NN \seq_set_eq:Nc \tl_set_eq:Nc +\cs_new_eq:NN \seq_set_eq:cN \tl_set_eq:cN +\cs_new_eq:NN \seq_set_eq:cc \tl_set_eq:cc +\cs_new_eq:NN \seq_gset_eq:NN \tl_gset_eq:NN +\cs_new_eq:NN \seq_gset_eq:Nc \tl_gset_eq:Nc +\cs_new_eq:NN \seq_gset_eq:cN \tl_gset_eq:cN +\cs_new_eq:NN \seq_gset_eq:cc \tl_gset_eq:cc +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \seq_set_from_clist:NN, \seq_set_from_clist:cN, +% \seq_set_from_clist:Nc, \seq_set_from_clist:cc, +% \seq_set_from_clist:Nn, \seq_set_from_clist:cn +% } +% \begin{macro} +% { +% \seq_gset_from_clist:NN, \seq_gset_from_clist:cN, +% \seq_gset_from_clist:Nc, \seq_gset_from_clist:cc, +% \seq_gset_from_clist:Nn, \seq_gset_from_clist:cn +% } +% Setting a sequence from a comma-separated list is done using a simple +% mapping. +% \begin{macrocode} +\cs_new_protected:Npn \seq_set_from_clist:NN #1#2 + { + \__kernel_tl_set:Nx #1 + { \s_@@ \clist_map_function:NN #2 \@@_wrap_item:n } + } +\cs_new_protected:Npn \seq_set_from_clist:Nn #1#2 + { + \__kernel_tl_set:Nx #1 + { \s_@@ \clist_map_function:nN {#2} \@@_wrap_item:n } + } +\cs_new_protected:Npn \seq_gset_from_clist:NN #1#2 + { + \__kernel_tl_gset:Nx #1 + { \s_@@ \clist_map_function:NN #2 \@@_wrap_item:n } + } +\cs_new_protected:Npn \seq_gset_from_clist:Nn #1#2 + { + \__kernel_tl_gset:Nx #1 + { \s_@@ \clist_map_function:nN {#2} \@@_wrap_item:n } + } +\cs_generate_variant:Nn \seq_set_from_clist:NN { Nc } +\cs_generate_variant:Nn \seq_set_from_clist:NN { c , cc } +\cs_generate_variant:Nn \seq_set_from_clist:Nn { c } +\cs_generate_variant:Nn \seq_gset_from_clist:NN { Nc } +\cs_generate_variant:Nn \seq_gset_from_clist:NN { c , cc } +\cs_generate_variant:Nn \seq_gset_from_clist:Nn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_const_from_clist:Nn, \seq_const_from_clist:cn} +% Almost identical to \cs{seq_set_from_clist:Nn}. +% \begin{macrocode} +\cs_new_protected:Npn \seq_const_from_clist:Nn #1#2 + { + \tl_const:Ne #1 + { \s_@@ \clist_map_function:nN {#2} \@@_wrap_item:n } + } +\cs_generate_variant:Nn \seq_const_from_clist:Nn { c } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \seq_set_split:Nnn , +% \seq_set_split:NVn , \seq_set_split:NnV , \seq_set_split:NVV , +% \seq_set_split:Nne , \seq_set_split:Nee , +% \seq_set_split:Nnx , \seq_set_split:Nxx , +% \seq_gset_split:Nnn, +% \seq_gset_split:NVn, \seq_gset_split:NnV, \seq_gset_split:NVV, +% \seq_gset_split:Nne, \seq_gset_split:Nee, +% \seq_gset_split:Nnx, \seq_gset_split:Nxx +% } +% \begin{macro} +% { +% \seq_set_split_keep_spaces:Nnn , \seq_set_split_keep_spaces:NnV , +% \seq_gset_split_keep_spaces:Nnn, \seq_gset_split_keep_spaces:NnV +% } +% \begin{macro}{\@@_set_split:NNnn} +% \begin{macro} +% { +% \@@_set_split:Nw, \@@_set_split:w, +% \@@_set_split_end: +% } +% When the separator is empty, everything is very simple, just map +% \cs{@@_wrap_item:n} through the items of the last argument. +% For non-trivial separators, the goal is to split a given token list +% at the marker, strip spaces from each item, and remove one set of +% outer braces if after removing leading and trailing +% spaces the item is enclosed within braces. After +% \cs{tl_replace_all:Nnn}, the token list \cs{l_@@_internal_a_tl} +% is a repetition of the pattern +% \cs{@@_set_split:Nw} \cs{prg_do_nothing:} +% \meta{item with spaces} \cs{@@_set_split_end:}. +% Then, \texttt{e}-expansion causes \cs{@@_set_split:Nw} +% to trim spaces, and leaves its result as +% \cs{@@_set_split:w} \meta{trimmed item} +% \cs{@@_set_split_end:}. This is then converted +% to the \pkg{l3seq} internal structure by another +% \texttt{e}-expansion. In the first step, we insert +% \cs{prg_do_nothing:} to avoid losing braces too early: +% that would cause space trimming to act within those +% lost braces. The second step is solely there to strip +% braces which are outermost after space trimming. +% \begin{macrocode} +\cs_new_protected:Npn \seq_set_split:Nnn + { \@@_set_split:NNNnn \__kernel_tl_set:Nx \tl_trim_spaces:n } +\cs_new_protected:Npn \seq_gset_split:Nnn + { \@@_set_split:NNNnn \__kernel_tl_gset:Nx \tl_trim_spaces:n } +\cs_new_protected:Npn \seq_set_split_keep_spaces:Nnn + { \@@_set_split:NNNnn \__kernel_tl_set:Nx \exp_not:n } +\cs_new_protected:Npn \seq_gset_split_keep_spaces:Nnn + { \@@_set_split:NNNnn \__kernel_tl_gset:Nx \exp_not:n } +\cs_new_protected:Npn \@@_set_split:NNNnn #1#2#3#4#5 + { + \tl_if_empty:nTF {#4} + { + \tl_set:Nn \l_@@_internal_a_tl + { \tl_map_function:nN {#5} \@@_wrap_item:n } + } + { + \tl_set:Nn \l_@@_internal_a_tl + { + \@@_set_split:Nw #2 \prg_do_nothing: + #5 + \@@_set_split_end: + } + \tl_replace_all:Nnn \l_@@_internal_a_tl {#4} + { + \@@_set_split_end: + \@@_set_split:Nw #2 \prg_do_nothing: + } + \__kernel_tl_set:Nx \l_@@_internal_a_tl { \l_@@_internal_a_tl } + } + #1 #3 { \s_@@ \l_@@_internal_a_tl } + } +\cs_new:Npn \@@_set_split:Nw #1#2 \@@_set_split_end: + { + \exp_not:N \@@_set_split:w + \exp_args:No #1 {#2} + \exp_not:N \@@_set_split_end: + } +\cs_new:Npn \@@_set_split:w #1 \@@_set_split_end: + { \@@_wrap_item:n {#1} } +\cs_generate_variant:Nn \seq_set_split:Nnn { NV , NnV , NVV , Nne , Nee } +\cs_generate_variant:Nn \seq_set_split:Nnn { Nnx , Nxx } +\cs_generate_variant:Nn \seq_gset_split:Nnn { NV , NnV , NVV , Nne , Nee } +\cs_generate_variant:Nn \seq_gset_split:Nnn { Nnx , Nxx } +\cs_generate_variant:Nn \seq_set_split_keep_spaces:Nnn { NnV } +\cs_generate_variant:Nn \seq_gset_split_keep_spaces:Nnn { NnV } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_set_filter:NNn, \seq_gset_filter:NNn} +% \begin{macro}{\@@_set_filter:NNNn} +% Similar to \cs{seq_map_inline:Nn}, without a +% \cs{prg_break_point:} because the user's code +% is performed within the evaluation of a boolean expression, +% and skipping out of that would break horribly. +% The \cs{@@_wrap_item:n} function inserts the relevant +% \cs{@@_item:n} without expansion in the input stream, +% hence in the \texttt{e}-expanding assignment. +% \begin{macrocode} +\cs_new_protected:Npn \seq_set_filter:NNn + { \@@_set_filter:NNNn \__kernel_tl_set:Nx } +\cs_new_protected:Npn \seq_gset_filter:NNn + { \@@_set_filter:NNNn \__kernel_tl_gset:Nx } +\cs_new_protected:Npn \@@_set_filter:NNNn #1#2#3#4 + { + \@@_push_item_def:n { \bool_if:nT {#4} { \@@_wrap_item:n {##1} } } + #1 #2 { #3 } + \@@_pop_item_def: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_concat:NNN, \seq_concat:ccc} +% \UnitTested +% \begin{macro}{\seq_gconcat:NNN, \seq_gconcat:ccc} +% \UnitTested +% When concatenating sequences, one must remove the leading \cs{s_@@} +% of the second sequence. The result starts with \cs{s_@@} (of the +% first sequence), which stops \texttt{f}-expansion. +% \begin{macrocode} +\cs_new_protected:Npn \seq_concat:NNN #1#2#3 + { \tl_set:Nf #1 { \exp_after:wN \use_i:nn \exp_after:wN #2 #3 } } +\cs_new_protected:Npn \seq_gconcat:NNN #1#2#3 + { \tl_gset:Nf #1 { \exp_after:wN \use_i:nn \exp_after:wN #2 #3 } } +\cs_generate_variant:Nn \seq_concat:NNN { ccc } +\cs_generate_variant:Nn \seq_gconcat:NNN { ccc } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[pTF]{\seq_if_exist:N, \seq_if_exist:c} +% Copies of the \texttt{cs} functions defined in \pkg{l3basics}. +% \begin{macrocode} +\prg_new_eq_conditional:NNn \seq_if_exist:N \cs_if_exist:N + { TF , T , F , p } +\prg_new_eq_conditional:NNn \seq_if_exist:c \cs_if_exist:c + { TF , T , F , p } +% \end{macrocode} +% \end{macro} +% +% \subsection{Appending data to either end} +% +% \begin{macro}{ +% \seq_put_left:Nn, \seq_put_left:NV, \seq_put_left:Nv, \seq_put_left:Ne, +% \seq_put_left:No, \seq_put_left:Nx, +% \seq_put_left:cn, \seq_put_left:cV, \seq_put_left:cv,\seq_put_left:ce, +% \seq_put_left:co, \seq_put_left:cx +% } +% \UnitTested +% \begin{macro}{ +% \seq_gput_left:Nn, \seq_gput_left:NV, \seq_gput_left:Nv, \seq_gput_left:Ne, +% \seq_gput_left:No, \seq_gput_left:Nx, +% \seq_gput_left:cn, \seq_gput_left:cV, \seq_gput_left:cv, \seq_gput_left:ce, +% \seq_gput_left:co, \seq_gput_left:cx +% } +% \begin{macro}[EXP]{\@@_put_left_aux:w} +% When adding to the left of a sequence, remove \cs{s_@@}. This is +% done by \cs{@@_put_left_aux:w}, which also stops +% \texttt{f}-expansion. +% \begin{macrocode} +\cs_new_protected:Npn \seq_put_left:Nn #1#2 + { + \__kernel_tl_set:Nx #1 + { + \exp_not:n { \s_@@ \@@_item:n {#2} } + \exp_not:f { \exp_after:wN \@@_put_left_aux:w #1 } + } + } +\cs_new_protected:Npn \seq_gput_left:Nn #1#2 + { + \__kernel_tl_gset:Nx #1 + { + \exp_not:n { \s_@@ \@@_item:n {#2} } + \exp_not:f { \exp_after:wN \@@_put_left_aux:w #1 } + } + } +\cs_new:Npn \@@_put_left_aux:w \s_@@ { \exp_stop_f: } +\cs_generate_variant:Nn \seq_put_left:Nn { NV , Nv , Ne , No , Nx } +\cs_generate_variant:Nn \seq_put_left:Nn { c , cV , cv , ce , co ,cx } +\cs_generate_variant:Nn \seq_gput_left:Nn { NV , Nv , Ne , No , Nx } +\cs_generate_variant:Nn \seq_gput_left:Nn { c , cV , cv , ce , co , cx } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \seq_put_right:Nn, \seq_put_right:NV, \seq_put_right:Nv, \seq_put_right:Ne, +% \seq_put_right:No, \seq_put_right:Nx, +% \seq_put_right:cn, \seq_put_right:cV, \seq_put_right:cv, \seq_put_right:cx, +% \seq_put_right:co, \seq_put_right:cx +% } +% \UnitTested +% \begin{macro} +% { +% \seq_gput_right:Nn, \seq_gput_right:NV, \seq_gput_right:Nv, \seq_gput_right:Ne, +% \seq_gput_right:No, \seq_gput_right:Nx, +% \seq_gput_right:cn, \seq_gput_right:cV, \seq_gput_right:cv, \seq_gput_right:ce, +% \seq_gput_right:co, \seq_gput_right:cx +% } +% Since there is no trailing marker, adding an item to the right of a +% sequence simply means wrapping it in \cs{@@_item:n}. +% \begin{macrocode} +\cs_new_protected:Npn \seq_put_right:Nn #1#2 + { \tl_put_right:Nn #1 { \@@_item:n {#2} } } +\cs_new_protected:Npn \seq_gput_right:Nn #1#2 + { \tl_gput_right:Nn #1 { \@@_item:n {#2} } } +\cs_generate_variant:Nn \seq_put_right:Nn { NV , Nv , Ne , No , Nx } +\cs_generate_variant:Nn \seq_put_right:Nn { c , cV , cv , ce , co , cx } +\cs_generate_variant:Nn \seq_gput_right:Nn { NV , Nv , Ne , No , Nx } +\cs_generate_variant:Nn \seq_gput_right:Nn { c , cV , cv , ce , co , cx } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Modifying sequences} +% +% \begin{macro}{\@@_wrap_item:n} +% This function converts its argument to a proper sequence item +% in an \texttt{e}-expansion context. +% \begin{macrocode} +\cs_new:Npn \@@_wrap_item:n #1 { \exp_not:n { \@@_item:n {#1} } } +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\l_@@_remove_seq} +% An internal sequence for the removal routines. +% \begin{macrocode} +\seq_new:N \l_@@_remove_seq +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\seq_remove_duplicates:N, \seq_remove_duplicates:c} +% \UnitTested +% \begin{macro}{\seq_gremove_duplicates:N, \seq_gremove_duplicates:c} +% \UnitTested +% \begin{macro}{\@@_remove_duplicates:NN} +% Removing duplicates means making a new list then copying it. +% \begin{macrocode} +\cs_new_protected:Npn \seq_remove_duplicates:N + { \@@_remove_duplicates:NN \seq_set_eq:NN } +\cs_new_protected:Npn \seq_gremove_duplicates:N + { \@@_remove_duplicates:NN \seq_gset_eq:NN } +\cs_new_protected:Npn \@@_remove_duplicates:NN #1#2 + { + \seq_clear:N \l_@@_remove_seq + \seq_map_inline:Nn #2 + { + \seq_if_in:NnF \l_@@_remove_seq {##1} + { \seq_put_right:Nn \l_@@_remove_seq {##1} } + } + #1 #2 \l_@@_remove_seq + } +\cs_generate_variant:Nn \seq_remove_duplicates:N { c } +\cs_generate_variant:Nn \seq_gremove_duplicates:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \seq_remove_all:Nn, \seq_remove_all:NV, \seq_remove_all:Ne, +% \seq_remove_all:Nx, +% \seq_remove_all:cn, \seq_remove_all:cV, \seq_remove_all:ce, +% \seq_remove_all:cx, +% } +% \UnitTested +% \begin{macro} +% { +% \seq_gremove_all:Nn, \seq_gremove_all:NV, \seq_gremove_all:Ne, +% \seq_gremove_all:Nx, +% \seq_gremove_all:cn, \seq_gremove_all:cV, \seq_gremove_all:ce, +% \seq_gremove_all:Nx +% } +% \UnitTested +% \begin{macro}{\@@_remove_all_aux:NNn} +% The idea of the code here is to avoid a relatively expensive addition of +% items one at a time to an intermediate sequence. +% The approach taken is therefore similar to +% that in \cs{@@_pop_right:NNN}, using a \enquote{flexible} +% \texttt{e}-type expansion to do most of the work. As \cs{tl_if_eq:nnT} +% is not expandable, a two-part strategy is needed. First, the +% \texttt{e}-type expansion uses \cs{str_if_eq:nnT} to find potential +% matches. If one is found, the expansion is halted and the necessary +% set up takes place to use the \cs{tl_if_eq:NNT} test. The \texttt{e}-type +% is started again, including all of the items copied already. This +% happens repeatedly until the entire sequence has been scanned. The code +% is set up to avoid needing an intermediate scratch list: the lead-off +% \texttt{e}-type expansion (|#1 #2 {#2}|) ensures that nothing is lost. +% \begin{macrocode} +\cs_new_protected:Npn \seq_remove_all:Nn + { \@@_remove_all_aux:NNn \__kernel_tl_set:Nx } +\cs_new_protected:Npn \seq_gremove_all:Nn + { \@@_remove_all_aux:NNn \__kernel_tl_gset:Nx } +\cs_new_protected:Npn \@@_remove_all_aux:NNn #1#2#3 + { + \@@_push_item_def:n + { + \str_if_eq:nnT {##1} {#3} + { + \if_false: { \fi: } + \tl_set:Nn \l_@@_internal_b_tl {##1} + #1 #2 + { \if_false: } \fi: + \exp_not:o {#2} + \tl_if_eq:NNT \l_@@_internal_a_tl \l_@@_internal_b_tl + { \use_none:nn } + } + \@@_wrap_item:n {##1} + } + \tl_set:Nn \l_@@_internal_a_tl {#3} + #1 #2 {#2} + \@@_pop_item_def: + } +\cs_generate_variant:Nn \seq_remove_all:Nn { NV , Ne , c , cV , ce } +\cs_generate_variant:Nn \seq_remove_all:Nn { Nx , cx } +\cs_generate_variant:Nn \seq_gremove_all:Nn { NV , Ne , c , cV , ce } +\cs_generate_variant:Nn \seq_gremove_all:Nn { Nx , cx } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_int_eval:w} +% Useful to more quickly go through items. +% \begin{macrocode} +\cs_new_eq:NN \@@_int_eval:w \tex_numexpr:D +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[noTF]{\seq_set_item:Nnn, \seq_set_item:cnn, \seq_gset_item:Nnn, \seq_gset_item:cnn} +% \begin{macro}{\@@_set_item:NnnNN, \@@_set_item:nnNNNN, \@@_set_item_false:nnNNNN, \@@_set_item:nNnnNNNN} +% \begin{macro}[rEXP]{\@@_set_item:wn, \@@_set_item_end:w} +% The conditionals are distinguished from the |Nnn| versions by the +% last argument \cs{use_ii:nn} vs \cs{use_i:nn}. +% \begin{macrocode} +\cs_new_protected:Npn \seq_set_item:Nnn #1#2#3 + { \@@_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_set:Nx \use_i:nn } +\cs_new_protected:Npn \seq_gset_item:Nnn #1#2#3 + { \@@_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_gset:Nx \use_i:nn } +\cs_generate_variant:Nn \seq_set_item:Nnn { c } +\cs_generate_variant:Nn \seq_gset_item:Nnn { c } +\prg_new_protected_conditional:Npnn \seq_set_item:Nnn #1#2#3 { TF , T , F } + { \@@_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_set:Nx \use_ii:nn } +\prg_new_protected_conditional:Npnn \seq_gset_item:Nnn #1#2#3 { TF , T , F } + { \@@_set_item:NnnNN #1 {#2} {#3} \__kernel_tl_gset:Nx \use_ii:nn } +\prg_generate_conditional_variant:Nnn \seq_set_item:Nnn { c } { TF , T , F } +\prg_generate_conditional_variant:Nnn \seq_gset_item:Nnn { c } { TF , T , F } +% \end{macrocode} +% Save the item to be stored and evaluate the position and the sequence +% length only once. Then depending on the sign of the position, check +% that it is not bigger than the length (in absolute value) nor zero. +% \begin{macrocode} +\cs_new_protected:Npn \@@_set_item:NnnNN #1#2#3 + { + \tl_set:Nn \l_@@_internal_a_tl { \@@_item:n {#3} } + \exp_args:Nff \@@_set_item:nnNNNN + { \int_eval:n {#2} } { \seq_count:N #1 } #1 \use_none:nn + } +\cs_new_protected:Npn \@@_set_item:nnNNNN #1#2 + { + \int_compare:nNnTF {#1} > 0 + { \int_compare:nNnF {#1} > {#2} { \@@_set_item:nNnnNNNN { #1 - 1 } } } + { + \int_compare:nNnF {#1} < {-#2} + { + \int_compare:nNnF {#1} = 0 + { \@@_set_item:nNnnNNNN { #2 + #1 } } + } + } + \@@_set_item_false:nnNNNN {#1} {#2} + } +% \end{macrocode} +% If the position is not ok, \cs{@@_set_item_false:nnNNNN} calls an +% error or returns \texttt{false} (depending on the \cs{use_i:nn} vs +% \cs{use_ii:nn} argument mentioned above). +% \begin{macrocode} +\cs_new_protected:Npn \@@_set_item_false:nnNNNN #1#2#3#4#5#6 + { + #6 + { + \msg_error:nneee { seq } { item-too-large } + { \token_to_str:N #3 } {#2} {#1} + } + { \prg_return_false: } + } +% \end{macrocode} +% If the position is ok, \cs{@@_set_item:nNnnNNNN} makes the assignment +% and returns \texttt{true} (in the case of conditionnals). Here |#1| +% is an integer expression (position minus one), it needs to be +% evaluated. The sequence |#5| starts with \cs{s_@@} (even if empty), +% which stops the integer expression and is absorbed by it. The +% \cs{if_meaning:w} test is slightly faster than an integer test (but +% only works when testing against zero, hence the offset we chose in +% the position). When we are done skipping items, insert the saved +% item \cs{l_@@_internal_a_tl}. For |put| functions the last argument +% of \cs{@@_set_item_end:w} is \cs{use_none:nn} and it absorbs the +% item |#2| that we are removing: this is only useful for the |pop| +% functions. +% \begin{macrocode} +\cs_new_protected:Npn \@@_set_item:nNnnNNNN #1#2#3#4#5#6#7#8 + { + #7 #5 + { + \s_@@ + \exp_after:wN \@@_set_item:wn + \int_value:w \@@_int_eval:w #1 + #5 \s_@@_stop #6 + } + #8 { } { \prg_return_true: } + } +\cs_new:Npn \@@_set_item:wn #1 \@@_item:n #2 + { + \if_meaning:w 0 #1 \@@_set_item_end:w \fi: + \exp_not:n { \@@_item:n {#2} } + \exp_after:wN \@@_set_item:wn + \int_value:w \@@_int_eval:w #1 - 1 \s_@@ + } +\cs_new:Npn \@@_set_item_end:w #1 \exp_not:n #2 #3 \s_@@ #4 \s_@@_stop #5 + { + #1 + \exp_not:o \l_@@_internal_a_tl + \exp_not:n {#4} + #5 #2 + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% {\seq_reverse:N, \seq_reverse:c, \seq_greverse:N, \seq_greverse:c} +% \begin{macro}{\@@_reverse:NN} +% \begin{macro}[EXP]{\@@_reverse_item:nwn} +% Previously, \cs{seq_reverse:N} was coded by collecting the items +% in reverse order after an \cs{exp_stop_f:} marker. +% \begin{verbatim} +% \cs_new_protected:Npn \seq_reverse:N #1 +% { +% \cs_set_eq:NN \@@_item:n \@@_reverse_item:nw +% \tl_set:Nf #2 { #2 \exp_stop_f: } +% } +% \cs_new:Npn \@@_reverse_item:nw #1 #2 \exp_stop_f: +% { +% #2 \exp_stop_f: +% \@@_item:n {#1} +% } +% \end{verbatim} +% At first, this seems optimal, since we can forget about each item +% as soon as it is placed after \cs{exp_stop_f:}. Unfortunately, +% \TeX{}'s usual tail recursion does not take place in this case: +% since the following \cs{@@_reverse_item:nw} only reads +% tokens until \cs{exp_stop_f:}, and never reads the +% |\@@_item:n {#1}| left by the previous call, \TeX{} cannot +% remove that previous call from the stack, and in particular +% must retain the various macro parameters in memory, until the +% end of the replacement text is reached. The stack is thus +% only flushed after all the \cs{@@_reverse_item:nw} are +% expanded. Keeping track of the arguments of all those calls +% uses up a memory quadratic in the length of the sequence. +% \TeX{} can then not cope with more than a few thousand items. +% +% Instead, we collect the items in the argument +% of \cs{exp_not:n}. The previous calls are cleanly removed +% from the stack, and the memory consumption becomes linear. +% \begin{macrocode} +\cs_new_protected:Npn \seq_reverse:N + { \@@_reverse:NN \__kernel_tl_set:Nx } +\cs_new_protected:Npn \seq_greverse:N + { \@@_reverse:NN \__kernel_tl_gset:Nx } +\cs_new_protected:Npn \@@_reverse:NN #1 #2 + { + \cs_set_eq:NN \@@_tmp:w \@@_item:n + \cs_set_eq:NN \@@_item:n \@@_reverse_item:nwn + #1 #2 { #2 \exp_not:n { } } + \cs_set_eq:NN \@@_item:n \@@_tmp:w + } +\cs_new:Npn \@@_reverse_item:nwn #1 #2 \exp_not:n #3 + { + #2 + \exp_not:n { \@@_item:n {#1} #3 } + } +\cs_generate_variant:Nn \seq_reverse:N { c } +\cs_generate_variant:Nn \seq_greverse:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_sort:Nn, \seq_sort:cn, \seq_gsort:Nn, \seq_gsort:cn} +% Implemented in \pkg{l3sort}. +% \end{macro} +% +% \subsection{Sequence conditionals} +% +% \begin{macro}[pTF]{\seq_if_empty:N, \seq_if_empty:c} +% \UnitTested +% Similar to token lists, we compare with the empty sequence. +% \begin{macrocode} +\prg_new_conditional:Npnn \seq_if_empty:N #1 { p , T , F , TF } + { + \if_meaning:w #1 \c_empty_seq + \prg_return_true: + \else: + \prg_return_false: + \fi: + } +\prg_generate_conditional_variant:Nnn \seq_if_empty:N + { c } { p , T , F , TF } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\seq_shuffle:N, \seq_shuffle:c, \seq_gshuffle:N, \seq_gshuffle:c} +% \begin{macro}{\@@_shuffle:NN} +% \begin{macro}{\@@_shuffle_item:n} +% \begin{variable}{\g_@@_internal_seq} +% We apply the Fisher--Yates shuffle, storing items in \tn{toks} +% registers. We use the primitive \cs{tex_uniformdeviate:D} for +% speed reasons. Its non-uniformity is of order its argument divided +% by $2^{28}$, not too bad for small lists. For sequences with more +% than $13$ elements there are more possible permutations than +% possible seeds ($13!>2^{28}$) so the question of uniformity is +% somewhat moot. The integer variables are declared in \pkg{l3int}: +% load-order issues. +% \begin{macrocode} +\seq_new:N \g_@@_internal_seq +\cs_new_protected:Npn \seq_shuffle:N { \@@_shuffle:NN \seq_set_eq:NN } +\cs_new_protected:Npn \seq_gshuffle:N { \@@_shuffle:NN \seq_gset_eq:NN } +\cs_new_protected:Npn \@@_shuffle:NN #1#2 + { + \int_compare:nNnTF { \seq_count:N #2 } > \c_max_register_int + { + \msg_error:nne { seq } { shuffle-too-large } + { \token_to_str:N #2 } + } + { + \group_begin: + \int_zero:N \l_@@_internal_a_int + \@@_push_item_def: + \cs_gset_eq:NN \@@_item:n \@@_shuffle_item:n + #2 + \@@_pop_item_def: + \seq_gclear:N \g_@@_internal_seq + \int_step_inline:nn \l_@@_internal_a_int + { + \seq_gput_right:Ne \g_@@_internal_seq + { \tex_the:D \tex_toks:D ##1 } + } + \group_end: + #1 #2 \g_@@_internal_seq + \seq_gclear:N \g_@@_internal_seq + } + } +\cs_new_protected:Npn \@@_shuffle_item:n + { + \int_incr:N \l_@@_internal_a_int + \int_set:Nn \l_@@_internal_b_int + { 1 + \tex_uniformdeviate:D \l_@@_internal_a_int } + \tex_toks:D \l_@@_internal_a_int + = \tex_toks:D \l_@@_internal_b_int + \tex_toks:D \l_@@_internal_b_int + } +\cs_generate_variant:Nn \seq_shuffle:N { c } +\cs_generate_variant:Nn \seq_gshuffle:N { c } +% \end{macrocode} +% \end{variable} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[TF] +% { +% \seq_if_in:Nn, \seq_if_in:NV, \seq_if_in:Nv, \seq_if_in:Ne, +% \seq_if_in:No, \seq_if_in:Nx, +% \seq_if_in:cn, \seq_if_in:cV, \seq_if_in:cv,\seq_if_in:ce, +% \seq_if_in:co, \seq_if_in:cx +% } +% \UnitTested +% \begin{macro}{\@@_if_in:} +% The approach here is to define \cs{@@_item:n} to compare its +% argument with the test sequence. If the two items are equal, the +% mapping is terminated and \cs{group_end:} \cs{prg_return_true:} +% is inserted after skipping over the rest of the recursion. On the +% other hand, if there is no match then the loop breaks, returning +% \cs{prg_return_false:}. +% Everything is inside a group so that \cs{@@_item:n} is preserved +% in nested situations. +% \begin{macrocode} +\prg_new_protected_conditional:Npnn \seq_if_in:Nn #1#2 + { T , F , TF } + { + \group_begin: + \tl_set:Nn \l_@@_internal_a_tl {#2} + \cs_set_protected:Npn \@@_item:n ##1 + { + \tl_set:Nn \l_@@_internal_b_tl {##1} + \if_meaning:w \l_@@_internal_a_tl \l_@@_internal_b_tl + \exp_after:wN \@@_if_in: + \fi: + } + #1 + \group_end: + \prg_return_false: + \prg_break_point: + } +\cs_new:Npn \@@_if_in: + { \prg_break:n { \group_end: \prg_return_true: } } +\prg_generate_conditional_variant:Nnn \seq_if_in:Nn + { NV , Nv , Ne , No , Nx , c , cV , cv , ce , co , cx } { T , F , TF } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Recovering data from sequences} +% +% \begin{macro}{\@@_pop:NNNN, \@@_pop_TF:NNNN} +% The two \texttt{pop} functions share their emptiness tests. We also +% use a common emptiness test for all branching \texttt{get} and +% \texttt{pop} functions. +% \begin{macrocode} +\cs_new_protected:Npn \@@_pop:NNNN #1#2#3#4 + { + \if_meaning:w #3 \c_empty_seq + \tl_set:Nn #4 { \q_no_value } + \else: + #1#2#3#4 + \fi: + } +\cs_new_protected:Npn \@@_pop_TF:NNNN #1#2#3#4 + { + \if_meaning:w #3 \c_empty_seq + % \tl_set:Nn #4 { \q_no_value } + \prg_return_false: + \else: + #1#2#3#4 + \prg_return_true: + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\seq_get_left:NN, \seq_get_left:cN} +% \UnitTested +% \begin{macro}{\@@_get_left:wnw} +% Getting an item from the left of a sequence is pretty easy: just +% trim off the first item after \cs{@@_item:n} at the start. We +% append a \cs{q_no_value} item to cover the case of an empty sequence +% \begin{macrocode} +\cs_new_protected:Npn \seq_get_left:NN #1#2 + { + \__kernel_tl_set:Nx #2 + { + \exp_after:wN \@@_get_left:wnw + #1 \@@_item:n { \q_no_value } \s_@@_stop + } + } +\cs_new:Npn \@@_get_left:wnw #1 \@@_item:n #2#3 \s_@@_stop + { \exp_not:n {#2} } +\cs_generate_variant:Nn \seq_get_left:NN { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_pop_left:NN, \seq_pop_left:cN} +% \UnitTested +% \begin{macro}{\seq_gpop_left:NN, \seq_gpop_left:cN} +% \UnitTested +% \begin{macro}{\@@_pop_left:NNN, \@@_pop_left:wnwNNN} +% The approach to popping an item is pretty similar to that to get +% an item, with the only difference being that the sequence itself has +% to be redefined. This makes it more sensible to use an auxiliary +% function for the local and global cases. +% \begin{macrocode} +\cs_new_protected:Npn \seq_pop_left:NN + { \@@_pop:NNNN \@@_pop_left:NNN \tl_set:Nn } +\cs_new_protected:Npn \seq_gpop_left:NN + { \@@_pop:NNNN \@@_pop_left:NNN \tl_gset:Nn } +\cs_new_protected:Npn \@@_pop_left:NNN #1#2#3 + { \exp_after:wN \@@_pop_left:wnwNNN #2 \s_@@_stop #1#2#3 } +\cs_new_protected:Npn \@@_pop_left:wnwNNN + #1 \@@_item:n #2#3 \s_@@_stop #4#5#6 + { + #4 #5 { #1 #3 } + \tl_set:Nn #6 {#2} + } +\cs_generate_variant:Nn \seq_pop_left:NN { c } +\cs_generate_variant:Nn \seq_gpop_left:NN { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_get_right:NN, \seq_get_right:cN} +% \UnitTested +% \begin{macro}[EXP]{\@@_get_right_loop:nw, \@@_get_right_end:NnN} +% First remove \cs{s_@@} and prepend \cs{q_no_value}. The first +% argument of \cs{@@_get_right_loop:nw} is the last item found, and +% the second argument is empty until the end of the loop, where it is +% code that applies \cs{exp_not:n} to the last item and ends the loop. +% \begin{macrocode} +\cs_new_protected:Npn \seq_get_right:NN #1#2 + { + \__kernel_tl_set:Nx #2 + { + \exp_after:wN \use_i_ii:nnn + \exp_after:wN \@@_get_right_loop:nw + \exp_after:wN \q_no_value + #1 + \@@_get_right_end:NnN \@@_item:n + } + } +\cs_new:Npn \@@_get_right_loop:nw #1#2 \@@_item:n + { + #2 \use_none:n {#1} + \@@_get_right_loop:nw + } +\cs_new:Npn \@@_get_right_end:NnN #1#2#3 { \exp_not:n {#2} } +\cs_generate_variant:Nn \seq_get_right:NN { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_pop_right:NN, \seq_pop_right:cN} +% \UnitTested +% \begin{macro}{\seq_gpop_right:NN, \seq_gpop_right:cN} +% \UnitTested +% \begin{macro}{\@@_pop_right:NNN, \@@_pop_right_loop:nn} +% The approach to popping from the right is a bit more involved, but does +% use some of the same ideas as getting from the right. What is needed is a +% \enquote{flexible length} way to set a token list variable. This is +% supplied by the |{ \if_false: } \fi:| \ldots +% |\if_false: { \fi: }| construct. Using an \texttt{e}-type +% expansion and a \enquote{non-expanding} definition for \cs{@@_item:n}, +% the left-most $n - 1$ entries in a sequence of $n$ items are stored +% back in the sequence. That needs a loop of unknown length, hence using the +% strange \cs{if_false:} way of including braces. When the last item +% of the sequence is reached, the closing brace for the assignment is +% inserted, and |\tl_set:Nn #3| is inserted in front of the final +% entry. This therefore does the pop assignment. One more iteration +% is performed, with an empty argument and \cs{use_none:nn}, which +% finally stops the loop. +% \begin{macrocode} +\cs_new_protected:Npn \seq_pop_right:NN + { \@@_pop:NNNN \@@_pop_right:NNN \__kernel_tl_set:Nx } +\cs_new_protected:Npn \seq_gpop_right:NN + { \@@_pop:NNNN \@@_pop_right:NNN \__kernel_tl_gset:Nx } +\cs_new_protected:Npn \@@_pop_right:NNN #1#2#3 + { + \cs_set_eq:NN \@@_tmp:w \@@_item:n + \cs_set_eq:NN \@@_item:n \scan_stop: + #1 #2 + { \if_false: } \fi: \s_@@ + \exp_after:wN \use_i:nnn + \exp_after:wN \@@_pop_right_loop:nn + #2 + { + \if_false: { \fi: } + \__kernel_tl_set:Nx #3 + } + { } \use_none:nn + \cs_set_eq:NN \@@_item:n \@@_tmp:w + } +\cs_new:Npn \@@_pop_right_loop:nn #1#2 + { + #2 { \exp_not:n {#1} } + \@@_pop_right_loop:nn + } +\cs_generate_variant:Nn \seq_pop_right:NN { c } +\cs_generate_variant:Nn \seq_gpop_right:NN { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[TF]{\seq_get_left:NN, \seq_get_left:cN} +% \begin{macro}[TF]{\seq_get_right:NN, \seq_get_right:cN} +% Getting from the left or right with a check on the results. The +% first argument to \cs{@@_pop_TF:NNNN} is left unused. +% \begin{macrocode} +\prg_new_protected_conditional:Npnn \seq_get_left:NN #1#2 { T , F , TF } + { \@@_pop_TF:NNNN \prg_do_nothing: \seq_get_left:NN #1#2 } +\prg_new_protected_conditional:Npnn \seq_get_right:NN #1#2 { T , F , TF } + { \@@_pop_TF:NNNN \prg_do_nothing: \seq_get_right:NN #1#2 } +\prg_generate_conditional_variant:Nnn \seq_get_left:NN + { c } { T , F , TF } +\prg_generate_conditional_variant:Nnn \seq_get_right:NN + { c } { T , F , TF } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[TF]{\seq_pop_left:NN, \seq_pop_left:cN} +% \begin{macro}[TF]{\seq_gpop_left:NN, \seq_gpop_left:cN} +% \begin{macro}[TF]{\seq_pop_right:NN, \seq_pop_right:cN} +% \begin{macro}[TF]{\seq_gpop_right:NN, \seq_gpop_right:cN} +% More or less the same for popping. +% \begin{macrocode} +\prg_new_protected_conditional:Npnn \seq_pop_left:NN #1#2 + { T , F , TF } + { \@@_pop_TF:NNNN \@@_pop_left:NNN \tl_set:Nn #1 #2 } +\prg_new_protected_conditional:Npnn \seq_gpop_left:NN #1#2 + { T , F , TF } + { \@@_pop_TF:NNNN \@@_pop_left:NNN \tl_gset:Nn #1 #2 } +\prg_new_protected_conditional:Npnn \seq_pop_right:NN #1#2 + { T , F , TF } + { \@@_pop_TF:NNNN \@@_pop_right:NNN \__kernel_tl_set:Nx #1 #2 } +\prg_new_protected_conditional:Npnn \seq_gpop_right:NN #1#2 + { T , F , TF } + { \@@_pop_TF:NNNN \@@_pop_right:NNN \__kernel_tl_gset:Nx #1 #2 } +\prg_generate_conditional_variant:Nnn \seq_pop_left:NN { c } + { T , F , TF } +\prg_generate_conditional_variant:Nnn \seq_gpop_left:NN { c } + { T , F , TF } +\prg_generate_conditional_variant:Nnn \seq_pop_right:NN { c } + { T , F , TF } +\prg_generate_conditional_variant:Nnn \seq_gpop_right:NN { c } + { T , F , TF } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% {\seq_item:Nn, \seq_item:NV, \seq_item:Ne, \seq_item:cn, \seq_item:cV, \seq_item:ce} +% \begin{macro}{\@@_item:wNn, \@@_item:nN, \@@_item:nwn} +% The idea here is to find the offset of the item from the left, then use +% a loop to grab the correct item. If the resulting offset is too large, +% then the argument delimited by \cs{@@_item:n} is \cs{prg_break:} instead +% of being empty, terminating the loop and returning nothing at all. +% \begin{macrocode} +\cs_new:Npn \seq_item:Nn #1 + { \exp_after:wN \@@_item:wNn #1 \s_@@_stop #1 } +\cs_new:Npn \@@_item:wNn \s_@@ #1 \s_@@_stop #2#3 + { + \exp_args:Nf \@@_item:nwn + { \exp_args:Nf \@@_item:nN { \int_eval:n {#3} } #2 } + #1 + \prg_break: \@@_item:n { } + \prg_break_point: + } +\cs_new:Npn \@@_item:nN #1#2 + { + \int_compare:nNnTF {#1} < 0 + { \int_eval:n { \seq_count:N #2 + 1 + #1 } } + {#1} + } +\cs_new:Npn \@@_item:nwn #1#2 \@@_item:n #3 + { + #2 + \int_compare:nNnTF {#1} = 1 + { \prg_break:n { \exp_not:n {#3} } } + { \exp_args:Nf \@@_item:nwn { \int_eval:n { #1 - 1 } } } + } +\cs_generate_variant:Nn \seq_item:Nn { NV , Ne , c , cV , ce } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_rand_item:N, \seq_rand_item:c} +% Importantly, \cs{seq_item:Nn} only evaluates its argument once. +% \begin{macrocode} +\cs_new:Npn \seq_rand_item:N #1 + { + \seq_if_empty:NF #1 + { \seq_item:Nn #1 { \int_rand:nn { 1 } { \seq_count:N #1 } } } + } +\cs_generate_variant:Nn \seq_rand_item:N { c } +% \end{macrocode} +% \end{macro} +% +% \subsection{Mapping over sequences} +% +% \begin{macro}{\seq_map_break:} +% \UnitTested +% \begin{macro}{\seq_map_break:n} +% \UnitTested +% To break a function, the special token \cs{prg_break_point:Nn} is +% used to find the end of the code. Any ending code is then inserted +% before the return value of \cs{seq_map_break:n} is inserted. +% \begin{macrocode} +\cs_new:Npn \seq_map_break: + { \prg_map_break:Nn \seq_map_break: { } } +\cs_new:Npn \seq_map_break:n + { \prg_map_break:Nn \seq_map_break: } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_map_function:NN, \seq_map_function:cN} +% \UnitTested +% \begin{macro}[rEXP]{\@@_map_function:Nw} +% The idea here is to apply the code of |#2| to each item in the +% sequence without altering the definition of \cs{@@_item:n}. The +% even-numbered arguments of \cs{@@_map_function:Nw} delimited by +% \cs{@@_item:n} are almost always empty, except +% at the end of the loop where it is \cs{prg_break:}. This allows to +% break the loop without needing to do a (relatively-expensive) quark +% test. +% \begin{macrocode} +\cs_new:Npn \seq_map_function:NN #1#2 + { + \exp_after:wN \use_i_ii:nnn + \exp_after:wN \@@_map_function:Nw + \exp_after:wN #2 + #1 + \prg_break: + \@@_item:n { } \@@_item:n { } \@@_item:n { } \@@_item:n { } + \prg_break_point: + \prg_break_point:Nn \seq_map_break: { } + } +\cs_new:Npn \@@_map_function:Nw #1 + #2 \@@_item:n #3 + #4 \@@_item:n #5 + #6 \@@_item:n #7 + #8 \@@_item:n #9 + { + #2 #1 {#3} + #4 #1 {#5} + #6 #1 {#7} + #8 #1 {#9} + \@@_map_function:Nw #1 + } +\cs_generate_variant:Nn \seq_map_function:NN { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_push_item_def:n, \@@_push_item_def:e} +% \begin{macro}{\@@_push_item_def:} +% \begin{macro}{\@@_pop_item_def:} +% The definition of \cs{@@_item:n} needs to be saved and restored at +% various points within the mapping and manipulation code. That is handled +% here: as always, this approach uses global assignments. +% \begin{macrocode} +\cs_new_protected:Npn \@@_push_item_def:n + { + \@@_push_item_def: + \cs_gset:Npn \@@_item:n ##1 + } +\cs_new_protected:Npn \@@_push_item_def:e + { + \@@_push_item_def: + \cs_gset:Npe \@@_item:n ##1 + } +\cs_new_protected:Npn \@@_push_item_def: + { + \int_gincr:N \g__kernel_prg_map_int + \cs_gset_eq:cN { @@_map_ \int_use:N \g__kernel_prg_map_int :w } + \@@_item:n + } +\cs_new_protected:Npn \@@_pop_item_def: + { + \cs_gset_eq:Nc \@@_item:n + { @@_map_ \int_use:N \g__kernel_prg_map_int :w } + \int_gdecr:N \g__kernel_prg_map_int + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_map_inline:Nn, \seq_map_inline:cn} +% \UnitTested +% The idea here is that \cs{@@_item:n} is already \enquote{applied} to +% each item in a sequence, and so an in-line mapping is just a case of +% redefining \cs{@@_item:n}. +% \begin{macrocode} +\cs_new_protected:Npn \seq_map_inline:Nn #1#2 + { + \@@_push_item_def:n {#2} + #1 + \prg_break_point:Nn \seq_map_break: { \@@_pop_item_def: } + } +\cs_generate_variant:Nn \seq_map_inline:Nn { c } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\seq_map_tokens:Nn, \seq_map_tokens:cn} +% \begin{macro}{\@@_map_tokens:nw} +% This is based on the function mapping but using the same tricks as +% described for \cs{prop_map_tokens:Nn}. The idea is to remove the leading +% \cs{s_@@} and apply the tokens such that they are safe with the +% break points, hence the \cs{use:n}. +% \begin{macrocode} +\cs_new:Npn \seq_map_tokens:Nn #1#2 + { + \exp_last_unbraced:Nno + \use_i:nn { \@@_map_tokens:nw {#2} } #1 + \prg_break: + \@@_item:n { } \@@_item:n { } \@@_item:n { } \@@_item:n { } + \prg_break_point: + \prg_break_point:Nn \seq_map_break: { } + } +\cs_generate_variant:Nn \seq_map_tokens:Nn { c } +\cs_new:Npn \@@_map_tokens:nw #1 + #2 \@@_item:n #3 + #4 \@@_item:n #5 + #6 \@@_item:n #7 + #8 \@@_item:n #9 + { + #2 \use:n {#1} {#3} + #4 \use:n {#1} {#5} + #6 \use:n {#1} {#7} + #8 \use:n {#1} {#9} + \@@_map_tokens:nw {#1} + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \seq_map_variable:NNn, \seq_map_variable:Ncn, +% \seq_map_variable:cNn, \seq_map_variable:ccn +% } +% \UnitTested +% This is just a specialised version of the in-line mapping function, +% using an \texttt{e}-type expansion for the code set up so that the +% number of |#| tokens required is as expected. +% \begin{macrocode} +\cs_new_protected:Npn \seq_map_variable:NNn #1#2#3 + { + \@@_push_item_def:e + { + \tl_set:Nn \exp_not:N #2 {##1} + \exp_not:n {#3} + } + #1 + \prg_break_point:Nn \seq_map_break: { \@@_pop_item_def: } + } +\cs_generate_variant:Nn \seq_map_variable:NNn { Nc } +\cs_generate_variant:Nn \seq_map_variable:NNn { c , cc } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \seq_map_indexed_function:NN, \seq_map_indexed_inline:Nn, +% \@@_map_indexed:nNN, \@@_map_indexed:Nw +% } +% Similar to \cs{seq_map_function:NN} but we keep track of the item +% index as a |;|-delimited argument of \cs{@@_map_indexed:Nw}. +% \begin{macrocode} +\cs_new:Npn \seq_map_indexed_function:NN #1#2 + { + \@@_map_indexed:NN #1#2 + \prg_break_point:Nn \seq_map_break: { } + } +\cs_new_protected:Npn \seq_map_indexed_inline:Nn #1#2 + { + \int_gincr:N \g__kernel_prg_map_int + \cs_gset_protected:cpn + { @@_map_ \int_use:N \g__kernel_prg_map_int :w } ##1##2 {#2} + \exp_args:NNc \@@_map_indexed:NN #1 + { @@_map_ \int_use:N \g__kernel_prg_map_int :w } + \prg_break_point:Nn \seq_map_break: + { \int_gdecr:N \g__kernel_prg_map_int } + } +\cs_new:Npn \@@_map_indexed:NN #1#2 + { + \exp_after:wN \@@_map_indexed:Nw + \exp_after:wN #2 + \int_value:w 1 + \exp_after:wN \use_i:nn + \exp_after:wN ; + #1 + \prg_break: \@@_item:n { } \prg_break_point: + } +\cs_new:Npn \@@_map_indexed:Nw #1#2 ; #3 \@@_item:n #4 + { + #3 + #1 {#2} {#4} + \exp_after:wN \@@_map_indexed:Nw + \exp_after:wN #1 + \int_value:w \int_eval:w 1 + #2 ; + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \seq_map_pairwise_function:NNN, \seq_map_pairwise_function:NcN, +% \seq_map_pairwise_function:cNN, \seq_map_pairwise_function:ccN +% } +% \begin{macro} +% { +% \@@_map_pairwise_function:wNN, \@@_map_pairwise_function:wNw, +% \@@_map_pairwise_function:Nnnwnn +% } +% The idea is to first expand both sequences, adding the +% usual |{ ? \prg_break: } { }| to the end of each one. This is +% most conveniently done in two steps using an auxiliary function. +% The mapping then throws away the first tokens of |#2| and |#5|, +% which for items in both sequences are \cs{s_@@} +% \cs{@@_item:n}. The function to be mapped are then be applied to +% the two entries. When the code hits the end of one of the +% sequences, the break material stops the entire loop and tidy up. +% This avoids needing to find the count of the two sequences, or +% worrying about which is longer. +% \begin{macrocode} +\cs_new:Npn \seq_map_pairwise_function:NNN #1#2#3 + { \exp_after:wN \@@_map_pairwise_function:wNN #2 \s_@@_stop #1 #3 } +\cs_new:Npn \@@_map_pairwise_function:wNN \s_@@ #1 \s_@@_stop #2#3 + { + \exp_after:wN \@@_map_pairwise_function:wNw #2 \s_@@_stop #3 + #1 { ? \prg_break: } { } + \prg_break_point: + } +\cs_new:Npn \@@_map_pairwise_function:wNw \s_@@ #1 \s_@@_stop #2 + { + \@@_map_pairwise_function:Nnnwnn #2 + #1 { ? \prg_break: } { } + \s_@@_stop + } +\cs_new:Npn \@@_map_pairwise_function:Nnnwnn #1#2#3#4 \s_@@_stop #5#6 + { + \use_none:n #2 + \use_none:n #5 + #1 {#3} {#6} + \@@_map_pairwise_function:Nnnwnn #1 #4 \s_@@_stop + } +\cs_generate_variant:Nn \seq_map_pairwise_function:NNN { Nc , c , cc } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_set_map_e:NNn, \seq_gset_map_e:NNn} +% \begin{macro}{\@@_set_map_e:NNNn} +% Very similar to \cs{seq_set_filter:NNn}. We could actually +% merge the two within a single function, but it would have weird +% semantics. +% \begin{macrocode} +\cs_new_protected:Npn \seq_set_map_e:NNn + { \@@_set_map_e:NNNn \__kernel_tl_set:Nx } +\cs_new_protected:Npn \seq_gset_map_e:NNn + { \@@_set_map_e:NNNn \__kernel_tl_gset:Nx } +\cs_new_protected:Npn \@@_set_map_e:NNNn #1#2#3#4 + { + \@@_push_item_def:n { \exp_not:N \@@_item:n {#4} } + #1 #2 { #3 } + \@@_pop_item_def: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_set_map:NNn, \seq_gset_map:NNn} +% \begin{macro}{\@@_set_map:NNNn} +% Similar to \cs{seq_set_map_e:NNn}, but prevents expansion of the +% <inline function>. +% \begin{macrocode} +\cs_new_protected:Npn \seq_set_map:NNn + { \@@_set_map:NNNn \__kernel_tl_set:Nx } +\cs_new_protected:Npn \seq_gset_map:NNn + { \@@_set_map:NNNn \__kernel_tl_gset:Nx } +\cs_new_protected:Npn \@@_set_map:NNNn #1#2#3#4 + { + \@@_push_item_def:n { \exp_not:n { \@@_item:n {#4} } } + #1 #2 { #3 } + \@@_pop_item_def: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_count:N, \seq_count:c} +% \begin{macro}{\@@_count:w, \@@_count_end:w} +% Since counting the items in a sequence is quite common, we optimize +% it by grabbing $8$~items at a time and correspondingly adding $8$ to +% an integer expression. At the end of the loop, |#9| is +% \cs{@@_count_end:w} instead of being empty. It removes |8+| and +% instead places the number of \cs{@@_item:n} that \cs{@@_count:w} +% grabbed before reaching the end of the sequence. +% \begin{macrocode} +\cs_new:Npn \seq_count:N #1 + { + \int_eval:n + { + \exp_after:wN \use_i:nn + \exp_after:wN \@@_count:w + #1 + \@@_count_end:w \@@_item:n 7 + \@@_count_end:w \@@_item:n 6 + \@@_count_end:w \@@_item:n 5 + \@@_count_end:w \@@_item:n 4 + \@@_count_end:w \@@_item:n 3 + \@@_count_end:w \@@_item:n 2 + \@@_count_end:w \@@_item:n 1 + \@@_count_end:w \@@_item:n 0 + \prg_break_point: + } + } +\cs_new:Npn \@@_count:w + #1 \@@_item:n #2 \@@_item:n #3 \@@_item:n #4 \@@_item:n + #5 \@@_item:n #6 \@@_item:n #7 \@@_item:n #8 #9 \@@_item:n + { #9 8 + \@@_count:w } +\cs_new:Npn \@@_count_end:w 8 + \@@_count:w #1#2 \prg_break_point: {#1} +\cs_generate_variant:Nn \seq_count:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Using sequences} +% +% \begin{macro}[EXP]{\seq_use:Nnnn, \seq_use:cnnn} +% \begin{macro}[EXP] +% {\@@_use:NNnNnn, \@@_use_setup:w, \@@_use:nwwwwnwn, \@@_use:nwwn} +% \begin{macro}[EXP]{\seq_use:Nn, \seq_use:cn} +% See \cs{clist_use:Nnnn} for a general explanation. The main +% difference is that we use \cs{@@_item:n} as a delimiter rather than +% commas. We also need to add \cs{@@_item:n} at various places, and +% \cs{s_@@}. +% \begin{macrocode} +\cs_new:Npn \seq_use:Nnnn #1#2#3#4 + { + \seq_if_exist:NTF #1 + { + \int_case:nnF { \seq_count:N #1 } + { + { 0 } { } + { 1 } { \exp_after:wN \@@_use:NNnNnn #1 ? { } { } } + { 2 } { \exp_after:wN \@@_use:NNnNnn #1 {#2} } + } + { + \exp_after:wN \@@_use_setup:w #1 \@@_item:n + \s_@@_mark { \@@_use:nwwwwnwn {#3} } + \s_@@_mark { \@@_use:nwwn {#4} } + \s_@@_stop { } + } + } + { + \msg_expandable_error:nnn + { kernel } { bad-variable } {#1} + } + } +\cs_generate_variant:Nn \seq_use:Nnnn { c } +\cs_new:Npn \@@_use:NNnNnn #1#2#3#4#5#6 { \exp_not:n { #3 #6 #5 } } +\cs_new:Npn \@@_use_setup:w \s_@@ { \@@_use:nwwwwnwn { } } +\cs_new:Npn \@@_use:nwwwwnwn + #1 \@@_item:n #2 \@@_item:n #3 \@@_item:n #4#5 + \s_@@_mark #6#7 \s_@@_stop #8 + { + #6 \@@_item:n {#3} \@@_item:n {#4} #5 + \s_@@_mark {#6} #7 \s_@@_stop { #8 #1 #2 } + } +\cs_new:Npn \@@_use:nwwn #1 \@@_item:n #2 #3 \s_@@_stop #4 + { \exp_not:n { #4 #1 #2 } } +\cs_new:Npn \seq_use:Nn #1#2 + { \seq_use:Nnnn #1 {#2} {#2} {#2} } +\cs_generate_variant:Nn \seq_use:Nn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Sequence stacks} +% +% The same functions as for sequences, but with the correct naming. +% +% \begin{macro}{ +% \seq_push:Nn, \seq_push:NV, \seq_push:Nv, \seq_push:Ne, +% \seq_push:No, \seq_push:Nx, +% \seq_push:cn, \seq_push:cV, \seq_push:cv, \seq_push:ce, +% \seq_push:co, \seq_push:cx +% } +% \UnitTested +% \begin{macro}{ +% \seq_gpush:Nn, \seq_gpush:NV, \seq_gpush:Nv, \seq_gpush:Ne, +% \seq_gpush:No, \seq_gpush:Nx, +% \seq_gpush:cn, \seq_gpush:cV, \seq_gpush:cv, \seq_gpush:ce, +% \seq_gpush:co, \seq_gpush:cx +% } +% \UnitTested +% Pushing to a sequence is the same as adding on the left. +% \begin{macrocode} +\cs_new_eq:NN \seq_push:Nn \seq_put_left:Nn +\cs_generate_variant:Nn \seq_push:Nn { NV , Nv , Ne , c , cV , cv , ce } +\cs_generate_variant:Nn \seq_push:Nn { No , Nx , co , cx } +\cs_new_eq:NN \seq_gpush:Nn \seq_gput_left:Nn +\cs_generate_variant:Nn \seq_gpush:Nn { NV , Nv , Ne , c , cV , cv , ce } +\cs_generate_variant:Nn \seq_gpush:Nn { No , Nx , co , cx } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\seq_get:NN, \seq_get:cN} +% \UnitTested +% \begin{macro}{\seq_pop:NN, \seq_pop:cN} +% \UnitTested +% \begin{macro}{\seq_gpop:NN, \seq_gpop:cN} +% \UnitTested +% In most cases, getting items from the stack does not need to specify +% that this is from the left. So alias are provided. +% \begin{macrocode} +\cs_new_eq:NN \seq_get:NN \seq_get_left:NN +\cs_new_eq:NN \seq_get:cN \seq_get_left:cN +\cs_new_eq:NN \seq_pop:NN \seq_pop_left:NN +\cs_new_eq:NN \seq_pop:cN \seq_pop_left:cN +\cs_new_eq:NN \seq_gpop:NN \seq_gpop_left:NN +\cs_new_eq:NN \seq_gpop:cN \seq_gpop_left:cN +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[TF]{\seq_get:NN, \seq_get:cN} +% \begin{macro}[TF]{\seq_pop:NN, \seq_pop:cN} +% \begin{macro}[TF]{\seq_gpop:NN, \seq_gpop:cN} +% More copies. +% \begin{macrocode} +\prg_new_eq_conditional:NNn \seq_get:NN \seq_get_left:NN { T , F , TF } +\prg_new_eq_conditional:NNn \seq_get:cN \seq_get_left:cN { T , F , TF } +\prg_new_eq_conditional:NNn \seq_pop:NN \seq_pop_left:NN { T , F , TF } +\prg_new_eq_conditional:NNn \seq_pop:cN \seq_pop_left:cN { T , F , TF } +\prg_new_eq_conditional:NNn \seq_gpop:NN \seq_gpop_left:NN { T , F , TF } +\prg_new_eq_conditional:NNn \seq_gpop:cN \seq_gpop_left:cN { T , F , TF } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Viewing sequences} +% +% \begin{macro}{\seq_show:N, \seq_show:c, \seq_log:N, \seq_log:c, \@@_show:NN} +% \begin{macro}[rEXP]{\@@_show_validate:nn} +% \UnitTested +% Apply the general \cs{__kernel_chk_tl_type:NnnT}. +% \begin{macrocode} +\cs_new_protected:Npn \seq_show:N { \@@_show:NN \msg_show:nneeee } +\cs_generate_variant:Nn \seq_show:N { c } +\cs_new_protected:Npn \seq_log:N { \@@_show:NN \msg_log:nneeee } +\cs_generate_variant:Nn \seq_log:N { c } +\cs_new_protected:Npn \@@_show:NN #1#2 + { + \__kernel_chk_tl_type:NnnT #2 { seq } + { + \s_@@ + \exp_after:wN \use_i:nn \exp_after:wN \@@_show_validate:nn #2 + \q_recursion_tail \q_recursion_tail \q_recursion_stop + } + { + #1 { seq } { show } + { \token_to_str:N #2 } + { \seq_map_function:NN #2 \msg_show_item:n } + { } { } + } + } +\cs_new:Npn \@@_show_validate:nn #1#2 + { + \quark_if_recursion_tail_stop:n {#2} + \@@_wrap_item:n {#2} + \@@_show_validate:nn + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Scratch sequences} +% +% \begin{variable}{\l_tmpa_seq, \l_tmpb_seq, \g_tmpa_seq, \g_tmpb_seq} +% Temporary comma list variables. +% \begin{macrocode} +\seq_new:N \l_tmpa_seq +\seq_new:N \l_tmpb_seq +\seq_new:N \g_tmpa_seq +\seq_new:N \g_tmpb_seq +% \end{macrocode} +% \end{variable} +% +% \begin{macrocode} +%</package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex |