生成 for 循环的各种方法有何区别?

生成 for 循环的各种方法有何区别?

我一直在想有哪些方法可以循环遍历逗号分隔的列表等。特别是,我想知道它们各自的优缺点。换句话说,我想知道以下这些事情(但不仅限于此列表):

  1. 无论它们是否可扩展,
  2. 如何处理空物品,
  3. 如何处理多余的前导空格和尾随空格
  4. 您是否可以使用\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. 这些回应针对了本文开头列出的不同方法的优缺点,但不一定仅限于这些建议,因为我可能不知道其他重要问题。例如,我不确定哪些是可扩展的。
  2. 回应中介绍了迭代项目列表的其他方法以及它们已知的弱点和优势。
  3. 可以说明现实示例的响应,人们希望在可扩展的上下文中使用此类循环
  4. 这些回应说明了如何保存从小组内部收集的信息以供日后使用。

答案1

我建议\current@item

\def\current@item#1{%
  \stepcounter{item@count}
  \item $|$#1$|$\ $|$\texttt{\detokenize{#1}}$|$%
}

因此输出还显示了实际作为其参数传递的内容。我还用\my@item@count一个简单的计数器改变了复杂的管理。我不评论\@tfor,这是一个不同的工具,不是为逗号分隔列表设计的。

  1. etoolbox\forcsvlist不会删除尾随空格;你的定义太复杂了,因为

    %% `etoolbox`
    \def\etoolbox@myloop#1{%%
      \forcsvlist\current@item{#1}}
    

    足够了。项目已明确传递。

  2. \@for是 LaTeX 内核中定义的基本工具。前导和尾随空格不会被删除。项目作为 传递\x,即 之后使用的控制序列\@for

  3. \foreach不删除尾随空格。项目作为 传递\x,即 之后使用的控制序列\foreach

  4. ae基本上类似于\@for,尽管它是通过扩展来工作的。前导和尾随空格不会被删除。项目被明确传递。

  5. 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}

相关内容