3

For my own good reasons I am loading \param_seq with an array of parameters, then want to transfer the content of \param_seq to \aux_seq by popping \param_seq, storing the popped item temporarily in \aux_tl, then pushing \aux_tl on to \aux_seq. After two cycles of popping and pushing I can already see things go awry; for some reason \aux_seq has as many copies of the last popped item as there were cycles, and \param_seq now has not lost the popped items but had them replaced with copies of the last popped. Not the kind of shenanigans one would expect from a pair of stacks. What am I doing wrong? I should add that in actual practice I am doing different things to different parameters, hence the case clause.

\documentclass{minimal}
%-----------------------------
\usepackage{xparse}
\usepackage{amsmath}
%=============================
\ExplSyntaxOn
\tl_new:N \output_tl
\tl_new:N \aux_tl
\seq_new:N \params_seq
\seq_new:N \aux_seq
\int_new:N \index_i
%-----------------------------
\NewDocumentCommand\myFunction{O{,} m}{\myFunction_build:nn {#1}{#2}}
  \cs_new:Npn \myFunction_build:nn #1 #2 
    {
    \tl_clear:N \output_tl 
    \seq_clear:N \params_seq 
    \seq_set_split:Nnn \params_seq { #1 } { #2 }
    \seq_pop_left:NN \params_seq \aux_tl
    \tl_use:N \aux_tl \\
    \seq_push:Nn \aux_seq \aux_tl
    \seq_pop_left:NN \params_seq \aux_tl
    \tl_use:N \aux_tl \\
    \seq_push:Nn \aux_seq \aux_tl 
    \int_set:Nn \index_i { 0 }
    \seq_map_inline:Nn \aux_seq 
      {
      \int_incr:N \index_i
      \int_case:nnTF {\index_i}
        {
                    {1} {\tl_put_right:Nn \output_tl {##1,\c_space_tl}}
                    {2} {\tl_put_right:Nn \output_tl {##1,\c_space_tl}}
                    {3} {\tl_put_right:Nn \output_tl {##1,\c_space_tl}}
                    {4} {\tl_put_right:Nn \output_tl {##1,\c_space_tl}}
        }
        {}
        {}
      }
    \text{contents\, of\, aux\, sequence\,after\,two\,pushes:\, } \tl_use:N \output_tl \\
    \int_set:Nn \index_i { 0 }
    \seq_map_inline:Nn \params_seq  
      {
      \int_incr:N \index_i
      \int_case:nnTF {\index_i}
        {
                    {1} {\tl_put_right:Nn \output_tl {##1,\c_space_tl}}
                    {2} {\tl_put_right:Nn \output_tl {##1,\c_space_tl}}
                    {3} {\tl_put_right:Nn \output_tl {##1,\c_space_tl}}
                    {4} {\tl_put_right:Nn \output_tl {##1,\c_space_tl}}
        }
        {}
        {}
      }
    \text{contents\, of\, params\, seq\,after\,two\,pops:\, } \tl_use:N \output_tl \\
    }
\ExplSyntaxOff
%=======================================
\begin{document}
\noindent
\textbf{Function call:} \verb+\myFunction{1,2,3,4,5}+\\
\textbf{Disappointing result:} \\
\myFunction{1,2,3,4,5}\\
\end{document} 
  • 3
    as a side note: please follow the naming scheme! It should be \myFunction_build -> \neuwirth_myFunction_build, \output_tl -> \l_neuwirth_output_tl, \params_seq -> \l_neuwirth_params_seq, \index_i -> \l_index_int etc. You can choose other prefix than neuwirth, but you have to use a prefix! – yo' Aug 31 '15 at 10:34
  • @yo It may seem I misname my variables out of spite, but this is not the case. Apart from section 3 in the expl3 document, what else do I need to study to get the naming right? Looking forward to your input. Thanks – Reinhard Neuwirth Aug 31 '15 at 11:59
  • 1
    I think it's covered there pretty well, especially in §3.2. It's just not explained there that for you (or me or any other L3 user), module is something that is specific to their works. Also, module names intended to be used by the general public should be registered with the team, to avoid name clashes. – yo' Aug 31 '15 at 12:16
  • @yo I have been bemoaning the fact that the l3 documents talk about local and global in a few places, but nowhere seem to say how to set the scope for a variable. Is the mechanism in the naming? For example, does the l_ in \l_neuwirth_output_tl make this a local tokenlist? – Reinhard Neuwirth Aug 31 '15 at 12:29
  • 1
    It's just a naming convention, l for local and g for global (and c for constant). I means: "I will never use \tl_gset on it; I wil stick to \tl_set." But nothing (technically speaking) stops you from doing \tl_gset:Nn \l_no_tl_at_the_end_of_the_name:::____ {foo}. – yo' Aug 31 '15 at 12:32
4

Apart from the bad naming scheme of your variables, the problem is in the misuse of \seq_push:Nn that should be instead

\seq_push:NV \l_neuwirth_aux_seq \l_neuwirth_aux_tl

and

\seq_push:NV \l_neuwirth_aux_seq \l_neuwirth_aux_tl

because you want to push the contents of the variable, not the variable. I used the “correct” variable names according to the declarations

\tl_new:N \l_neuwirth_output_tl
\tl_new:N \l_neuwirth_aux_tl
\seq_new:N \l_neuwirth_params_seq
\seq_new:N \l_neuwirth_aux_seq
\int_new:N \l_neuwirth_index_int

Full code. Note also

  1. the naming of the internal function
  2. protected
  3. \int_case:nn instead of \int_case:nnTF with empty arguments
  4. ~ for a space in output
  5. \seq_pop:NN is to be preferred in case a sequence is used as a stack: the fact that the top is the “left side” should be of no concern.
\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn
\tl_new:N \l_neuwirth_output_tl
\tl_new:N \l_neuwirth_aux_tl
\seq_new:N \l_neuwirth_params_seq
\seq_new:N \l_neuwirth_aux_seq
\int_new:N \l_neuwirth_index_int

\NewDocumentCommand\myFunction{O{,} m}
 {
  \neuwirth_myfunction_build:nn {#1}{#2}
 }

\cs_new_protected:Npn \neuwirth_myfunction_build:nn #1 #2 
 {
  \tl_clear:N \l_neuwirth_output_tl 
  \seq_clear:N \l_neuwirth_params_seq 
  \seq_set_split:Nnn \l_neuwirth_params_seq { #1 } { #2 }
  \seq_pop:NN \l_neuwirth_params_seq \l_neuwirth_aux_tl
  \tl_use:N \l_neuwirth_aux_tl \\
  \seq_push:NV \l_neuwirth_aux_seq \l_neuwirth_aux_tl
  \seq_pop:NN \l_neuwirth_params_seq \l_neuwirth_aux_tl
  \tl_use:N \l_neuwirth_aux_tl \\
  \seq_push:NV \l_neuwirth_aux_seq \l_neuwirth_aux_tl 
  \int_set:Nn \l_neuwirth_index_int { 0 }
  \seq_map_inline:Nn \l_neuwirth_aux_seq 
   {
    \int_incr:N \l_neuwirth_index_int
    \int_case:nn {\l_neuwirth_index_int}
     {
      {1} {\tl_put_right:Nn \l_neuwirth_output_tl {##1,~}}
      {2} {\tl_put_right:Nn \l_neuwirth_output_tl {##1,~}}
      {3} {\tl_put_right:Nn \l_neuwirth_output_tl {##1,~}}
      {4} {\tl_put_right:Nn \l_neuwirth_output_tl {##1,~}}
     }
   }
  \mbox{contents~of~aux~sequence~after~two~pushes:~} \tl_use:N \l_neuwirth_output_tl \\
  \int_set:Nn \l_neuwirth_index_int { 0 }
  \seq_map_inline:Nn \l_neuwirth_params_seq  
   {
    \int_incr:N \l_neuwirth_index_int
    \int_case:nn {\l_neuwirth_index_int}
     {
      {1} {\tl_put_right:Nn \l_neuwirth_output_tl {##1,~}}
      {2} {\tl_put_right:Nn \l_neuwirth_output_tl {##1,~}}
      {3} {\tl_put_right:Nn \l_neuwirth_output_tl {##1,~}}
      {4} {\tl_put_right:Nn \l_neuwirth_output_tl {##1,~}}
     }
   }
  \mbox{contents~of~params~seq~after~two~pops:~} \tl_use:N \l_neuwirth_output_tl \\
 }
\ExplSyntaxOff

\begin{document}

\noindent
\textbf{Function call:} \verb+\myFunction{1,2,3,4,5}+\\
\textbf{Disappointing result:} \\
\myFunction{1,2,3,4,5}

\end{document} 

enter image description here

Contrary to what's said, the result should not be disappointing any more.

Actually, you're using the wrong method for displaying the sequences; here's some code that does the right thing:

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn
\tl_new:N \l_neuwirth_aux_tl
\seq_new:N \l_neuwirth_params_seq
\seq_new:N \l_neuwirth_aux_seq

\NewDocumentCommand\myFunction{O{,} m}
 {
  \neuwirth_myfunction_build:nn {#1}{#2}
 }

\cs_new_protected:Npn \neuwirth_myfunction_build:nn #1 #2 
 {
  \seq_set_split:Nnn \l_neuwirth_params_seq { #1 } { #2 }
  \seq_pop:NN \l_neuwirth_params_seq \l_neuwirth_aux_tl
  \tl_use:N \l_neuwirth_aux_tl \\
  \seq_push:NV \l_neuwirth_aux_seq \l_neuwirth_aux_tl
  \seq_pop:NN \l_neuwirth_params_seq \l_neuwirth_aux_tl
  \tl_use:N \l_neuwirth_aux_tl \\
  \seq_push:NV \l_neuwirth_aux_seq \l_neuwirth_aux_tl 
  \mbox{contents~of~aux~sequence~after~two~pushes:~} \seq_use:Nn \l_neuwirth_aux_seq {,~}\\
  \mbox{contents~of~params~seq~after~two~pops:~} \seq_use:Nn \l_neuwirth_params_seq {,~}\\
 }
\ExplSyntaxOff

\begin{document}

\noindent
\textbf{Function call:} \verb+\myFunction{1,2,3,4,5}+\\
\textbf{Expected result:} \\
\myFunction{1,2,3,4,5}

\end{document} 

enter image description here

| improve this answer | |
  • On the topic of \int_case:nn vs. \int_case:nnTF: in actual practice I do want to do things if there are no parameters passed at all. Although wishing to avoid cluttering my MWE's with detail that has no relevance to the point I am trying to make, I will in future have to add a comment here or there to make things clearer. – Reinhard Neuwirth Aug 31 '15 at 12:07
  • One more thing still puzzles me: why are the items still in \l_neuwirth_params_seq despite being popped? If I pop them all I end up not with an empty sequence but one that contains the popped items in reverse. – Reinhard Neuwirth Aug 31 '15 at 12:37
  • @ReinhardNeuwirth You're popping and pushing them back. I'm not sure what you want to achieve with your \seq_map_inline:Nn, though. – egreg Aug 31 '15 at 12:51
  • (1) I am popping them from one (params) and pushing them onto the other (aux). And the manual says for \seq_pop_left:NN that the item is removed from the sequence once popped, as indeed one would expect from a stack. (2) the inline operations are there to see what is in those sequences, just for the purpose of this exercise. I am about to turn this little gadget into something really useful. – Reinhard Neuwirth Aug 31 '15 at 13:12
  • 1
    @ReinhardNeuwirth Your \seq_map_inline:Nn method doesn't display the sequences; I added a good method. – egreg Aug 31 '15 at 13:36

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.