pgffor:对 \foreach-list 中最后一项进行特殊处理

pgffor:对 \foreach-list 中最后一项进行特殊处理

\foreach有没有一种方法可以以特殊方式处理 -list 中的最后一项。以下示例可以说明此方法的应用:

\documentclass{minimal}
\usepackage{pgffor}
\begin{document}
\verb|\foreach \n in {1,...,5}{\n --}| leads to
\foreach \n in {1,...,5}{\n --} better would be
1--2--3--4--5 without the last --

A bad workaround: \foreach \n/\l in {1/--,2/--,3/--,4/--,5/{}}{\n\l}
or \foreach \n in {1--,2--,3--,4--,5}{\n}

% unfortunatly
% \foreach \n/\l in {1/--,...,4/--,5/{}}{\n\l}
% or \foreach \n in {1--,...,4--,5}{\n}
% doesn't work
\end{document}

答案1

使用count选项\foreach,可以得到一个存储列表中位置的计数器。由于列表中元素的数量可能未知,因此通常更容易检查第一个元素而不是最后一个元素。这是一个例子。(请注意,在 v2.10 中,count当仅包含 时,该选项不起作用pgffor,因为它缺少\pgfmathparse。所以你也需要包含pgfmath(然后抱怨缺少pgfkeys,所以我只是在示例中包含了所有内容)。)

\documentclass{minimal}
\usepackage{pgf,pgffor}

\begin{document}
\foreach \n [count=\ni] in {a,...,e} {% Comment signs at end of line to avoid
    \ifnum\ni=1%                        unwanted whitespace.
        \n%
    \else%
        --\n%
    \fi%
}
\end{document}

或者你也可以直接写

a%
\foreach \n in {b,...,e} {--\n}

答案2

子部分点符号章节56. 重复操作:Foreach 语句pgfmanual v2.10(第 504-505 页)有大量示例。其中一个允许您执行以下操作:

\documentclass{minimal}
\usepackage{pgffor}
\begin{document}
\foreach \n in {1--, ...--, 4--, 5}{\n}
\end{document}

答案3

虽然这里的其他答案提供了完成手头任务的解决方案,但它们并没有真正解决标题中的问题。我在解决另一个问题时遇到了这个问题,但很失望这里没有解决方案展示如何对列表的最后一个成员进行特殊处理。因此我把它包括在这里以防别人真的需要它。也许有更优雅的解决方案的人会被鼓励发布一个。

我必须承认这不是很优雅,但只是蛮力方法在列表处理开始时计算成员数量,并保持计数以查看我们处于哪个列表成员。这允许对第一的中间, 和最后的列表成员。因此\ColorListMembers{first,second,third,fourth}我可以获得:

在此处输入图片描述

笔记:

  • 我在用包裹xstring对于数值比较,因为我更喜欢它的语法,但这可以很容易地适应而不需要该包。

参考:

代码:

\documentclass[border=2pt]{standalone}
\usepackage{xcolor}
\usepackage{pgffor}
\usepackage{xstring}


% Specify the formatting of each of the list members
\newcommand*{\FormatFirstListMember}[1]{\textcolor{magenta}{\texttt{#1}}}%
\newcommand*{\FormatMiddleListMember}[1]{\textcolor{blue}{\texttt{#1}}}%
\newcommand*{\FormatLastListMember}[1]{\texttt{#1}}%


\newcounter{TotalNumberOfListMembers}%
\newcommand{\SetTotalNumberOfListMembers}[1]{%
    \setcounter{TotalNumberOfListMembers}{0}%
    \foreach \member in {#1} {%
        \stepcounter{TotalNumberOfListMembers}%
    }%
}%


\newcounter{CurrentListMemberCount}%
\newcommand*{\ColorListMembers}[1]{%
    \SetTotalNumberOfListMembers{#1}%
    \setcounter{CurrentListMemberCount}{0}%
    \foreach \member in {#1} {%
        \stepcounter{CurrentListMemberCount}%
        \IfEq{\the\value{CurrentListMemberCount}}{1}{%
            \FormatFirstListMember{\member}%
        }{%
            \IfEq{\the\value{CurrentListMemberCount}}{\the\value{TotalNumberOfListMembers}}{%
                --\FormatLastListMember{\member}%
            }{%
                --\FormatMiddleListMember{\member}%
            }%
        }%
    }%
}%

\begin{document}
    \ColorListMembers{first,second,third,fourth}
\end{document}

答案4

Caramdir 的答案的一个温和的变体(或多或少等同于替代方案):

\documentclass{article}
\usepackage{pgffor}
\newcommand*\minusminus[1]{--#1}
\newcommand*\initial[1]{#1\global\let\doit=\minusminus}
\let\doit=\initial
\begin{document}
 \foreach \n in {1,...,5} {%
  \doit{\n}%
 }
\end{document}

\doit在第一次调用后重新定义自身,然后添加短划线。这有点邪恶,但我想到的是调用一个函数指针,其目标被替换,这没那么邪恶。

编辑:这是一个实际上特殊处理最后一项的解决方案。

\documentclass{article}
\usepackage{pgffor,etoolbox}
\newcommand*\last{}
\newcommand*\storage{}
\newcommand*\doit[1]{%
 \xdef\storage{\expandonce\storage\noexpand\transform{\last}}%
 \xdef\last{#1}%
}
\newcommand*\finalize{\storage\last}
\newcommand*\minusminus[1]{#1--}
\newcommand*\initial[1]{#1\global\let\transform=\minusminus}
\let\transform=\initial
\begin{document}
 \foreach \n in {1,...,5} {
  \doit{\n}
 }
 \finalize
\end{document}

发生的事情是,每次迭代都会\doit保存其参数,然后添加以前的参数(已保存)添加到\storage列表中,并附带一个对其进行转换的指令。在最后一次迭代中,参数被保存但从未添加,这是在 中完成的\finalize,但没有进行转换。

请注意,正如艾哈迈德·穆萨 (Ahmed Musa) 所描述的那样,即使在这种方法中,也会出现“卡莱尔魔杖”的外观。第一个(即第零个)\last应该被忽略。您甚至可以将其加倍(即,将\transform自身重新定义为其他东西,然后重新定义自身),以使第一个项目(或实际上任何特定数量的初始项目)表现出不同的行为。当然,您也可以对最后一个项目做一些事情,而不是像我一样什么都不做。

据我所知,从数学角度来说,这\finalize一步是必要的。你只有越过列表后才能知道自己已经到达列表末尾,因此每一项都必须在后面处理,尤其是最后一项必须“手动”处理。这是我能做到的最自动化的程度。

编辑:回应 Peter Grill 的评论:实际上有一种方法可以做到\AtEndForeach,尽管它不太好。我不知道为什么没有机制来设置它,因为它在代码中是无操作的:

\makeatletter
 \let\pgffor@afterhook=\finalize
\makeatother

我想你可以将其设为宏:

\makeatletter
\newcommand\AtEndForeach{%
 \def\pgffor@afterhook
}
\makeatother

\AtEndForeach{\finalize}
\foreach \n in {1,...,5} {
 \doit{\n}
}

请注意一个\pgffor@atendforeach,但是有目的;它似乎要完成remember关键选项的操作。您需要 after 钩子。

相关内容