TransWikia.com

tikz parallel foreach

TeX - LaTeX Asked on January 13, 2021

I have seen the solution in this post

pgfset{
  foreach/parallel foreach/.style args={#1in#2via#3}{evaluate=#3 as #1 using {{#2}[#3-1]}},
}

but there is a bug: when all lists both have only one item, it will not work as expected. The reason is in this post.

So I try to introduce latex3 to make it bug free.

documentclass[tikz, border=1cm]{standalone}
usepackage{xparse}
ExplSyntaxOn
clist_new:N g_list_clist
NewDocumentCommand {getitem} {m O{1}} {
  clist_gset:Nn g_list_clist #1
  clist_item:Nn g_list_clist {#2}
}
ExplSyntaxOff
pgfset{
  foreach/parallel foreach/.style args={#1in#2via#3}{
    evaluate=#3 as #1 using "beforegetitem{#2}[#3]"
  },
}

begin{document}
begin{tikzpicture}
  foreach i [
    count = c,
    parallel foreach = x in {test} via c,
  ] in {1}
    node at (i, 0) {blankx};
  node at (1, -1) {no blankgetitem{{test}}[1]};
end{tikzpicture}
end{document}

But there is a small problem: the item obtained in this way has an extra blank space before it. How to fix this problem? And why #1 doesn’t need to be braced?
enter image description here

One Answer

You define parallel foreach as being a style with parameter text #1in#2via#3, with no spaces, and then you use:

parallel foreach = x in {test} via c

so the arguments to parallel foreach are ␣x␣, ␣{test}␣, and ␣c, then when you pass it to getitem, you have:

clist_gset:Nng_list_clist␣{test}␣

The first space token is consumed by TeX when grabbing the second argument to clist_gset:Nn, but the second space is typeset in your node.

There are several possibilities here, for instance tl_trim_spaces:n:

clist_gset:Nx g_list_clist { tl_trim_spaces:n {#1} }

(actually, the implementation is a bit more complicated to check for braces around the argument to parallel foreach; see the code at the bottom of this answer). However expl3 provides clist_item:nn, so you can take items from a comma-separated list without storing it in a variable, which makes things simpler here:

exp_args:Nx clist_item:nn
  { tl_trim_spaces:n {#1} } {#2}

Though this doesn't cope with the possibility of multiple items in the list because of the extra braces. You need to check if the argument to parallel foreach is within braces, and if so remove them, with something like:

NewDocumentCommand {getitem} {m O{1}} {
  exp_args:Nx clist_item:nn
    { tl_trim_spaces_apply:nN {#1} zhiyuan_remove_braces:n } {#2}
}
cs_new:Npn zhiyuan_remove_braces:n #1
  {
    tl_if_head_is_group:nTF {#1}
      { exp_not:n #1 }
      { exp_not:n {#1} }
  }

With that, your code produces the expected output:

enter image description here

documentclass[tikz, border=1cm]{standalone}
usepackage{xparse}
ExplSyntaxOn
NewDocumentCommand {getitem} {m O{1}} {
  exp_args:Nx clist_item:nn
    { tl_trim_spaces_apply:nN {#1} zhiyuan_remove_braces:n } {#2}
}
cs_new:Npn zhiyuan_remove_braces:n #1
  {
    tl_if_head_is_group:nTF {#1}
      { exp_not:n #1 }
      { exp_not:n {#1} }
  }
ExplSyntaxOff
pgfset{
  foreach/parallel foreach/.style args={#1in#2via#3}{
    evaluate=#3 as #1 using "beforegetitem{#2}[#3]"
  },
}

begin{document}
begin{tikzpicture}
  foreach i [
    count = c,
    parallel foreach = x in {test,another} via c,
  ] in {1,2}
    node at (1, i) {blankx};
    node at (1, -1) {no blankgetitem{{test}}[1]};
end{tikzpicture}
end{document}

And here's the code using a clist variable rather than clist_item:nn:

documentclass[tikz, border=1cm]{standalone}
usepackage{xparse}
ExplSyntaxOn
clist_new:N g_list_clist
NewDocumentCommand {getitem} {m O{1}} {
  use:x
    {
      clist_gset:Nx exp_not:N g_list_clist
        { tl_trim_spaces_apply:nN {#1} zhiyuan_remove_braces:n }
    }
  clist_item:Nn g_list_clist {#2}
}
cs_new:Npn zhiyuan_remove_braces:n #1
  {
    tl_if_head_is_group:nTF {#1}
      { exp_not:n #1 }
      { exp_not:n {#1} }
  }
ExplSyntaxOff
pgfset{
  foreach/parallel foreach/.style args={#1in#2via#3}{
    evaluate=#3 as #1 using "beforegetitem{#2}[#3]"
  },
}

begin{document}
begin{tikzpicture}
  foreach i [
    count = c,
    parallel foreach = x in {test,another} via c,
  ] in {1,2}
    node at (1, i) {blankx};
    node at (1, -1) {no blankgetitem{{test}}[1]};
end{tikzpicture}
end{document}

Correct answer by Phelype Oleinik on January 13, 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