Here is a solution I am building into catopitons package. There is no hardwired local scoping of computations in this scheme. Arbitrary parsers, even active parsers, are accepted. There is still some way to the perfect finish line. The provision for user variables/keys such as 'evaluate' and 'count' are yet to be provided. Since there are no hardwired local groups, we don't need the 'remember' variable of \foreach here. The variable 'remember' of \foreach should actually read 'remember last'. In the present scheme we need to complete arguments with tokens of the form 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}


\foreachmacro is much more versatile than any looping macro in the LaTeX kernel, except for the fact that it locally scopes assignments. It does that for good reason. Two obvious motives might be to minimize the number of globally defined macros and to avoid redefining existing macros. – Ahmed Musa May 05 '12 at 23:27\globaldefs=-1?\foreach \xa\xb in {a1/a2,b1/b2,c1/c2,d1/d2}{% \globaldefs=-1 \advance\nra by1 \nrb=0 \foreach \ya\yb in {s1/s2,t1/t2,u1/u2,v1/v2}{% \advance\nrb by1 \ifnum\nrb>1 \breakforeach\fi \print }% }– Marco Daniel May 06 '12 at 18:35\globaldefsis positive every assignment is implicitly prefixed with\global, and if\globaldefsis negative,\globalis ignored. Ordinarily this parameter is zero. – Ahmed Musa May 06 '12 at 18:40\globaldefsparameter is positive at the time of the assignment, a prefix of\globalis automatically implied; -- Does it work? – Marco Daniel May 06 '12 at 18:43\globaltakes assignments across ALL existing groups. I wanted the assignment restricted to my own groups, which wasn't possible since\pgfforeachcompelled me to prefix the needed assignments with\global.\globaldefshas no role in that case. – Ahmed Musa May 06 '12 at 20:04