3

In LaTeX3, we may write these lines:

\cs_new_nopar:Nn \module_main:nn #1#2
  {
    \clist_map_function:NN \g_module_some_clist \module_mapping:n
  }  
\cs_new_nopar:Nn \module_mapping:n #1
  {
    % #1 is the clist item
  }

Now I also want to pass two arguments of \module_main:nn to \module_mapping. Are there any simple way to do this, except for defining two new functions similar to \clist_map_function:NN and \__clist_map_function:Nw?

To get full expansion I could not use \clist_map_inline.

Z.H.
  • 3,346
  • 21
  • 39

1 Answers1

4

The standard mapping functions are meant for the simple case where you want to pass the mapped tokens to some pre-defined auxiliary. In a non-expansion context you can define the auxiliary dynamically to include other data

\cs_new_protected:Npn \module_main:N #1
  {
    \cs_set_protected:Npn \__module_aux:n ##1
      {
        % Code using #1
      }
    \clist_map_function:NN \l__module_clist \__module_aux:n
  }

or as you note use \clist_map_inline:Nn. The first approach can be used where the wrapper does not need to be expandable but the mapping does, while the second cannot.

For fully-expandable mappings the expectation is people will use the generic tools for defining recursions: it's not possible to predict every use case. For example, we might have

\cs_new:Npn \module_main:N #1
  {
    \exp_after:wN \__module_loop:Nw \exp_after:wN #1
      \l__module_clist , \q_recursion_tail , \q_recursion_stop
  }
\cs_new:Npn \__module_loop:Nw #1#2 ,
  {
    \quark_if_recursion_tail_stop:n {#2}
    % Code using #1 and #2
    \__module_loop:Nw #1 % Loop passing #1 from \module_main:N 
  }

Passing lots of arguments like this can get a be tricky and might require an auxiliary to do a 'shuffle' of the arguments, e.g.

\cs_new_nopar:Npn \module_main:Nnn 
  {
    \__module_aux:VNnn \l__module_clist
  }
\cs_new:Npn \__module_aux:nNnn #1#2#3#4
  {
      \__module_loop:Nnnw #2 {#3} {#4}
        #1 , \q_recursion_tail , \q_recursion_stop
  }
\cs_generate_variant:Nn \__module_aux:nNnn { V }
\cs_new:Npn \__module_loop:Nnnw #1#2#3#4 ,
  {
    \quark_if_recursion_tail_stop:n {#4}
    % Code using #1 to #4
    \__module_loop:Nnnw #1#2#3 % Loop passing args from \module_main:Nnn
  }

For cases where you want to do a test on the input and only use the other arguments in one case you leave the latter in the input stream and do something like

\cs_new_nopar:Npn \module_main:Nnn 
  {
    \exp_after:wN \__module_loop:wNnn
        \l__module_clist , \q_recursion_tail , \q_recursion_stop
  }
\cs_new:Npn \__module_loop:wNnn #1 ,
  {
    \quark_if_recursion_tail_stop_do:nn {#1} ( \use_none:nnn }
    \str_if_eq:nnT {#1} { fixed-string }
      {
        \use_i_delimit_by_q_recursion_stop:nw { \__module_aux:nNnn {#1} }
      }
    \__module_loop:wNnn
  }
\cs_new:Npn \__module_aux:nNnn #1#2#3#4
  {
    % Do stuff
  }

where \use_i_delimit_by_q_recursion_stop:nw lets us break out of the loop and insert the auxiliary and the hit, while \use_none:nnn cleans up if there is no hit.

More generally, \use_none_delimit_by_q_recursion_stop:w/\use_i_delimit_by_q_recursion_stop:nw can be used to terminate a generic recursion prematurely irrespective of how many arguments are passed, so we might modify one of the above to

\cs_new_nopar:Npn \module_main:Nnn 
  {
    \__module_aux:VNnn \l__module_clist
  }
\cs_new:Npn \__module_aux:nNnn #1#2#3#4
  {
      \__module_loop:Nnnw #2 {#3} {#4}
        #1 , \q_recursion_tail , \q_recursion_stop
  }
\cs_generate_variant:Nn \__module_aux:nNnn { V }
\cs_new:Npn \__module_loop:Nnnw #1#2#3#4 ,
  {
    \quark_if_recursion_tail_stop:n {#4}
    \test:T % Terminate the loop early
      {
        \use_none_delimit_by_q_recursion_stop:w
      }
    \__module_loop:Nnnw #1#2#3 % Loop passing args from \module_main:Nnn
  }
Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
  • The same point applies to generic mappings to token lists, where multi-token relationships may be present in the data. – Joseph Wright Feb 13 '15 at 07:38
  • Is it a proper solution to this problem to create two new functions similar to \clist_map_function:NN and \__clist_map_function:Nw(Since I need \clist_map_break:)? – Z.H. Feb 13 '15 at 07:55
  • @Z.H. Please see the edit: the 'built in' functions are somewhat optimised but in general we can use the standard interfaces provided by expl3 to create a range of recursion behaviours. – Joseph Wright Feb 13 '15 at 08:40
  • When reading the document for \quark_if_recursion_tail_stop_do function, I found it says this is part of using \use_none_delimit_by_q_recursion_stop:w. Some mistakes in it? – Z.H. Feb 13 '15 at 10:58
  • @Z.H. Thanks. Sorry I came across this much later. The sentence was technically correct but confusing. I've just reworded the documentation. – Bruno Le Floch Feb 11 '17 at 03:03