TikZ 的 \foreach \x/\y 中...运行索引集是一个宏

TikZ 的 \foreach \x/\y 中...运行索引集是一个宏

请看一下这个包含我的问题的最小示例:

\documentclass[english]{article}
\usepackage{tikz,ifthen,listings}
\newcommand\IndexListOne{1,2,3}
\newcommand\IndexListTwo{One,Two,Three}
%
\begin{document}
\noindent Consider this loop:\\
\foreach [count=\xi] \x in \IndexListOne {
\foreach [count=\yi] \y in \IndexListTwo {
\ifthenelse{ \equal{\xi}{\yi} }{\texttt{Each loop runs over the pair (\x,\y) as I want it to.}\\}{}
}
}
%
I know I can achieve this also with:\\
%
\foreach \x/\y in {1/One,2/Two,3/Three} {
\texttt{Each loop runs over the pair (\x,\y) as I want it to.}\\
}
The problem is that the macros `IndexListOne` and `IndexListTwo` are long in the actual application I face. So I either want to know how to define a macro like:
\begin{lstlisting}
\newcommand\IndexListCombined{1/One,2/Two,3/Two} 
\end{lstlisting}
so that I can use
\begin{lstlisting}
\foreach \x/\y in \IndexListCombined {...
\end{lstlisting}
Alternatively, even better would be something like:
\begin{lstlisting}
\foreach \x in \IndexListOne and \y in \IndexListTwo {...
\end{lstlisting}
\end{document}

实际上,我有四个索引列表IndexListOneIndexListFour每个列表包含 20 个逗号分隔的索引。因此,像我的最小示例一样使用四个循环需要太多的运行时间。我认为下面 kpyms 的解决方案(创建的解决方案)\mylist很棒,因为它很短、灵活,并且需要的运行时间很少。唉,因为我无法运行 Python,有没有乳胶代码可以生成 kpym\mylist并合并可选数量的列表(在我的情况下是四个列表)?

答案1

只是为了好玩,一个 python 解决方案(根据@percusse 的评论更新):

\documentclass[border=7mm]{standalone}
\usepackage{python}

\begin{document}

\begin{python}
a = "1,2,3"
b = "one,two,three"
for t in zip(a.split(','),b.split(',')):
    print '%s is %s\\quad' % t
\end{python}%

\end{document}

LaTeX 显示文本“1 是一 2 是二 3 是三”

或者使用 python 连接列表并循环获得相同的结果foreach

\documentclass[border=7mm]{standalone}
\usepackage{python, pgffor}

\begin{document}

\begin{python}%
a = "1,2,3"
b = "one,two,three"
print '\global\def\mylist{'+','.join([x+'/'+y for x,y in zip(a.split(','),b.split(','))])+'}'
\end{python}%

\foreach \i/\j in \mylist{\i\ is \j\quad}

\end{document}

或者,如果您只想将两个长列表合并为一个,然后将其复制/粘贴到代码中,则可以使用以下 javascript 解决方案:https://kpym.github.io/joinlatexlists/

答案2

它并不漂亮,但我思考可以。所有列表的长度必须相同。

\documentclass[varwidth,border=5]{standalone}
\usepackage{pgf,pgffor}
\makeatletter
\newif\if@il@finish
\def\interleavelists#1#2{%
  \let\il@list=\@empty%
  \begingroup%
  \il@interleave{#2}%
  \endgroup%
  \let#1=\il@list%
}
\def\@stop{@stop}%
\def\il@interleave#1{%
  \global\let\il@tmp=\@empty%
  \pgfutil@for\@i:=\noexpand#1\do{%
    \expandafter\expandafter\expandafter\il@extract\@i,@stop,\stop%
    \expandafter\let\@i=\il@rest%
    \ifx\il@item\@stop%
      \@il@finishtrue%
    \else%
      \ifx\il@tmp\@empty%
        \expandafter\pgfutil@g@addto@macro\expandafter\il@tmp\expandafter%
          {\il@item}%
      \else%
        \expandafter\pgfutil@g@addto@macro\expandafter\il@tmp\expandafter%
          {\expandafter/\il@item}%
      \fi%
    \fi%
  }%
  \if@il@finish%
    \let\@next=\relax%
  \else%
    \ifx\il@list\@empty%
      \expandafter\pgfutil@g@addto@macro\expandafter\il@list\expandafter%
        {\il@tmp}%
    \else%
      \expandafter\pgfutil@g@addto@macro\expandafter\il@list\expandafter%
        {\expandafter,\il@tmp}%
    \fi%
    \def\@next{\il@interleave{#1}}%
  \fi%
  \@next%
}
\def\il@extract#1,#2,\stop{\def\il@item{#1}\def\il@rest{#2}}

\def\lista{1,2,3}
\def\listb{One,Two,Three}
\def\listc{I,II,III}
\def\listd{un,deux,trois}

\def\showlist#1{\string#1\space\meaning#1}
\begin{document}
\ttfamily
\interleavelists\listx{\lista,\listb}\showlist\listx\par
\interleavelists\listy{\lista,\listb,\listc}\showlist\listy\par
\interleavelists\listz{\lista,\listb,\listc,\listd}\showlist\listz
\end{document}

在此处输入图片描述

答案3

你想要这样的东西吗?

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\combinedforeach}{sm sm +m}
 {
  \IfBooleanTF{#1}
   {
    \IfBooleanTF{#3}
     {
      \sebastian_combined_foreach:VVn #2 #4 { #5 }
     }
     {
      \sebastian_combined_foreach:Vnn #2 { #4 } { #5 }
     }
   }
   {
    \IfBooleanTF{#3}
     {
      \sebastian_combined_foreach:nVn { #2 } #4 { #5 }
     }
     {
      \sebastian_combined_foreach:nnn { #2 } { #4 } { #5 }
     }
   }
 }

\seq_new:N \l_sebastian_foreach_first_seq
\seq_new:N \l_sebastian_foreach_second_seq

\cs_new_protected:Nn \sebastian_combined_foreach:nnn
 {
  \group_begin:
  \seq_set_split:Nnn \l_sebastian_foreach_first_seq { , } { #1 }
  \seq_set_split:Nnn \l_sebastian_foreach_second_seq { , } { #2 }
  \cs_set:Nn \__sebastian_process:nn { #3 }
  \seq_mapthread_function:NNN
    \l_sebastian_foreach_first_seq
    \l_sebastian_foreach_second_seq
    \__sebastian_process:nn
  \group_end:
 }

\cs_generate_variant:Nn \sebastian_combined_foreach:nnn { Vn, nV, VV }
\ExplSyntaxOff

\newcommand\IndexListOne{1,2,3}
\newcommand\IndexListTwo{One,Two,Three}

\begin{document}

\combinedforeach*{\IndexListOne}*{\IndexListTwo}{(#1--#2)\par}

\combinedforeach{1,2,3,4}{5,6,7}{#2!#1!}

\end{document}

您可以使用\xand来代替and 。当任一列表结束时,处理都会停止。\y#1#2

参数前的星号表示该参数应该扩展。

在此处输入图片描述

对于任意数量的列表(最多 9 个),全部设置为控制序列,这是一种(缓慢的)可能性;列表不必具有相同的长度,循环将在给定列表的最大长度处停止。列表中的项目再次用 表示#1#2依此类推。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\combinedforeach}{m +m}
 {
  \sebastian_combined_foreach:nn { #1 } { #2 }
 }

\int_new:N \l_sebastian_combined_lists_int
\int_new:N \l_sebastian_combined_max_int
\tl_new:N \l_sebastian_combined_action_tl
\tl_new:N \l_sebastian_combined_arg_tl

\cs_new_protected:Nn \sebastian_combined_foreach:nn
 {
  \group_begin:
  \int_set:Nn \l_sebastian_combined_lists_int { \clist_count:n { #1 } }
  \int_set:Nn \l_sebastian_combined_max_int { \c_max_int }
  \clist_map_inline:nn { #1 }
   {
    \int_set:Nn \l_sebastian_combined_max_int
     { \int_min:nn { \l_sebastian_combined_max_int } { \clist_count:N ##1 } }
   }
  \tl_set:Nx \l_sebastian_combined_action_tl
   {
    __sebastian_combined_action: \prg_replicate:nn { \l_sebastian_combined_lists_int } { n }
   }
  \cs_set:cn { \l_sebastian_combined_action_tl } { #2 }
  \int_step_inline:nnnn { 1 } { 1 } { \l_sebastian_combined_max_int }
   {
    \tl_clear:N \l_sebastian_combined_arg_tl
    \clist_map_inline:nn { #1 }
     {
      \tl_put_right:Nx \l_sebastian_combined_arg_tl
       {
        { \clist_item:Nn ####1 { ##1 } }
       }
     }
    \sebastian_combined_doaction:cV { \l_sebastian_combined_action_tl }  \l_sebastian_combined_arg_tl
   } 
  \group_end:
 }

\cs_new:Nn \sebastian_combined_doaction:Nn { #1 #2 }
\cs_generate_variant:Nn \sebastian_combined_doaction:Nn { cV }
\ExplSyntaxOff

\newcommand\IndexListOne{1,2,3}
\newcommand\IndexListTwo{One,Two,Three}
\newcommand\IndexListThree{A,B,C,D}
\newcommand\IndexListFour{aleph,beth,gimel}

\begin{document}

\combinedforeach{\IndexListOne,\IndexListTwo}{(#1--#2)\par}

\combinedforeach{
  \IndexListOne,
  \IndexListTwo,
  \IndexListThree,
  \IndexListFour,
}{(#1--#2--#3--#4)\par}

\end{document}

在此处输入图片描述

答案4

在您的原始回答中,您说您尝试连接两个长列表。但 20 个元素的列表并不长。因此,您可以使用非优化的 O(N^2) 方法。

即使有 4 个列表,第一种方法也能正常工作,因为您不需要 4 个嵌套循环,而只需要一个包含 3 个子循环的循环。因此,它仅比 2 个列表慢 3 倍。

\documentclass[varwidth,border=7mm]{standalone}
\usepackage{pgffor}

% example : \get{\listb}{3}{\b} defines \b to be the 3th element of \listb
\def\get#1#2#3{
  \foreach[count=\i] \x in #1{
    \ifnum \i=#2 \xdef#3{\x}\breakforeach \fi
  }
}

\begin{document}

% 4 lists of 20 elements
\def\lista{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t}
\def\listb{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}
\def\listc{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T}
\def\listd{+,-,+,-,+,-,+,-,+,-,+,-,+,-,+,-,+,-,+,-}

% construct the \newlist
\def\newlist{}
\foreach[count=\j] \a in \lista{
  \get{\listb}{\j}{\b}
  \get{\listc}{\j}{\c}
  \get{\listd}{\j}{\d}
  \xdef\newlist{\newlist%
  \ifnum \j>1,\fi%
  \a/\b/\c/\d}
}

% loop over the \newlist
\foreach \a/\b/\c/\d in \newlist{
  (\a,\b,\c,\d)\newline
}
\end{document}

在此处输入图片描述

相关内容