
我一直想知道可用於循環逗號分隔清單等的各種方法。我特別想知道他們的各種優點和缺點。換句話說,我想知道以下內容(但不限於此列表):
- 無論它們是否可擴展,
- 他們如何處理空物品,
- 他們如何處理無關的前導和尾隨空格
- 是否可以使用
\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
是可擴展的,但有限制:即,它在 -type 參數中不可擴展f
。pgffor
的\foreach
循環在組內執行。因此,您需要使用\gdef
或\xdeg
儲存群組內的資訊以供日後使用。我還沒有完全探索這裡介紹的其他哪些循環具有類似的缺點(不確定這個字的選擇是否正確)。不知道\foreach
可不可以擴充。- 有些方法可以很好地處理它們的列表,無論是透過巨集明確傳遞還是隱式傳遞。例如,
pgffor
's\foreach
知道如何處理透過巨集傳遞的清單。etoolbox
需要\forcsvlist
先擴展該巨集:因此我對 a 的第一個插圖\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}