TransWikia.com

Map arbitrary operation on zipped iteration over two comma-separated lists

TeX - LaTeX Asked on November 1, 2021

I am trying without success to combine these two approaches into something powerful that would allow me to zip-iterate over two lists and do arbitrary cool things:

zip{a,b,c}{1,2,3}{#1-#2 }

would expand to

a-1 b-2 c-3 

And why not

zip[#1/#2][#3::#4]
    {a/A,b/B,c/C}
    {1::I,2::II,3::III}
    {Grand #1 is #2 but grand #3 is #4.\}

would expand to

Grand a is A but grand 1 is I.\
Grand b is B but grand 2 is II.\
Grand c is C but grand 3 is III.\

My best attempt so far was to append this code after @BrunoLeFloch’s solution:

NewDocumentCommand{zip}{mmm}
 {
  cs_set:Npn __egreg_apply_aux:nn ##1 q_stop { #3 }
  clist_map_zip_ii:VVN #1 #2 __egreg_apply_aux:nn
 }

with no luck, and maybe the arcanes of Interface3 find me not pure-hearted enough to unveil their secrets yet :'(

3 Answers

Defining zip in the first way is quite easy:

documentclass{article}
usepackage{xparse}

ExplSyntaxOn

NewDocumentCommand{zip}{mm +m}
 {
  seq_set_from_clist:Nn l__iagolito_zip_a_seq { #1 }
  seq_set_from_clist:Nn l__iagolito_zip_b_seq { #2 }
  cs_set:Nn __iagolito_zip:nn { #3 }
  seq_mapthread_function:NNN l__iagolito_zip_a_seq l__iagolito_zip_b_seq __iagolito_zip:nn
 }

seq_new:N l__iagolito_zip_a_seq
seq_new:N l__iagolito_zip_b_seq

ExplSyntaxOff

begin{document}

zip{a,b,c}{1,2,3}{#1-#2 }

end{document}

You can check this prints

a-1 b-2 c-3

If the two lists have different number of elements, the loop ends when either list ends.


The more complex features can be accomplished as well. Beware that the two optional argument must both appear, if the complex processing is needed.

The idea is to populate another sequence where the two lists are merged and then an auxiliary macro can be applied.

documentclass{article}
usepackage{xparse}

ExplSyntaxOn

NewDocumentCommand{zip}{oomm +m}
 {
  IfNoValueTF { #1 }
   { iagolito_zip_simple:nnn { #3 } { #4 } { #5 } }
   { iagolito_zip_full:nnnnn { #1 } { #2 } { #3 } { #4 } { #5 } }
 }

seq_new:N l__iagolito_zip_a_seq
seq_new:N l__iagolito_zip_b_seq
seq_new:N l__iagolito_zip_c_seq

cs_new_protected:Nn iagolito_zip_simple:nnn
 {
  seq_set_from_clist:Nn l__iagolito_zip_a_seq { #1 }
  seq_set_from_clist:Nn l__iagolito_zip_b_seq { #2 }
  cs_set:Nn __iagolito_zip:nn { #3 }
  seq_mapthread_function:NNN l__iagolito_zip_a_seq l__iagolito_zip_b_seq __iagolito_zip:nn
 }

cs_new_protected:Nn iagolito_zip_full:nnnnn
 {
  seq_set_from_clist:Nn l__iagolito_zip_a_seq { #3 }
  seq_set_from_clist:Nn l__iagolito_zip_b_seq { #4 }
  seq_clear:N l__iagolito_zip_c_seq
  cs_set:Npn __iagolito_zip_process:w #1 q_stop #2 q_stop { #5 }
  seq_mapthread_function:NNN l__iagolito_zip_a_seq l__iagolito_zip_b_seq __iagolito_merge:nn
  seq_map_inline:Nn l__iagolito_zip_c_seq { ##1 }
 }

cs_new_protected:Nn __iagolito_merge:nn
 {
  seq_put_right:Nn l__iagolito_zip_c_seq { __iagolito_zip_process:w #1 q_stop #2 q_stop }
 }

ExplSyntaxOff

begin{document}

zip{a,b,c}{1,2,3}{#1-#2 }

zip[#1/#2][#3::#4]
    {a/A,b/B,c/C}
    {1::I,2::II,3::III}
    {Grand #1 is #2 but grand #3 is #4.par}

end{document}

enter image description here

If you want to pass macros expanding to lists, do one step expansion.

documentclass{article}
usepackage{xparse}

ExplSyntaxOn

NewDocumentCommand{zip}{oomm +m}
 {
  IfNoValueTF { #1 }
   { iagolito_zip_simple:oon { #3 } { #4 } { #5 } }
   { iagolito_zip_full:nnoon { #1 } { #2 } { #3 } { #4 } { #5 } }
 }

seq_new:N l__iagolito_zip_a_seq
seq_new:N l__iagolito_zip_b_seq
seq_new:N l__iagolito_zip_c_seq

cs_new_protected:Nn iagolito_zip_simple:nnn
 {
  seq_set_from_clist:Nn l__iagolito_zip_a_seq { #1 }
  seq_set_from_clist:Nn l__iagolito_zip_b_seq { #2 }
  cs_set:Nn __iagolito_zip:nn { #3 }
  seq_mapthread_function:NNN l__iagolito_zip_a_seq l__iagolito_zip_b_seq __iagolito_zip:nn
 }
cs_generate_variant:Nn iagolito_zip_simple:nnn { oo }

cs_new_protected:Nn iagolito_zip_full:nnnnn
 {
  seq_set_from_clist:Nn l__iagolito_zip_a_seq { #3 }
  seq_set_from_clist:Nn l__iagolito_zip_b_seq { #4 }
  seq_clear:N l__iagolito_zip_c_seq
  cs_set:Npn __iagolito_zip_process:w #1 q_stop #2 q_stop { #5 }
  seq_mapthread_function:NNN l__iagolito_zip_a_seq l__iagolito_zip_b_seq __iagolito_merge:nn
  seq_map_inline:Nn l__iagolito_zip_c_seq { ##1 }
 }
cs_generate_variant:Nn iagolito_zip_full:nnnnn { nnoo }

cs_new_protected:Nn __iagolito_merge:nn
 {
  seq_put_right:Nn l__iagolito_zip_c_seq { __iagolito_zip_process:w #1 q_stop #2 q_stop }
 }

ExplSyntaxOff

newcommand{listA}{a,b,c}
newcommand{listB}{1,2,3}
newcommand{listC}{a/A,b/B,c/C}
newcommand{listD}{1::I,2::II,3::III}

begin{document}

zip{a,b,c}{1,2,3}{#1-#2 }
zip{listA}{1,2,3}{#1-#2 }
zip{a,b,c}{listB}{#1-#2 }
zip{listA}{listB}{#1-#2 }

zip[#1/#2][#3::#4]
    {a/A,b/B,c/C}
    {1::I,2::II,3::III}
    {Grand #1 is #2 but grand #3 is #4.par}

zip[#1/#2][#3::#4]
    {listC}
    {1::I,2::II,3::III}
    {Grand #1 is #2 but grand #3 is #4.par}

zip[#1/#2][#3::#4]
    {a/A,b/B,c/C}
    {listD}
    {Grand #1 is #2 but grand #3 is #4.par}

zip[#1/#2][#3::#4]
    {listC}
    {listD}
    {Grand #1 is #2 but grand #3 is #4.par}

end{document}

The output is the same as before, just repeated four times for each instance.

Answered by egreg on November 1, 2021

The simple case with two arguments

enter image description here

documentclass{article}

begin{document}

defzip#1#2#3{%
defz##1##2{#3}%
xzip#1,relax#2,relax}
defxzip#1,#2relax#3,#4relax{%
z{#1}{#3}%
ifrelaxdetokenize{#2}relax
expandafterzzgobble
fi
xzip#2relax#4relax
}

defzzgobble#1relax#2relax{}


zip{a,b,c}{1,2,3}{#1-#2 }
end{document}

Answered by David Carlisle on November 1, 2021

EDIT to provide full answer to all scenarios posed by OP. As long as there are always two lists to combine, the effort can be done with listofitems.

Optional sub-argument separators can be multi-token; however they cannot include / or |, though if this is an issue, let me know. Perhaps some accommodation can be had.

documentclass{article}
usepackage[T1]{fontenc}
usepackage{listofitems}
defzipaux{}
newcommandzip[1][]{%
  deftmpA{#1}%
  zipB%
}
newcommandzipB[1][]{%
  expandafterzipCexpandafter{tmpA}{#1}%
}
newcommandzipC[5]{%
  ifxrelax#1relax
    setsepchar{,}%
    readlist*argA{#3}%
    ifxrelax#2relax
      renewcommandzipaux[2]{#5}%
      setsepchar{,}
    else
      renewcommandzipaux[3]{#5}%
      setsepchar{,/#2}
    fi
  else
    setsepchar{,/#1}%
    readlist*argA{#3}%
    ifxrelax#2relax
      renewcommandzipaux[3]{#5}%
      setsepchar{,}
    else
      renewcommandzipaux[4]{#5}%
      setsepchar{,/#2}
    fi
  fi%
  readlist*argB{#4}%
  foreachitemzinargB{%
    ifxrelax#1relax
      ifxrelax#2relax
        zipaux{argA[zcnt]}{argB[zcnt]}%
      else
        zipaux{argA[zcnt]}{argB[zcnt,1]}{argB[zcnt,2]}%
      fi
    else
      ifxrelax#2relax
        zipaux{argA[zcnt,1]}{argA[zcnt,2]}{argB[zcnt]}%
      else
        zipaux{argA[zcnt,1]}{argA[zcnt,2]}
               {argB[zcnt,1]}{argB[zcnt,2]}%
      fi
    fi
  }%
}
begin{document}
zip{a,b,c}{1,2,3}{#1-#2 }

zip[?]{a?A,b?B,c?C}{1,2,3}{(#1*#2)-#3 }

zip[?][::]{a?A,b?B,c?C}{1::X,2::Y,3::Z}{(#1*#2)-#3/#4 }

zip[][::]{a,b,c}{1::X,2::Y,3::Z}{(#1-#2)/#3 }
end{document}

enter image description here

Answered by Steven B. Segletes on November 1, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP