3

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 "before\getitem{#2}[#3]"
  },
}

\begin{document}
\begin{tikzpicture}
  \foreach \i [
    count = \c,
    parallel foreach = \x in {test} via \c,
  ] in {1}
    \node at (\i, 0) {blank\x};
  \node at (1, -1) {no blank\getitem{{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

1 Answer 1

3

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:Nn\g_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 "before\getitem{#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) {blank\x};
    \node at (1, -1) {no blank\getitem{{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 "before\getitem{#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) {blank\x};
    \node at (1, -1) {no blank\getitem{{test}}[1]};
\end{tikzpicture}
\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .