7

Obviously, the \seq_set_split:Nnn function allows the delimiter to be used explicitly; for example the statement \seq_set_split:Nnn \Args_seq {,} {x,y,z} will put x, y and z into the first three locations in \Args_seq.

Or, wrapping this functionality into a \NewDocumentCommand the delimiter can be passed as a parameter, see for example \myMacroA in the MWE below, where in \seq_set_split:Nnn \Args_seq #2 {#1} parameter #1 is split into \Args_seq using the delimiter specified in parameter #2.

However, in one of my gizmos I wanted to pop delimiters off a stack (implemented by an L3seq of course), store them in a _tl type variable and then use that variable in the \seq_set_split function. All attempts failed miserably; the \myMacroB_Main control sequence below illustrates the point. What type of variable does the delimiter require?

Much obliged, as usual.

\documentclass{minimal}
%   --------------------
\usepackage[check-declarations]{expl3}
\usepackage{xparse}
\usepackage{amsmath}
%   --------------------
\ExplSyntaxOn
%   VARIABLE DECLARATIONS:
\seq_new:N \Args_seq
\int_new:N \ItemCount_int
\int_new:N \index_i
\tl_new:N \aux_tl
\tl_new:N \sep_tl
%   --------------------
\NewDocumentCommand\myMacroA{O{}O{,}}{\myMacroA_Main:nn {#1}{#2}}
\cs_new:Npn \myMacroA_Main:nn #1 #2
  {
    \seq_clear:N \Args_seq
    \seq_set_split:Nnn \Args_seq #2 {#1}
    \myShowArgs{\Args_seq}
  }
\NewDocumentCommand\myMacroB{O{}O{,}}{\myMacroB_Main:nn {#1}{#2}}
\cs_new:Npn \myMacroB_Main:nn #1 #2
  {
    \seq_clear:N \Args_seq
    \tl_set:Nn \sep_tl {#2}
    \seq_set_split:Nnn \Args_seq \sep_tl {#1}  %does not accept the delimiter as a _tl variable.
    \myShowArgs{\Args_seq}
  }
\cs_new:Npn \myShowArgs #1
  {
    \tl_clear:N \aux_tl    
    \int_set:Nn \ItemCount_int {\seq_count:N #1}
    \int_set:Nn \index_i {1}
    \int_do_until:nNnn \index_i > \ItemCount_int
      {
        \tl_set:Nx \aux_tl {\seq_item:Nn #1 {\index_i}}
        \tl_use:N \aux_tl \text{~}
        \int_incr:N \index_i
      }
  }
\ExplSyntaxOff
\begin{document}
1. \verb+\myMacroA[a,b,c,1,2,3]+\\
\myMacroA[a,b,c,1,2,3]

2. \verb+\myMacroA[1;2;3;a;b;c][;]+\\
\myMacroA[1;2;3;a;b;c][;]

3. \verb+\myMacroB[a,b,c,1,2,3]+\\
\myMacroB[a,b,c,1,2,3]

4. \verb+\myMacroB[1;2;3;a;b;c][;]+\\
\myMacroB[1;2;3;a;b;c][;]
\end{document} 
3
  • Not part of the question, but there are a few things that should be 'tidied up' here for expl3 'compliance': do you details? BTW, is there a reason that \myMacroA/B don't take the list as a mandatory argument and don't have the 'truly' optional argument first?
    – Joseph Wright
    Sep 1, 2016 at 10:51
  • @JosephWright the list could be a mandatory variable of course in which case it makes sense to put the optional delimiter out front. For better or worse, whenever I can I like to keep all arguments optional and have my macros do something completely standard or typical if written down without any arguments at all. In this case I might have specified something like {O{a,b,c}O{,}}. Sep 1, 2016 at 13:15
  • Can you quickly explain what the purpose is for the two different macros \myMacroA and \myMacroB? Both split a string into its parts, don't they?
    – normanius
    Dec 17, 2022 at 9:28

2 Answers 2

7

You need a variant of \seq_set_split:Nnn that uses the value of \sep_tl, not the literal \sep_tl -- which is not the delimiter there.

With other words: \seq_set_split:NVn, which does not exist yet but can be generated with

\cs_generate_variant:Nn \seq_set_split:Nnn {NVn}

This \seq_set_split:NVn will now use the Value of \sep_tl (or any other variable that can be used with an V type.

Here is the shortened code, clearified, shortened, using the expl3 naming syntax, but first some comments:

  • \seq_clear:N isn't necessary if \seq_set_split is used -- that command clears the sequence anyway
  • \seq_use:Nn is much easier than glueing things together with some \tl_map_inline etc. loop.
  • There are two scratch variables ('registers') for almost any expl3 datatype, that can be used for storing intermediate data, such as \l_tmpa_tl and \l_tmpb_tl or \l_tmpa_seq etc.

\documentclass{article}% Don't use minimal!
%   --------------------
\usepackage[check-declarations]{expl3}
\usepackage{xparse}
\usepackage{amsmath}
%   --------------------
\ExplSyntaxOn
%   VARIABLE DECLARATIONS:
\seq_new:N \l_neuwirth_Args_seq
\tl_new:N \l_neuwirth_sep_tl
%   --------------------

\cs_generate_variant:Nn \seq_set_split:Nnn {NVn}

\NewDocumentCommand\myMacroA{O{}O{,}}{\neuwirth_myMacroA:nn {#1}{#2}}
\NewDocumentCommand\myMacroB{O{}O{,}}{\neuwirth_myMacroB:nn {#1}{#2}}


\cs_new:Npn \neuwirth_myMacroA:nn #1 #2
  {
    %\seq_clear:N \l_neuwirth_Args_seq
    \seq_set_split:Nnn \l_neuwirth_Args_seq #2 {#1}
    \myShowArgs{\l_neuwirth_Args_seq}
  }

\cs_new:Npn \neuwirth_myMacroB:nn #1 #2
  {
    %\seq_clear:N \l_neuwirth_Args_seq% Not necessary since \seq_set_split clears the seq-variable
    \tl_set:Nn \l_neuwirth_sep_tl {#2}
    \seq_set_split:NVn \l_neuwirth_Args_seq \l_neuwirth_sep_tl {#1}
    \neuwirth_myShowArgs{\l_neuwirth_Args_seq}
  }

\cs_new:Npn \neuwirth_myShowArgs #1 {%
  \seq_use:Nn #1 {\c_space_token}
}

\ExplSyntaxOff
\begin{document}
1. \verb+\myMacroA[a,b,c,1,2,3]+\\
\myMacroA[a,b,c,1,2,3]

2. \verb+\myMacroA[1;2;3;a;b;c][;]+\\
\myMacroA[1;2;3;a;b;c][;]

3. \verb+\myMacroB[a,b,c,1,2,3]+\\
\myMacroB[a,b,c,1,2,3]

4. \verb+\myMacroB[1;2;3;a;b;c][;]+\\
\myMacroB[1;2;3;a;b;c][;]
\end{document} 

enter image description here

9
  • 1
    I can't help myself but I feel that experienced users shouldn't post bad code. At least, the mistakes shall be pointed out...
    – yo'
    Sep 1, 2016 at 10:57
  • @yo': Solution first, then improvement ;-)
    – user31729
    Sep 1, 2016 at 10:57
  • Much nicer. Just two things: (1) \myMacroA_Main:nn shall be \neuwirth_myMacroA:nn and similar for B. (2) Both shall be \cs_new_protected rather than \cs_new.
    – yo'
    Sep 1, 2016 at 11:07
  • @yo': Sighhhhhhhhhhhhhhhhhhhhhhhhhhhh!
    – user31729
    Sep 1, 2016 at 11:11
  • Nobody has ever said expl3 compliance is easy.
    – yo'
    Sep 1, 2016 at 11:12
9

You have several errors in your code. Comments marked %%% refer either to the code at the left or to the code that follows

\documentclass{article}
%   --------------------
\usepackage[check-declarations]{expl3}
\usepackage{xparse}
\usepackage{amsmath}
%   --------------------
\ExplSyntaxOn
%   VARIABLE DECLARATIONS:
%%% the names are not compliant
\seq_new:N \Args_seq
\int_new:N \ItemCount_int
\int_new:N \index_i
\tl_new:N \aux_tl
\tl_new:N \sep_tl
%   --------------------
\cs_generate_variant:Nn \seq_set_split:Nnn {NVn}
%%% one of the arguments ought to be mandatory
\NewDocumentCommand\myMacroA{O{}O{,}}{\myMacroA_Main:nn {#1}{#2}}
%%% the name is not compliant
\cs_new:Npn \myMacroA_Main:nn #1 #2
  {
    \seq_clear:N \Args_seq
    \seq_set_split:Nnn {\Args_seq} #2 {#1}
    \myShowArgs{\Args_seq}
  }
\NewDocumentCommand\myMacroB{O{}O{,}}{\myMacroB_Main:nn {#1}{#2}}

%%% the name is not compliant; the function should be protected
\cs_new:Npn \myMacroB_Main:nn #1 #2
  {
    \seq_clear:N \Args_seq %%% useless
    \tl_set:Nn \sep_tl {#2}
    \seq_set_split:NVn \Args_seq \sep_tl {#1}  %does not accept the delimiter as a _tl variable.
    \myShowArgs{\Args_seq} %%% the call is not compliant
  }
%%% the name is not compliant and the code below can be shortened
\cs_new:Npn \myShowArgs #1
  {
    \tl_clear:N \aux_tl    
    \int_set:Nn \ItemCount_int {\seq_count:N #1}
    \int_set:Nn \index_i {1}
    \int_do_until:nNnn \index_i > \ItemCount_int
      {
        \tl_set:Nx \aux_tl {\seq_item:Nn #1 {\index_i}}
        \tl_use:N \aux_tl \text{~}
        \int_incr:N \index_i
      }
  }
\ExplSyntaxOff
\begin{document}
1. \verb+\myMacroA[a,b,c,1,2,3]+\\
\myMacroA[a,b,c,1,2,3]

2. \verb+\myMacroA[1;2;3;a;b;c][;]+\\
\myMacroA[1;2;3;a;b;c][;]

3. \verb+\myMacroB[a,b,c,1,2,3]+\\
\myMacroB[a,b,c,1,2,3]

4. \verb+\myMacroB[1;2;3;a;b;c][;]+\\
\myMacroB[1;2;3;a;b;c][;]
\end{document}

Now, let's look at compliant and simplified code. I used rn as your personal prefix.

  1. The main argument to \myMacroA and \myMacroB ought to be mandatory

  2. Function where variables are set should be protected.

  3. If an argument to a function is supposed to be a variable, then the argument type should be N

  4. With \seq_use:Nn the code for showing the sequence items can be simplified; if instead you want to do something to each item, use \seq_map_inline:Nn or \seq_map_function:NN

  5. When you're using \seq_set_split:Nnn (or the variant) you don't need to clear the variable beforehand; the same for \tl_set:Nn

\documentclass{article}
%   --------------------
\usepackage[check-declarations]{expl3}
\usepackage{xparse}
\usepackage{amsmath}
%   --------------------
\ExplSyntaxOn
%   VARIABLE DECLARATIONS:
\seq_new:N \l_rn_args_seq
\tl_new:N \l_rn_sep_tl
%   --------------------
\cs_generate_variant:Nn \seq_set_split:Nnn {NVn}
\NewDocumentCommand\myMacroA{O{,}m}
  {
    \rn_MacroA_Main:nn {#1}{#2}
  }
\cs_new_protected:Npn \rn_MacroA_Main:nn #1 #2
  {
    \seq_set_split:Nnn \l_rn_args_seq {#1} {#2}
    \rn_ShowArgs:N \l_rn_args_seq
  }

\NewDocumentCommand\myMacroB{O{,}m}
  {
    \rn_MacroB_Main:nn {#1}{#2}
  }
\cs_new_protected:Npn \rn_MacroB_Main:nn #1 #2
  {
    \tl_set:Nn \l_rn_sep_tl {#1}
    \seq_set_split:NVn \l_rn_args_seq \l_rn_sep_tl {#2}
    \rn_ShowArgs:N \l_rn_args_seq
  }
\cs_new:Npn \rn_ShowArgs:N #1
  {
   \seq_use:Nn #1 { ~ }
  }
\ExplSyntaxOff

\begin{document}
1. \verb+\myMacroA{a,b,c,1,2,3}+\\
\myMacroA{a,b,c,1,2,3}

2. \verb+\myMacroA[;]{1;2;3;a;b;c}+\\
\myMacroA[;]{1;2;3;a;b;c}

3. \verb+\myMacroB{a,b,c,1,2,3}+\\
\myMacroB{a,b,c,1,2,3}

4. \verb+\myMacroB[;]{1;2;3;a;b;c}+\\
\myMacroB[;]{1;2;3;a;b;c}
\end{document}

enter image description here

2
  • I'd be tempted to make the variables internal as well ... otherwise very nice :-)
    – Joseph Wright
    Sep 1, 2016 at 13:10
  • @JosephWright That adds a level of complexity that seemed not relevant for illustrating the main rules.
    – egreg
    Sep 1, 2016 at 13:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.