5

I trust the code below is commented sufficiently well to obviate further explanations. The macro defined in the MWE does not produce the expected result. My reading of \seq_map_inline:Nn \params_seq in the interface3 document is that ##1 presents tokens stored in \params_seq one at a time, and that \tl_clear:N \output_tl will clear the tokenlist. The result suggests I misread either one or the other, possibly both.

I will be grateful for any help received.

\documentclass{minimal}
%-----------------------------
%RN 24 August 2015
%   BACKGROUND:
% The purpose of the macro is to provide the template for macros 
% that accept a parameter input stream of arbitrary length 
% (separator optionally definable), and partition it into chunks 
% of optionally defineable length for further processing.   
%   ISSUE:
% I expect the result to be:
%   1 2 3 4 5
%   6 7 8 9 10
%   11 12 13 14 15
%   16 17
% and not:       
%   1 2 3 4 5
%   1 2 3 4 5 6 7 8 9 10
%   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
%   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17        
%-----------------------------
\usepackage{xparse}
%=======================================
\ExplSyntaxOn
%-----------------------------
\tl_new:N \output_tl 
\seq_new:N \params_seq 
\int_new:N \index_i
%-----------------------------
\NewDocumentCommand\myPartitionerTemplate{O{,} m O{3}}{\myPartitioner_build:nn {#1}{#2}{#3}}
\cs_new:Npn \myPartitioner_build:nn #1 #2 #3
  { %begin control sequence
    \tl_clear:N \output_tl 
    \seq_clear:N \params_seq 
    \int_set:Nn \index_i { 0 }
  %load parameter #2 into the _seq array:
  \seq_set_split:Nnn \params_seq { #1 } { #2 }  
    \seq_map_inline:Nn \params_seq  
        {   %begin processing \params_seq inline
            % keep a count of how many parameters have been read:
            \int_incr:N \index_i    
            \tl_put_right:Nn \output_tl {\ ##1 }    
            %   once a partition is full, i.e. paramcount mod   param#3 = 0,
            % "use" it, then clear \output_tl: 
            \if_int_compare:w  \int_mod:nn {\index_i} {#3} = 0
                {
                \tl_use:N \output_tl \\ 
                \tl_clear:N \output_tl
                }       
            \fi:
        }   %end inline processing
        %   "use" final partially full partition, else it's 
        % been "used" already
        \if_int_compare:w  \int_mod:nn {\index_i} {#3} > 0
            \tl_use:N \output_tl \\
        \fi:
  } %end control sequence
\ExplSyntaxOff
%=======================================
\begin{document}
\verb+\myPartitionerTemplate{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17}[5]+\\
\myPartitionerTemplate{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17}[5]
\end{document} 
  • @Manuel Name three. If you have nothing else to contribute, Manuel, I suggest your remain silent. – Reinhard Neuwirth Aug 25 '15 at 21:17
  • @Manuel My apologies for being abrasive. Every time I work with Latex3 I get over-excited, not to say nervous, and this often translates to jumpy and erratic behaviour. – Reinhard Neuwirth Aug 25 '15 at 22:06
6

Your problem is

\if_int_compare:w  \int_mod:nn {\index_i} {#3} = 0
  {
    \tl_use:N \output_tl \\ 
    \tl_clear:N \output_tl
  }
\fi:

Here you are using the lowest-level numerical conditional, \if_int_compare:w (the TeX \ifnum primitive), which does not use brace groups for its argument. Thus the { ... } pair here cause a group to applied to the \tl_clear:N and you therefore do not get the expected reset behaviour.

You could fix by removing the stray group, but I'd favour \int_comapre:nNnTF (or \int_compare:nTF) here, which does use a brace group and which does not need you to watch the fact you've not safely terminated your conditionals. Tidying up the code to use higher-level interfaces where available, correctly-named variables, etc., leads to

\documentclass{article}
%-----------------------------
%RN 24 August 2015
%   BACKGROUND:
% The purpose of the macro is to provide the template for macros 
% that accept a parameter input stream of arbitrary length 
% (separator optionally definable), and partition it into chunks 
% of optionally definable length for further processing.   
%   ISSUE:
% I expect the result to be:
%   1 2 3 4 5
%   6 7 8 9 10
%   11 12 13 14 15
%   16 17

%-----------------------------
\usepackage{xparse}
%=======================================
\ExplSyntaxOn
%-----------------------------
\tl_new:N \l__myPartitioner_output_tl 
\seq_new:N \l__myPartitioner_params_seq 
\int_new:N \l__myPartitioner_index_int
%-----------------------------
\NewDocumentCommand\myPartitionerTemplate{O{,} m O{3}}
  {\myPartitioner_build:nnn {#1}{#2}{#3}}
\cs_new_protected:Npn \myPartitioner_build:nnn #1 #2 #3
  { %begin control sequence
    \tl_clear:N \l__myPartitioner_output_tl 
    \seq_clear:N \l__myPartitioner_params_seq 
    \int_zero:N \l__myPartitioner_index_int
  %load parameter #2 into the _seq array:
    \seq_set_split:Nnn \l__myPartitioner_params_seq {#1} {#2}  
    \seq_map_inline:Nn \l__myPartitioner_params_seq  
      { %begin processing \l__myPartitioner_params_seq inline
        % keep a count of how many parameters have been read:
        \int_incr:N \l__myPartitioner_index_int    
        \tl_put_right:Nn \l__myPartitioner_output_tl
           { \c_space_tl ##1 }    
        %   once a partition is full, i.e. paramcount mod   param#3 = 0,
        % "use" it, then clear \l__myPartitioner_output_tl: 
        \int_compare:nNnT
          { \int_mod:nn { \l__myPartitioner_index_int } {#3} } = \c_zero
          {
            \tl_use:N \l__myPartitioner_output_tl \\ 
            \tl_clear:N \l__myPartitioner_output_tl      
          }
      }   % end inline processing
      % "use" final partially full partition, else it's 
      % been "used" already
    \int_compare:nNnT
       { \int_mod:nn {\l__myPartitioner_index_int } {#3} } > \c_zero
       { \tl_use:N \l__myPartitioner_output_tl }
  } %end control sequence
\ExplSyntaxOff
%=======================================
\begin{document}
\verb+\myPartitionerTemplate{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17}[5]+\\
\myPartitionerTemplate{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17}[5]
\end{document} 
  • Now that you pointed me in the right direction it is clear I should have had a more careful look at the possibilities offered to compare integers. I did have an uneasy feeling about that :w at the end of \if_int_compare:w, had quick read in the manual, didn't grasp the half of it, and went and used it anyway. Your modified code works, I mark it down as the answer. I have taken your recommendations on board and feel I learnt something from the experience. Thanks. – Reinhard Neuwirth Aug 25 '15 at 21:39
  • 1
    There's one thing missing, :nn #1 #2 #3 should be :nnn #1 #2 #3. – Manuel Aug 25 '15 at 21:57
  • @Joseph I notice you replaced my naïve use of 0 with \c_zero and \ with \c_space and wonder if this touches on some fundamental misunderstanding in my head on how to use constants in latex3. Can you point me in the right direction on this point? – Reinhard Neuwirth Aug 25 '15 at 21:58
  • @Manuel Well spotted: fixed – Joseph Wright Aug 26 '15 at 5:54
  • 1
    @ReinhardNeuwirth Pre-defined constants have a (slight) performance gain over literal numbers, so using \c_zero is very slightly faster than using 0. They are also 'self-terminating', important if you use \if_int_compare:w (less so for \int_compare:nNnTF, which includes the termination internally). On the space, \c_space_tl expands to a space, so if we did for example an x-type expansion you'd end up with a literal space, whereas \ is a non-expandable primitive (outside special contexts). As such, the former is probably what you wanted. – Joseph Wright Aug 26 '15 at 5:57

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.