请问有没有办法关闭组范围\foreach
?以下问题说明了我的困难。没有全局分配,目标就无法实现,但全局分配可能会把事情搞得太过分。
安德鲁·斯泰西:这是一个真正的问题。
\documentclass{article}
\usepackage{pgffor}
\begin{document}
\newcount\nra\newcount\nrb
\def\print{%
\par{\tt\tabskip=20pt
\halign to\hsize{##&##&##\cr
\number\nra-\number\nrb:& \xa/\xb & \ya/\yb\cr
}}%
}
\begingroup
\nrb=10
Value of `{\tt\string\nrb}' within the current group is \number\nrb.
\par\medskip
\xdef\savednrb{\the\nrb}
\foreach \xa\xb in {a1/a2,b1/b2,c1/c2,d1/d2}{%
\global\advance\nra by1
\nrb=0
\foreach \ya\yb in {s1/s2,t1/t2,u1/u2,v1/v2}{%
\global\advance\nrb by1
\ifnum\nrb>1 \breakforeach\fi
\print
}%
}
\endgroup
\par\medskip
Value of `{\tt\string\nrb}' outside the latest group is \number\nrb, not \number\savednrb.
\end{document}
答案1
这是我正在构建的解决方案卡托皮通包。在这个方案中,没有硬连线的计算本地范围。接受任意解析器,甚至活动解析器。距离完美的终点线还有一段路要走。尚未提供“评估”和“计数”等用户变量/键的规定。由于没有硬连线的本地组,我们不需要\foreach
这里的“记住”变量。变量“记住”\foreach
实际上应该读作“记住最后”。在目前的方案中,我们需要用以下形式的标记来完成参数n,...,m
。
\documentclass{article}
\usepackage{tikz}
\usepackage{catoptions}
\makeatletter
% \newforeach*[<options>]<holder-cmds>\in<list>\do{<callback>}
%
% The optional <options> include
% 1. Main list parser <parser>; its default value is comma (,).
% 2. Sub list parser <subparser>; its default value is slash (/).
% <subparser> or <sub parser> separates the sublists, eg, slash
% in the following list:
% \x/\y/\z -> a/b/c, d/e/f, g/h/i
% 3. Other options as in pgf's \foreach (to be included).
%
% Both the main parser and sub parser can be active characters,
% unlike in pgf's \foreach.
%
% The optional star (*) suffix, when specified, implies that <listcmd>
% (a macro containing the list) is given in place of <list>.
%
% Any type of argument and delimiter is allowed in \newforeach.
\robust@def*\newforeach{\cpt@teststopt\cpt@foreach@a{}}
\robust@def*\cpt@foreach@a[#1]#2\in#3\do{%
\ifnextcharTF\bgroup{%
\cpt@foreach@c{#1}{#2}{#3}%
}{%
\cpt@foreach@b{#1}{#2}{#3}%
}%
}
\robust@def*\cpt@foreach@b#1#2#3#4;{\cpt@foreach@c{#1}{#2}{#3}{#4}}
\robust@def*\cpt@foreach@c#1#2#3#4{%
% Localize internal macros but not user callbacks:
\begingroup
\cpt@stchoose{cpt@st}{#3}\foreach@list\cpt@foreach@c
\def\foreach@parser{,}%
\def\foreach@subparser{/}%
\def\foreach@normalize##1##2{%
\ifblankTF{##2}{%
\def\normalized@list{}%
}{%
\expandafter\csv@@normalize\expandafter[##1]{##2}%
}%
}%
% Process the keys/variables:
\def\foreach@split##1=##2=##3\@nil{%
\csn@edef{foreach@\cptzapspaces{##1}}{\cpttrimspace{##2}}%
}%
\kv@@normalize{#1}%
\expandafter\cptfor\normalized@list\dofor{%
\foreach@split##1==\@nil
}%
% Normalize the given list for active subparser and spurious spaces:
\cptexpandsecond\foreach@normalize
{{\foreach@subparser}{\expandcsonce\foreach@list}}%
\let\foreach@list\normalized@list
% We need a scanner here, but user callbacks will not be rescanned:
\def\foreach@rescan##1{%
\begingroup
\endlinechar\m@one\newlinechar\m@one
\everyeof{\foreach@nil}%
\def\reserved@a####1\foreach@nil{%
\endgroup\edef##1{\unexpanded{####1}}%
}%
\expandafter\reserved@a\scantokens\expandafter{##1}%
}%
\def\ifforeach@subparser##1{%
\expandafter\ifstrcmpTF\expandafter{\foreach@subparser}{##1}%
}%
% \foreach@holderlistb contains holders defined in terms of the
% parameters of \csv@do; \foreach@holderlista is simply a comma list
% of the holders:
\@tempcnta\z@pt\def\foreach@holderlista{}%
\def\foreach@holderlistb{}\def\foreach@paramlist{}%
% Normalize holder-macro list for active subparsers and spurious
% spaces:
\foreach@normalize\foreach@subparser{#2}%
\expandafter\cpttfor\normalized@list\dofor{%
\ifforeach@subparser{##1}{%
\ifnum\@tempcnta=\z@pt
\cpt@err{First item of \noexpand\newforeach can't
\MessageBreak be a delimiter}\@ehc
\fi
% Insert holder delimiter:
\edef\foreach@paramlist{%
\expandcsonce\foreach@paramlist\unexpanded{##1}%
}%
}{%
\advance\@tempcnta\@ne
\edef\foreach@holderlista{%
\ifcsemptyTF\foreach@holderlista
{}{\expandcsonce\foreach@holderlista,}%
\unexpanded{##1}%
}%
\edef\foreach@holderlistb{%
\expandcsonce\foreach@holderlistb
\edef\unexpanded{##1}{%
\noexpand\cpttrimspace{\cpt@hashchar\number\@tempcnta}%
}%
}%
\edef\foreach@paramlist{%
\expandcsonce\foreach@paramlist\cpt@hashchar\number\@tempcnta
}%
}%
}%
% Save holder macros #2 (eg, \x, \y, \z) so that \newforeach can later
% restore their prior definitions:
\cptexpandargonce{\pushfunctions\newforeach}%
\foreach@holderlista\cpt@csvdepth
\foreach@rescan\foreach@holderlistb
\foreach@rescan\foreach@paramlist
\let\foreach@nil\relax
\def\elt{foreach@do@\romannumeral\cpt@csvdepth}%
\cptexpanded{\endgroup
\def\noexpandcsn\elt\expandcsonce\foreach@paramlist\foreach@nil{%
\expandcsonce\foreach@holderlistb\unexpanded{#4}%
}%
\def\noexpand\csv@do####1{%
\noexpandcsn\elt####1\foreach@nil
}%
\csv@@parse[\foreach@parser]{\expandcsonce\foreach@list}%
}%
% Restore holder macros to their pre-loop values:
\popfunctions\newforeach\cpt@csvdepth
}
\newletcs\breaknewforeach\loopbreak
\makeatother
\begin{document}
% Examples:
\newcount\nra\newcount\nrb
% The parser is semicolon (;) in the outer loop; the subparser is slash (/).
\newforeach[parser=;,sub parser=/] \xa/\ya/\za \in
a1/b1/c1; a2/b2/c2; a3/b3/c3 \do{%
\advance\nra by1 \nrb=0
% \xa, \ya, \za, etc, will be despaced (ie, spurious leading and
% trailing spaces around their contents will be removed internally).
% The parser is comma (,) in the inner loop; the subparser is bar (|).
% The token 'parser={,}' below can be omitted, since comma is the
% default parser:
\newforeach[parser={,},sub parser=|] \xb|\yb|\zb \in
s1|t1|u1, s2|t2|u2, s3|t3|u3 \do{%
\advance\nrb by1
\def\elt{cmda@\romannumeral\nra @\romannumeral\nrb}%
\expandafter\edef\csname\elt\endcsname####1{%
\xa\ya\za*####1*\xb\yb\zb
}%
\ifnum\nrb>1 \breaknewforeach\fi
{\tt\expandafter\meaning\csname\elt\endcsname}\endgraf
}%
}
\par\medskip
% TikZ example:
\begin{tikzpicture}[shading=ball]
\newforeach \x / \cola \in 0/red,1/green,2/blue,3/yellow\do{%
\newforeach \y / \colb \in {0/red,1/green,2/blue,3/yellow}\do{%
\shade[ball color=\cola!50!\colb] (\x,\y) circle (0.4cm);
}%
}
\end{tikzpicture}
% Or
\begingroup
\catcode`\,=13 % active commas
\gdef\listb{0/red, 1/green, 2/blue, 3/yellow}
\endgroup
\tikz[shading=ball]
\newforeach \x/\cola \in 0/red,1/green,2/blue,3/yellow\do{%
\newforeach* \y/\colb \in \listb\do{%
\shade[ball color=\cola!50!\colb] (\x,\y) circle (0.4cm);
}%
};
\begin{tikzpicture}[shading=ball]
% Exercise: why can't we use \tikz[shading=ball] here?
\newforeach[parser=;] \x/\cola \in 0/red; 1/green; 2/blue; 3/yellow\do{%
\edef\cmdx{\x}%
\foreach[remember=\y as \remy (initially 0)] \y/\colb in
{0/red, 1/green, 2/blue, 3/yellow}{%
\edef\cmdy{\y}%
\shade[ball color=\cola!50!\colb] (\x,\y) circle (0.4cm);
}%
}
% \show\cmdx % defined
% \show\cmdy % undefined
\end{tikzpicture}
% Examining \foreach's variable 'remember':
\begin{tikzpicture}[shading=ball]
% \remx is remembered as \x only after the loop, hence only the last
% value (3) is returned in \remx. \savedremx returns repeated entries of initial
% assignment:
\foreach[remember=\x as \remx (initially 0)] \x / \cola in
{0/red,1/green,2/blue,3/yellow}{%
\xdef\savedremx{\ifdefFT\savedremx{}{\ifcsemptyTF\savedremx{}{\savedremx,}}\remx}%
\foreach \y / \colb in {0/red,1/green,2/blue,3/yellow}{%
\shade[ball color=\cola!50!\colb] (\x,\y) circle (0.4cm);
}%
}
%\show\remx
%\show\savedremx
\end{tikzpicture}
%\show\remx
\end{document}