5

How can I recursively process a comma-separated list in expl3? How to make a recursive Latex macro with comma separated argument list looks like a (more general) duplicate and offers no fewer than two expl3 answers. However, I'm obviously an idiot because I still can't figure out how to do it. I understand that expansion always confuses me, but I don't understand why none of the many variants I've played with have worked - or even given me errors. (OK, I do more-or-less understand why they don't give me errors, given that they don't work.)

I've also tried with sequences. Unsurprisingly, I couldn't get those to work either. I tried using an aux function along (I thought) the lines shown in Scott H.'s answer. However, I apparently can't discern the salient features of that solution well enough to adapt it here. I didn't understand egreg's answer well enough to try anything.

\documentclass[border=10pt]{standalone}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\cs_new_protected_nopar:Nn \subsets_construct_subset:nn
{
  \clist_set:Nn \l_tmpa_clist { #1 }
  \int_zero:N \l_tmpa_int
  \tl_clear:N \l_tmpa_tl
  \clist_map_inline:Nn \l_tmpa_clist
  {
    \int_incr:N \l_tmpa_int
    \int_compare:nF { \l_tmpa_int = #2 }{
      \int_compare:nTF { #2 = 1 }{
        \int_compare:nF { \l_tmpa_int = 2 } { \tl_put_right:Nn \l_tmpa_tl { , } }
      }{
        \int_compare:nF { \l_tmpa_int = 1 } { \tl_put_right:Nn \l_tmpa_tl { , } }
      }
      \tl_put_right:Nn \l_tmpa_tl { ##1 }
    }
  }
  \tl_use:N \l_tmpa_tl
}
\cs_generate_variant:Nn \clist_set:Nn { Nx }
\subsets_construct_subset:nn { 2, 3, 4, 5 } { 2 } ~ : ~
\subsets_construct_subset:nn { 2, 4, 5 } { 1 } ~ : ~
\subsets_construct_subset:nn { \subsets_construct_subset:nn { 2, 3, 4, 5 } { 2 } } { 1 } ??
\ExplSyntaxOff
\end{document}

recursive processing & expansion

Sometimes, expansion seems more difficult to deal with in expl3 than it did before the revolution!

EDIT I wanted to use this in an answer to The way to list subsets of the set {1, 2, 3, 4, 5} with forest or tree package. However, my answer there actually does it differently. I rewrote the Forest code in order to avoid the need for applying the expl3 function recursively. I wanted to avoid needing to do that, partly because a recursive approach seemed to reflect what was needed (or what I thought was needed) and partly because adding cycles to a Forest tree makes things very slow, very quickly. Also, I thought I must be missing some simple and obvious solution, as what I wanted seemed a pretty mundane sort of thing to need to do. So I wanted, as each node was added, to construct its content based on its parent's content and its n value. However, to do this, its necessary to repeatedly apply the expl3 function as the tree grows. My current solution just adds the nodes and populates them only when the entire structure is in place (using an expl3 function very similar to the one Christian suggests below, as it happens). But this seems a less transparent approach ....

  • Would you please add what's the expected output from the last call? Anyway, passing \subsets_construct_subset:nn { 2, 3, 4, 5 } { 2 } as the second argument to \clist_set:Nn will not build a clist and only one item will be seen; since \subsets_construct_subset:nn doesn't work by expansion alone, I can't see what you're after. – egreg Aug 6 '17 at 8:00
  • 1
    @egreg: The output should be 4,5 I think. The first strips the 3 from the 2,3,4,5 list, leaving 2,4,5 and this should be feeded again, stripping the first element in the list, so 4,5 remains. But I agree that this not expandable and the \clist_set:Nn will not generate a \clist – user31729 Aug 6 '17 at 8:23
  • @egreg 4,5. I realise it doesn't work. But I don't know how to make it work. How can I get something which is expanded to the list produced by the first step, which I can feed back in for the second step? – cfr Aug 6 '17 at 12:55
  • @egreg Because expansion just expands the tokens? I already said I don't understand it! – cfr Aug 6 '17 at 12:56
1

I can offer a recursion based on what the first token in the first argument is; if it is \subsets_construct_subset:nn, the function is executed and the result passed as argument to the same function.

I see several cases in which this can fail, though. Anyway, this is obviously not a problem of expansion: \subsets_construct_subset:nn doesn't work by pure expansion, so you cannot hope some f expansion magic can work (and x expansion definitely won't, because it doesn't work by pure expansion).

\documentclass{article}
\usepackage{expl3}
\begin{document}

\ExplSyntaxOn
\cs_new_protected:Nn \subsets_construct_subset:nn
 {
  \clist_set:Nn \l_tmpa_clist { #1 }
  \exp_last_unbraced:Nx \token_if_eq_meaning:NNTF { \tl_head:n { #1 } } \subsets_construct_subset:nn
   {
    \cs_set_eq:NN \__subsets_use:n \use_none:n
    #1
    \cs_set_eq:NN \__subsets_use:n \use:n
    \subsets_construct_subset:xn { \tl_use:N \l_tmpa_tl } { #2 }
   }
   {
    \__subsets_construct_subset:nn { #1 } { #2 }
    \__subsets_use:n { \tl_use:N \l_tmpa_tl }
   }
 }
\cs_set_eq:NN \__subsets_use:n \use:n % initialize
\cs_new_protected:Nn \__subsets_construct_subset:nn
 {
  \int_zero:N \l_tmpa_int
  \tl_clear:N \l_tmpa_tl
  \clist_map_inline:Nn \l_tmpa_clist
  {
    \int_incr:N \l_tmpa_int
    \int_compare:nF { \l_tmpa_int = #2 }{
      \int_compare:nTF { #2 = 1 }{
        \int_compare:nF { \l_tmpa_int = 2 } { \tl_put_right:Nn \l_tmpa_tl { , } }
      }{
        \int_compare:nF { \l_tmpa_int = 1 } { \tl_put_right:Nn \l_tmpa_tl { , } }
      }
      \tl_put_right:Nn \l_tmpa_tl { ##1 }
    }
  }
 }
\cs_generate_variant:Nn \subsets_construct_subset:nn { x }

\subsets_construct_subset:nn { 2, 3, 4, 5 } { 2 } \par
\subsets_construct_subset:nn { 2, 4, 5 } { 1 } \par
\subsets_construct_subset:nn { \subsets_construct_subset:nn { 2, 3, 4, 5 } { 2 } } { 1 } 
\ExplSyntaxOff

\end{document}

enter image description here

  • Thanks very much. I've actually rewritten the Forest code to use a non-recursive approach (similar to Christian's answer, as it happens). But it would be nice not to have to do it that way. What does it mean for something to work or not work 'by pure expansion'? – cfr Aug 6 '17 at 20:45
  • It seems weird to me that there's no simple way to do this. (That is, this is very impressive, of course, but it seems quite a fragile and roundabout way to have to do something which seemed to me fairly mundane.) – cfr Aug 6 '17 at 20:51
1

A solution without recursion, by 'double' - looping over the second argument which is a comma separated list as well:

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn


\clist_new:N \l_cfr_subset_clist

\cs_new:Nn \subsets_construct_subset:nn
{
  \group_begin:
  \clist_set:Nn \l_cfr_subset_clist {#1}
  \clist_set:Nn \l_tmpb_clist { #2 } 
  \clist_map_inline:Nn \l_tmpb_clist {% Loop over the indices
    \int_zero:N \l_tmpa_int
    \clist_set_eq:NN \l_tmpa_clist \l_cfr_subset_clist
    \clist_clear:N \l_cfr_subset_clist
    \clist_map_inline:Nn \l_tmpa_clist
    {% Loop over the current temp list
      \int_incr:N \l_tmpa_int
      \int_compare:nF { \l_tmpa_int = ##1 }{
        \clist_put_right:Nn \l_cfr_subset_clist {####1}
      }
    }
  }
  \clist_use:Nn \l_cfr_subset_clist {,}
 \group_end:
}





\subsets_construct_subset:nn { 2, 3, 4, 5 } { 2 } ~ : ~
\par
\subsets_construct_subset:nn { 2, 4, 5 } { 1 } ~ : ~
\par
\subsets_construct_subset:nn { 2, 3, 4, 5 } { 2, 1 } ~ : ~
\par
\subsets_construct_subset:nn { 2, 3, 4, 5 } { 2, 1,1 } ~ : ~
\ExplSyntaxOff
\end{document}

enter image description here

  • Yes, thanks. This is pretty similar to what I've got. But it means rewriting the Forest code, which I'd hoped to avoid. – cfr Aug 6 '17 at 20:38
  • @cfr: It's not recursive, I know. – user31729 Aug 6 '17 at 20:39
  • tex.stackexchange.com/questions/384906/… is my rewrite. But it means more Forest cycling .... ;( – cfr Aug 6 '17 at 20:49

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.