请看一下这个包含我的问题的最小示例:
\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}
实际上,我有四个索引列表IndexListOne
,IndexListFour
每个列表包含 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}
或者使用 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}
您可以使用\x
and来代替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}