The future of foreach

Hxal hxal at freenode.d.channel
Sun Dec 23 05:29:19 PST 2007


Janice Caron Wrote:

> Walter has stated many times that foreach is a good thing because it
> expresses the programmer's intent, and leaves the optimisation down to
> the compiler. (Should it use pointers? Should it use indeces? etc.)
> 
> I agree with him.
> 
> However, it is sadly flawed in that you can't iterate through two
> collections in lockstep. I'm sure that many suggestions have been
> proposed in the past to work around this limitation, but the bottom
> line has always been that we're stuck with opApply(), and opApply()
> cannot be made to loop through two things at once.
> 
> So...
> 
> I'd like to suggest a /gradual/ change. It seems to me that this would
> work without really hurting anything, and programmers could get used
> to new idioms a little bit at a time.
> 
> STEP ONE - Make it work for built-in arrays /only/
> 
> This one seems pretty straightforward. For built-in arrays, we allow
> people to do this:
> 
>     int[] a, b, c;
>     foreach(ref x;a)(y;b)(z;c) { x = y * z; }
> 
> This should present the compiler with no difficulty, because we're
> /only/ talking about builtin arrays here, and so there's no opApply()
> to worry about.
> 
> This will also give us coders a chance to play with it and get used to
> the idiom.
> 
> At this point, /some/ structs and classes will be able to add their
> own elementwise features simply by providing a function which returns
> an array. For example:
> 
>     Vector!(10,int) a,b,c;
>     foreach(ref x;a.toArray)(y;b.toArray)(z;c.toArray) { x = y * z; }
> 
> It's not perfect (yet), but it's a step in the right direction.
> 
> STEP TWO - Allow foreach to recurse into multidimensional arrays
> 
> This is a pretty nice one.
> 
>     int[][] a;
>     foreach(int[] x;a) { /*elements of a*/ }
>     foreach(int x; a) { /* elements of elements of a*/ }
> 
> Now we'll be able to add elementwise features to even more structs and
> classes. For example:
> 
>     Matrix!(10,10,int) a,b,c;
>     foreach(ref int x;a.toArray)(int y;b.toArray)(int z;c.toArray) { x
> = y * z; }
> 
> (Yes, I'm aware that that's not doing matrix multiplication, but
> apparently there is a need to do this). Again, it's not perfect (yet),
> but it's moving just a little bit closer.
> 
> STEP THREE - Extend these features to "array-like types".
> 
> If we consider an "array-like type" to be any class or struct which implements:
> 
>     opIndex()
>     opIndexAssign()
>     length()
> 
> and/or
> 
>     ptr()
>     end()
> 
> (with the latter two returning iterators), then I see no reason why
> these features couldn't also be made to work with arbitrary
> collections. The rule would be:
> 
>     (1) if we implement opIndex(), opIndexAssign() and length(), use those, else
>     (2) if we implement ptr() and end(), use those, else
>     (3) if we implement opApply(), use that (with all the old limitations), else
>     (4) compile-time error
> 
> Of course, we don't have iterators yet, so I should probably have
> added step 2.5, finish implementiing iterators. We already have /most/
> of what iterators need: opEquals(), opPostInc(), opPostDec() and
> opStar() (hopefully to be renamed opDeref()). I think we're still
> missing opStarAssign() / opDerefAssign(), but once that's in place
> we'd be good to go.
> 
> Once step three is in place, structs and classes will no longer need a
> toArray() function, and (better still) the mechanism will work even
> for collections which /can't/ return an array, such as linked lists.
> At this point we'll be able to do
> 
>     List!(Widget) a,b,c;
>     foreach(ref x;a)(y;b)(z;c) { a = b.someFunction(c); }
> 
> Thoughts?

What you're suggesting is perfectly possible to do without any language changes.
See ZipIterator in http://zygfryd.net/hg/jive/file/tip/jive/iterators.d ;
while you can't iterate two opApply-exposing objects without using inefficient
buffering you can always implement some init()/next() protocol in your types
and make a special case for it in iterator adaptors like zip/lockstep.
(In the example such a special case is implemented for TangoIterator).




More information about the Digitalmars-d mailing list