我知道这个包裹电子工具箱有一个 \foreach 命令。
我想设置一次列表并多次使用它。在示例中,我将设置 \mylist 等于逗号分隔值列表 10,20,30,40,50。
我想要一个像这样的命令
\forparteach 3 \var \in {10,20,30, 40,50} {
\var
}
是相同的
\foreach \var \in {10,20,30} {
\var
}
甚至
\forparteach {1,3,4,2,5,5} \var \in {10,20,30,40,50} {
\var
}
是相同的
\foreach \var \in {10,30,40,20,50,50} {
\var
}
是否有任何软件包可以做到这一点或者有一个简单的解决方案?
谢谢!
答案1
这两件事都可以用当前的宏\foreach
直接实现。更具体地说,\breakforeach
它允许您中断循环,从而允许您将循环限制在第一个n
条目。并且您可以将列表存储在数组中并访问其元素。请注意,我甚至没有尝试编写新的宏,\forparteach
因为\foreach
它非常微妙,我不敢乱搞它。
\documentclass{article}
\usepackage{pgffor}
\begin{document}
\section*{Cutting a loop off at some number}
\foreach \var [count=\n] in {10,20,30,40,50}
{ \var
\ifnum\n>2
\breakforeach
\fi
}
\section*{Selecting a subset of items from a list/an array}
\def\myarray{{10,20,30,40,50}}
\foreach \var [evaluate=\var as \myindex using {int(\var-1)}] in {1,3,4,2,5,5} {
\pgfmathparse{\myarray[\myindex]}\pgfmathresult
}
\end{document}
答案2
您可以实现自己的 foreach 循环。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\xforeach}{sO{}mm}
{
\IfBooleanTF { #1 }
{
\marks_xforeach:Vnn #3 { #2 } { #4 }
}
{
\marks_xforeach:nnn { #3 } { #2 } { #4 }
}
}
\keys_define:nn { marks/xforeach }
{
upto .int_set:N = \l_marks_xforeach_upto_int,
items .clist_set:N = \l_marks_xforeach_items_clist,
}
\clist_new:N \l__marks_xforeach_main_clist
\seq_new:N \l__marks_xforeach_items_seq
\seq_new:N \l__marks_xforeach_main_seq
\cs_new_protected:Nn \marks_xforeach:nnn
{
\clist_set:Nn \l__marks_xforeach_main_clist { #1 }
\keys_set:nn { marks/xforeach } { upto=-1, items=, #2 }
\cs_set_protected:Nn \__marks_xforeach_cycle:n { #3 }
\seq_clear:N \l__marks_xforeach_items_seq
\clist_if_empty:NTF \l_marks_xforeach_items_clist
{% items not specified
\int_compare:nT { \l_marks_xforeach_upto_int = -1 }
{
\int_set:Nn \l_marks_xforeach_upto_int
{ \clist_count:N \l__marks_xforeach_main_clist }
}
\int_step_inline:nn { \l_marks_xforeach_upto_int }
{
\seq_put_right:Nn \l__marks_xforeach_items_seq { ##1 }
}
}
{% items
\seq_set_from_clist:NN \l__marks_xforeach_items_seq \l_marks_xforeach_items_clist
}
\seq_clear:N \l__marks_xforeach_main_seq
\seq_map_inline:Nn \l__marks_xforeach_items_seq
{
\seq_put_right:Nx \l__marks_xforeach_main_seq
{
\clist_item:Nn \l__marks_xforeach_main_clist { ##1 }
}
}
\seq_map_function:NN \l__marks_xforeach_main_seq \__marks_xforeach_cycle:n
}
\cs_generate_variant:Nn \marks_xforeach:nnn { V }
\ExplSyntaxOff
\begin{document}
\newcommand{\myarray}{10,20,30,40,50}
\xforeach{10,20,30,40,50}{(Item: #1) }
\xforeach*{\myarray}{(Item: #1) }
\xforeach[upto=3]{10,20,30,40,50}{(Item: #1) }
\xforeach*[upto=3]{\myarray}{(Item: #1) }
\xforeach[items={1,3,4,2,5,5}]{10,20,30,40,50}{(Item: #1) }
\xforeach*[items={1,3,4,2,5,5}]{\myarray}{(Item: #1) }
\end{document}
需要注意的几点:与 不同\foreach
,循环不是按组执行的;当前项用 表示,#1
而不是通过指定保存它的宏来表示。 *-variant 接受宏作为第二个参数,而不是显式列表。
人们可以依赖类似括号的存在\foreach
,但在我看来,使用选项和特定命令更清晰。
答案3
您可以使用 来做这些事情xinttools
。\xintFor/\xintFor*
循环不会创建组。另一方面,它们目前没有实现一些很好的\foreach
语法,例如1, 2, ..., 5
。并且没有简单的方法来创建key=value
选择,因为xinttools
它不是像 LaTeX3 提供的成熟编程接口,而是一小段有用的宏(也可以在 Plain TeX 下使用)。
\documentclass{article}
\usepackage{xinttools}
\usepackage{parskip}
\begin{document}\pagestyle{empty}\thispagestyle{empty}
\section*{Cutting a loop off at some number}
First three:
\begin{itemize}
% this uses \xintFor* as argument will be {item1}{item2}....
% i.e. a "list" in the sense of xinttools doc, not a
% "comma separated list"
\xintFor* #1 in {\xintKeep{3}{\xintCSVtoList{10,20,30,40,50}}}
\do
{\item (Item: #1)}
\end{itemize}
Last two:
\begin{itemize}
\xintFor* #1 in {\xintKeep{-2}{\xintCSVtoList{10,20,30,40,50}}}
\do
{\item (Item: #1)}
\end{itemize}
Trim first two:
\begin{itemize}
\xintFor* #1 in {\xintTrim{2}{\xintCSVtoList{10,20,30,40,50}}}
\do
{\item (Item: #1)}
\end{itemize}
Trim last two:
\begin{itemize}
\xintFor* #1 in {\xintTrim{-2}{\xintCSVtoList{10,20,30,40,50}}}
\do
{\item (Item: #1)}
\end{itemize}
Nested keep/trim (keep 2 after having trimmed 2):
\begin{itemize}
\xintFor* #1 in {\xintKeep{2}{\xintTrim{2}{\xintCSVtoList{10,20,30,40,50}}}}
\do
{\item (Item: #1)}
\end{itemize}
\clearpage
\section*{Selecting a subset of items from a list}
Selecting items 1, 3, 4, 2, 5, 5:
% for better efficiency, let's convert comma separated values
% in a "list" (in xinttools terminology) first, once and for all
% because \xintNthElt macro expects such as list
\oodef\MyListOfBracedItems{\xintCSVtoList{10,20,30,40,50}}
\begin{itemize}
% this uses the non-starred \xintFor, which accepts comma separated values
\xintFor #1 in {1, 3, 4, 2, 5, 5}
\do
{\item (Item: \xintNthElt{#1}{\MyListOfBracedItems})}
\end{itemize}
It is also possible to create an ``array'', for better efficiency afterwards:
\xintAssignArray\xintCSVtoList{10, 20, 30, 40, 50}\to\MyArray
\begin{itemize}
% this uses the starred \xintFor, which accepts braced items (or
% single tokens; spaces skipped)
\xintFor* #1 in {1 3 4 2 5 5}
\do
{\item (Item: \MyArray{#1})}
\end{itemize}
\end{document}
第 1 页
第2页