两个列表之间的 Foreach 循环

两个列表之间的 Foreach 循环
\documentclass{article}
\usepackage{tikz}

\def\firstlist{0,1,2}
\def\secondlist{0,1,2}

\newcommand{\testa}{
 \foreach \x/\y in {\firstlist/\secondlist} {
    \draw(0,\x)--(1,\y);
    }
}

\newcommand{\testb}{
 \foreach \x/\y in {0/0,1/1,2/2} {
    \draw(0,\x)--(1,\y);
    }
}
\begin{document}
\begin{tikzpicture}
\testa
\end{tikzpicture}
\end{document}

如何才能使命令的输出\testa等于\testb命令的输出?

答案1

这是一个非常简单的解决方案:

\documentclass{article}
\usepackage{tikz}

\def\firstlist{0,1,2}
\def\secondlist{0,1,2}

\newcommand{\testa}{
  \foreach \x [count=\c,evaluate=\c as \y using {{\secondlist}[\c-1]}]  in \firstlist {
    \draw(0,\x)--(1,\y);
  }
}

\begin{document}
\begin{tikzpicture}
\testa
\end{tikzpicture}
\end{document}

定义新样式的扩展解决方案parallel foreach。您可以使用多个并行列表:

\documentclass{standalone}
\usepackage{tikz}

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

\def\firstlist{0,1,2,10,11}
\def\secondlist{0,1,2,10,11}
\def\thirdlist{1,2,10,11,0}

\newcommand{\testa}{
  \foreach \x [count=\c,
  parallel foreach=\y in \secondlist via \c,
  parallel foreach=\z in \thirdlist via \c]
  in \firstlist
  {
    \node[left] at (0,\x) {\x};
    \draw(0,\x)--(1,\y);
    \draw[red](0,\x)--(1,\z);
  }
}

\begin{document}
\begin{tikzpicture}
\testa
\end{tikzpicture}
\end{document}

两种方法都使用大批pgfmath 的特点:它们将每个元素评估为一个数学公式。如果您想要字符串元素,请使用引号 ( "...")。

\documentclass[margin=1mm]{standalone}
\usepackage{tikz}

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

\def\firstlist{0,1,2,10,11}
\def\secondlist{0,1,2,10,11}
\def\thirdlist{1,2,10,11,0}
\def\fourthlist{"label a","label b","label c","label $\delta$","label e"}
\newcommand{\testa}{
  \foreach \x [count=\c,
  parallel foreach=\y in \secondlist via \c,
  parallel foreach=\z in \thirdlist via \c,
  parallel foreach=\lab in \fourthlist via \c]
  in \firstlist
  {
    \node[left] at (0,\x) {\lab};
    \draw(0,\x)--(1,\y);
    \draw[red](0,\x)--(1,\z);
  }
}

\begin{document}
\begin{tikzpicture}
\testa
\end{tikzpicture}
\end{document}

在此处输入图片描述

答案2

你可以计算其中一个数组中有多少个元素,并将该数字用作 foreach 的限制。然后每次旋转都会访问数组的一个元素;

\documentclass[tikz]{standalone}
\def\firstlist{{0,1,2}} % <== Notice the double brace for array notation
\def\secondlist{{0,1,2}}

\newcommand{\testa}{
\pgfmathdim{\firstlist}%Get the number of elements in array
  \foreach \x in {0,...,\numexpr\pgfmathresult-1\relax}{%array index starts from zero
    \draw(0,{array(\firstlist,\x)})--(1,{array(\secondlist,\x)});
    }
}

\newcommand{\testb}{
 \foreach \x/\y in {0/0,1/1,2/2} {
    \draw(0,\x)--(1,\y);
    }
}
\begin{document}
\begin{tikzpicture}
\testa
\begin{scope}[shift={(2,0)}] %Test the result with \testb next to it
\testb
\end{scope}
\end{tikzpicture}
\end{document}

在此处输入图片描述

答案3

我不太确定你是否想这么做。;-)

\documentclass{article}
\usepackage{xparse}

\def\xforeach#1#{\xforeachaux{#1}}

\ExplSyntaxOn
\NewDocumentCommand{\xforeachaux}{mm}
 {
  \carlitos_xforeach:nn { #1 } { #2 }
 }

\seq_new:N \l_carlitos_xf_first_seq
\seq_new:N \l_carlitos_xf_second_seq
\seq_new:N \l_carlitos_xf_list_seq

\cs_new_protected:Npn \carlitos_xforeach:nn #1 #2
 {
  \seq_clear:N \l_carlitos_xf_list_seq
  \__carlitos_split:n { #2 }
  \seq_mapthread_function:NNN
   \l_carlitos_xf_first_seq
   \l_carlitos_xf_second_seq
   \__carlitos_xf_additem:nn
  \__carlitos_xf_do:nx { #1 } { \seq_use:Nn \l_carlitos_xf_list_seq { , } }
 }
\cs_new_protected:Npn \__carlitos_split:n #1
 {
  \__carlitos_split_aux:w #1 \q_stop
 }
\cs_new_protected:Npn \__carlitos_split_aux:w #1/#2 \q_stop
 {
  \seq_set_split:Nno \l_carlitos_xf_first_seq { , } { #1 }
  \seq_set_split:Nno \l_carlitos_xf_second_seq { , } { #2 }
 }
\cs_new_protected:Npn \__carlitos_xf_additem:nn #1 #2
 {
  \seq_put_right:Nn \l_carlitos_xf_list_seq { #1/#2 }
 }
\cs_new_protected:Npn \__carlitos_xf_do:nn #1 #2
 {
  \foreach #1 { #2 }
 }
\cs_generate_variant:Nn \seq_set_split:Nnn { Nno }
\cs_generate_variant:Nn \__carlitos_xf_do:nn { nx }
\ExplSyntaxOff

\usepackage{tikz}

\def\firstlist{0,1,2}
\def\secondlist{0,1,2}

\newcommand{\testa}{
 \xforeach \x/\y in {\firstlist/\secondlist} {
    \draw(0,\x)--(1,\y);
    }
}

\newcommand{\testb}{
 \foreach \x/\y in {0/0,1/1,2/2} {
    \draw(0,\x)--(1,\y);
    }
}
\begin{document}
\begin{tikzpicture}
\testa
\end{tikzpicture}
\qquad
\begin{tikzpicture}
\testb
\end{tikzpicture}
\end{document}

在此处输入图片描述


一个完全不同的实现,你甚至不需要\x/\y;它支持最多三个斜杠分隔的参数,可以是逗号分隔的列表(逗号前后的空格被忽略)或扩展为逗号分隔列表的宏。

foreach 循环中的每个循环都使用第二个参数执行,该参数使用#1#2#3表示当前列表元素。循环次数由第一个列表决定。

如果给定的项目超过三个,可能应该提供更好的错误恢复,但这留作练习。

\documentclass{article}
\usepackage{xparse,tikz}

\ExplSyntaxOn

\NewDocumentCommand{\Xforeach}{mm}
 {
  \carlitos_Xforeach:nn { #1 } { #2 }
 }

\seq_new:N \l__carlitos_Xf_input_seq
\seq_new:N \l__carlitos_Xf_output_seq
\seq_new:N \l__carlitos_Xf_lista_seq
\seq_new:N \l__carlitos_Xf_listb_seq
\seq_new:N \l__carlitos_Xf_listc_seq
\tl_new:N \l__carlitos_Xf_tmp_tl
\int_new:N \l__carlitos_Xf_cycle_int

\cs_new_protected:Npn \carlitos_Xforeach:nn #1 #2
 {
  \seq_set_split:Nnn \l__carlitos_Xf_input_seq { / } { #1 }
  \seq_clear:N \l__carlitos_Xf_output_seq
  \seq_map_inline:Nn \l__carlitos_Xf_input_seq
   {
    \seq_put_right:No \l__carlitos_Xf_output_seq { ##1 }
   }
  \int_case:nnF { \seq_count:N \l__carlitos_Xf_input_seq }
   {
    { 1 } { \__carlitos_Xf_one:n   { #2 } }
    { 2 } { \__carlitos_Xf_two:n   { #2 } }
    { 3 } { \__carlitos_Xf_three:n { #2 } }
   }
   {
    Only~one,~two~or~three!
   }
 }

\cs_new_protected:Npn \__carlitos_Xf_one:n #1
 {
  \seq_pop_left:NN \l__carlitos_Xf_output_seq \l__carlitos_Xf_tmp_tl
  \seq_set_split:NnV \l__carlitos_Xf_first_seq { , } \l__carlitos_Xf_tmp_tl
  \cs_set_protected:Npn \__carlitos_Xf_loop:n ##1 { #1 }
  \cs_generate_variant:Nn \__carlitos_Xf_loop:n { x }
  \int_zero:N \l__carlitos_Xf_cycle_int
  \seq_map_inline:Nn \l__carlitos_Xf_first_seq
   {
    \int_incr:N \l__carlitos_Xf_cycle_int
    \__carlitos_Xf_loop:x { \seq_item:Nn \l__carlitos_Xf_first_seq { \l__carlitos_Xf_cycle_int } }
   }
 }
\cs_new_protected:Npn \__carlitos_Xf_two:n #1
 {
  \seq_pop_left:NN \l__carlitos_Xf_output_seq \l__carlitos_Xf_tmp_tl
  \seq_set_split:NnV \l__carlitos_Xf_first_seq { , } \l__carlitos_Xf_tmp_tl
  \seq_pop_left:NN \l__carlitos_Xf_output_seq \l__carlitos_Xf_tmp_tl
  \seq_set_split:NnV \l__carlitos_Xf_second_seq { , } \l__carlitos_Xf_tmp_tl
  \cs_set_protected:Npn \__carlitos_Xf_loop:nn ##1 ##2 { #1 }
  \cs_generate_variant:Nn \__carlitos_Xf_loop:nn { xx }
  \int_zero:N \l__carlitos_Xf_cycle_int
  \seq_map_inline:Nn \l__carlitos_Xf_first_seq
   {
    \int_incr:N \l__carlitos_Xf_cycle_int
    \__carlitos_Xf_loop:xx
     { \seq_item:Nn \l__carlitos_Xf_first_seq { \l__carlitos_Xf_cycle_int } }
     { \seq_item:Nn \l__carlitos_Xf_second_seq { \l__carlitos_Xf_cycle_int } }
   }
 }
\cs_new_protected:Npn \__carlitos_Xf_three:n #1
 {
  \seq_pop_left:NN \l__carlitos_Xf_output_seq \l__carlitos_Xf_tmp_tl
  \seq_set_split:NnV \l__carlitos_Xf_first_seq { , } \l__carlitos_Xf_tmp_tl
  \seq_pop_left:NN \l__carlitos_Xf_output_seq \l__carlitos_Xf_tmp_tl
  \seq_set_split:NnV \l__carlitos_Xf_second_seq { , } \l__carlitos_Xf_tmp_tl
  \seq_pop_left:NN \l__carlitos_Xf_output_seq \l__carlitos_Xf_tmp_tl
  \seq_set_split:NnV \l__carlitos_Xf_third_seq { , } \l__carlitos_Xf_tmp_tl
  \cs_set_protected:Npn \__carlitos_Xf_loop:nnn ##1 ##2 ##3 { #1 }
  \cs_generate_variant:Nn \__carlitos_Xf_loop:nnn { xxx }
  \int_zero:N \l__carlitos_Xf_cycle_int
  \seq_map_inline:Nn \l__carlitos_Xf_first_seq
   {
    \int_incr:N \l__carlitos_Xf_cycle_int
    \__carlitos_Xf_loop:xxx
     { \seq_item:Nn \l__carlitos_Xf_first_seq { \l__carlitos_Xf_cycle_int } }
     { \seq_item:Nn \l__carlitos_Xf_second_seq { \l__carlitos_Xf_cycle_int } }
     { \seq_item:Nn \l__carlitos_Xf_third_seq { \l__carlitos_Xf_cycle_int } }
   }
 }

\ExplSyntaxOff
\begin{document}
\def\firstlist{0,1,2}
\def\secondlist{0,1,2}
\def\thirdlist{a,b,c}

\Xforeach{\firstlist}{--#1-- }

\bigskip

\begin{tikzpicture}
\Xforeach{\firstlist/\secondlist}{\draw(0,#1)--(1,#2);}
\end{tikzpicture}\qquad
\begin{tikzpicture}
\Xforeach{0,1,2/\secondlist}{\draw(0,#1)--(1,#2);}
\end{tikzpicture}

\bigskip

\Xforeach{\firstlist/\secondlist/\thirdlist}{#1/#2/#3 }

\Xforeach{\firstlist/\secondlist/\thirdlist}{#1/#2/#3 }

\Xforeach{\firstlist/0,1,2/a,b,c}{#1/#2/#3 }
\end{document}

在此处输入图片描述

答案4

你总是可以“推出自己的”解决方案,但要使它看起来这么好,需要做大量的工作\foreach

\documentclass[tikz,border=5]{standalone}
\makeatletter
\def\attxt{@}
\long\def\frch#1#2#3{\long\def\frchaction##1##2{#3}%
\def\lsta{}\def\lstb{}%
\edef\lst{#1,@,/#2,@,}\expandafter\@frch\lst\@}
\def\@frch#1,#2/#3,#4\@{%
\def\tmpa{#1}\def\tmpb{#3}%
\ifx\tmpa\attxt%
  \ifx\tmpb\attxt%
  \else%
    \def\lstb{#3}\frchaction{\lsta}{#3}\@frch @,/#4,\@%
  \fi%
\else%
  \ifx\tmpb\attxt%
    \def\lsta{#1}\frchaction{#1}{\lstb}\@frch#2/@,\@%
  \else%
    \def\lsta{#1}\def\lstb{#3}\frchaction{#1}{#3}\@frch#2/#4\@%
  \fi%
\fi}

\begin{document}
\begin{tikzpicture}
\frch{0,1,2}{0,1,2}{ \draw (0,#1) -- (1,#2); }
\frch{0,1,2}{0,1,2,3,4,5}{ \draw (2,#1) -- (3,#2); }
\frch{0,1,2,3,4,5}{0,1,2}{ \draw (4,#1) -- (5,#2); }
\end{tikzpicture}
\end{document}

在此处输入图片描述

相关内容