我一直在想有哪些方法可以循环遍历逗号分隔的列表等。特别是,我想知道它们各自的优缺点。换句话说,我想知道以下这些事情(但不仅限于此列表):
- 无论它们是否可扩展,
- 如何处理空物品,
- 如何处理多余的前导空格和尾随空格
- 您是否可以使用
\def
/\edef
或需要使用\gdef
/\xdef
来保存循环内的信息以供以后使用。
以下是我熟悉的循环方法列表(无特定顺序)。以下列表中\current@item
表示一个宏,接受一个参数,用于格式化迭代中的当前项。
使用来自的命令2ekernal
:
%% \@for
\def\@for@myloop#1{%%
\@for \x:=#1 \do{\current@item \x}}
%% \@tfor (not a comma separated list--probably shouldn't be here)
\def\@tfor@myloop#1{%%
\@tfor \x:=#1 \do{\if,\x\relax\else\current@item \x\fi}}
使用etoolbox
包
%% `etoolbox`: need to be careful whether passed a macro:
%% in that case expansion may be necessary to that the
%% delimiters are visible to `\forcsvlist`
\def\etoolbox@myloop#1{%%
\expandafter\forcsvlist
\expandafter\current@item
\expandafter{#1}}
使用pgffor
包
%% pgffor
\def\pgffor@myloop#1{%%
\foreach \x in {#1} {\current@item \x}}
自制方法:
%% version a la `ae`
\def\@ae@myloop#1,#2\@nil{%%
\current@item{#1}%%
\expandafter\ifx\expandafter\relax\detokenize{#2}\relax\else
\@ae@myloop#2\@nil
\fi
}
\def\ae@myloop#1{%%
\@ae@myloop#1,\@nil
}
然后还有各种expl3
方法,例如(有很多,因此这并不详尽):
\tl_map_inline:nn
\tl_map_function:Nn
\clist_map_inline:Nn
\seq_map_inline:Nn
下面是一个 MWE,说明了每个结果(包括一个 LaTeX3 版本):
\documentclass{article}
\usepackage[margin=0.5in,paperheight=15in]{geometry}
\usepackage{xparse}
\usepackage{etoolbox}
\usepackage{pgffor}
\usepackage{enumitem}
\makeatletter
\def\my@item@count{0}
\def\step@my@counter{\xdef\my@item@count{\number\numexpr\my@item@count+1\relax}}
%% \rules to emphasize how spaces are seen and treated!
\def\current@item#1{\step@my@counter\item \rule{0.4pt}{2ex}#1\rule{0.4pt}{2ex}}
%% `etoolbox`
\def\etoolbox@myloop#1{%%
\forcsvlist \current@item {#1}}
%% but if passed a macro, then it first needs to be expanded so the delimiters are visible to `\forcsvlist`. You'll need to write
%% \expandafter\forcsvlist \expandafter\current@item \expandafter{#1}}
%% \@for
\def\@for@myloop#1{%%
\@for \x:=#1 \do{\current@item \x}}
%% \@tfor
\def\@tfor@myloop#1{%%
\@tfor \x:=#1 \do{\current@item \x}}
%% pgffor
\def\pgffor@myloop#1{%%
\foreach \x in {#1} {\current@item \x}}
%% version a la `ae`
\def\@ae@myloop#1,#2\@nil{%%
\current@item{#1}%%
\expandafter\ifx\expandafter\relax\detokenize{#2}\relax\else
\@ae@myloop#2\@nil
\fi
}
\def\ae@myloop#1{%%
\@ae@myloop#1,\@nil
}
\def\listoffruit#1#2#3{%%
\def\my@item@count{0}%%
\noindent
List of type \texttt{#1}: \parbox[t]{3in}{\raggedright#3}
\begin{itemize}[topsep=4pt,itemsep=2pt]
\csname#1@myloop\endcsname{#2}%%
\end{itemize}
Total number of bulleted items: \my@item@count
\par \vspace{2ex}\hrule\par \vspace{2ex}
}
\ExplSyntaxOn
\NewDocumentCommand{\expl@myloop}{ m }
{
\clist_map_inline:nn{#1}{\current@item {##1}}
}
\ExplSyntaxOff
\makeatother
\def\apples{apples}
\def\bananas{bananas}
\def\cherries{cherries}
\begin{document}
\listoffruit{etoolbox}{apples,, oranges, bananas ,cherries}{Ignores leading spaces and empty items. Trailing spaces not ignored.}
\listoffruit{@for}{apples,, oranges, bananas ,cherries}{Empty lines and trailing or leading spaces not ignored. Something goes on with last item in list.}
\listoffruit{@tfor}{\apples,{} {oranges}\bananas\cherries}{Spaces ignored, all other token respected, bracketed tokens treated as one.}
\listoffruit{pgffor}{apples,, oranges, bananas ,cherries}{Ignores leading spaces, empty items and trailing spaces not ignored.}
\listoffruit{ae}{apples,, oranges, bananas ,cherries}{Leading or trailing spaces not ignored. Empty items not ignored.}
\listoffruit{expl}{apples,, oranges, bananas ,cherries}{Trailing or leading spaces ignored. Empty items ignored.}
\end{document}
我所知道的问题:
- 我相信
\@for
是不可扩展的, \cslist_map_inline:nn
可扩展但有限制:即它不能在f
-type 参数中扩展。pgffor
的\foreach
循环在组内执行。因此您需要使用\gdef
或\xdeg
保存组内的信息以供日后使用。我还没有完全探索这里介绍的其他哪些循环具有类似的缺点(不确定这是否是正确的措辞)。我不知道它是否\foreach
可扩展。- 某些方法可以很好地处理列表,无论是通过显式传递还是通过宏隐式传递。例如,知道
pgffor
如何\foreach
处理通过宏传递的列表。 需要先扩展该宏:这就是我对 的第一个说明如此复杂的原因。etoolbox
\forcsvlist
\forcslist
所以我感兴趣的是:
- 这些回应针对了本文开头列出的不同方法的优缺点,但不一定仅限于这些建议,因为我可能不知道其他重要问题。例如,我不确定哪些是可扩展的。
- 回应中介绍了迭代项目列表的其他方法以及它们已知的弱点和优势。
- 可以说明现实示例的响应,人们希望在可扩展的上下文中使用此类循环
- 这些回应说明了如何保存从小组内部收集的信息以供日后使用。
答案1
我建议\current@item
\def\current@item#1{%
\stepcounter{item@count}
\item $|$#1$|$\ $|$\texttt{\detokenize{#1}}$|$%
}
因此输出还显示了实际作为其参数传递的内容。我还用\my@item@count
一个简单的计数器改变了复杂的管理。我不评论\@tfor
,这是一个不同的工具,不是为逗号分隔列表设计的。
etoolbox
:\forcsvlist
不会删除尾随空格;你的定义太复杂了,因为%% `etoolbox` \def\etoolbox@myloop#1{%% \forcsvlist\current@item{#1}}
足够了。项目已明确传递。
\@for
是 LaTeX 内核中定义的基本工具。前导和尾随空格不会被删除。项目作为 传递\x
,即 之后使用的控制序列\@for
。\foreach
不删除尾随空格。项目作为 传递\x
,即 之后使用的控制序列\foreach
。ae
基本上类似于\@for
,尽管它是通过扩展来工作的。前导和尾随空格不会被删除。项目被明确传递。expl
删除前导和尾随空格,以及空项;项被明确传递。
\documentclass{article}
\usepackage[margin=0.5in,paperheight=15in]{geometry}
\usepackage{xparse}
\usepackage{etoolbox}
\usepackage{pgffor}
\usepackage{enumitem}
\makeatletter
\newcounter{item@count}
%% \rules to emphasize how spaces are seen and treated!
\def\current@item#1{%
\stepcounter{item@count}
\item $|$#1$|$\ $|$\texttt{\detokenize{#1}}$|$%
}
%% `etoolbox`
\def\etoolbox@myloop#1{%%
\forcsvlist\current@item{#1}}
%% \@for
\def\@for@myloop#1{%%
\@for \x:=#1\do{\current@item \x}}
%% pgffor
\def\pgffor@myloop#1{%%
\foreach \x in {#1} {\typeout{pgf:\x}\current@item \x}}
%% version a la `ae`
\def\@ae@myloop#1,#2\@nil{%%
\current@item{#1}%%
\if\relax\detokenize{#2}\relax\else
\@ae@myloop#2\@nil
\fi
}
\def\ae@myloop#1{%%
\@ae@myloop#1,\@nil
}
\def\listoffruit#1#2{%%
\setcounter{item@count}{0}%%
\noindent
List of type \texttt{#1}
\begin{itemize}[topsep=4pt,itemsep=2pt]
\csname#1@myloop\endcsname{#2}%%
\end{itemize}
Total number of bulleted items: \arabic{item@count}%
\par \vspace{2ex}\hrule\par \vspace{2ex}
}
\ExplSyntaxOn
\NewDocumentCommand{\expl@myloop}{ m }
{
\clist_map_inline:nn{#1}{\current@item {##1}}
}
\ExplSyntaxOff
\makeatother
\begin{document}
\listoffruit{etoolbox}{apples,, oranges, bananas ,cherries}
\listoffruit{@for}{apples,, oranges, bananas ,cherries}
\listoffruit{pgffor}{apples,, oranges, bananas ,cherries}
\listoffruit{ae}{apples,, oranges, bananas ,cherries}
\listoffruit{expl}{apples,, oranges, bananas ,cherries}
\end{document}
我会添加另一个expl3
循环,它也会考虑空项目:
\ExplSyntaxOn
\NewDocumentCommand{\seq@myloop}{ m }
{
\seq_set_split:Nnn \l_tmpa_seq { , } { #1 }
\seq_map_inline:Nn \l_tmpa_seq { \current@item {##1} }
}
\ExplSyntaxOff
(当然,\l_tmpa_seq
不鼓励使用,最好分配一个新变量)。
对比结果应该没有疑问。 唯一\foreach
优越的地方是它对“不完全列表”的处理,例如1,2,...,20
。
答案2
LuaLaTeX:
\documentclass{standalone}
\usepackage{tikz}
\usepackage{filecontents}
\begin{document}
\newcommand{\drawCircle}[1]{\tikz \draw (0,0) circle (#1);}
\begin{filecontents*}{testlua.lua}
for i=1,10 do
tex.print("\\drawCircle{",0.1*i,"}")
end
\end{filecontents*}
\directlua{dofile('testlua.lua')}
\end{document}