Excellent solution! I like it a lot. Your proposal covers for 1, 2 and 3 beautifully and you've convinced me, that 4 won't be as useful as it will be painful. +vote.
On Wed, Oct 12, 2011 at 6:15 PM, Steven Schveighoffer <[email protected]> wrote: > On Wed, 12 Oct 2011 03:55:36 -0400, Gor Gyolchanyan > <[email protected]> wrote: > >> The foreach loop is truly a marvelous tool, which allows one to >> implement custom iterations, which look and feel just like all other >> kinds of iterations. >> The only philosophical problem with it is, that it thinks that only >> classes and structs can be looped over in a custom way and that they >> can have only one way to be iterated over. >> It would be great to be able to implement iterative algorithms for >> arbitrary types (templated iterations), like strided iteration to >> extract the red part of an image. >> It would also be great to be able to have more, then one kind of >> iteration for every type of iterable. > > You can already do this. > > struct Iterable > { > int opApply(int delegate(ref int) dg) > { > int result = 0; > foreach(int i; 0..100) > { > auto t = i; // I hate this part of opApply BTW. > if((result = dg(t)) != 0) break; > } > return result; > } > > int inReverse(int delegate(ref int) dg) > { > int result = 0; > foreach(int i; 0..100) > { > auto t = 99-i; > if((result = dg(t)) != 0) break; > } > return result; > } > } > > void main() > { > Interable it; > foreach(i; it) {} > foreach(i; &it.inReverse) {} > } > > I've proposed an enhancement to make this better: > > http://d.puremagic.com/issues/show_bug.cgi?id=2498 > >> Here's the list of what I mean by that: >> 1. Allow passing parameters to iterables in foreach: >> foreach(c, i; MyType(), 3) { } >> the `3` would be passed to MyType's opApply right after the >> delegate (of such an overload of opApply is available, of course). > > This might be useful, but I don't like the syntax. > > My preference would be to pass the parameters to the function itself, and > infer the delegate from the foreach body. i.e.: > > struct Iterable > { > int foo(int x, int delegate(ref int) dg) {...} > } > > Iterable it; > foreach(i; it.foo(3)) {...} > > Of course, this syntax is predicated on acceptance of my afore-mentioned > enhancement request. > >> 2. Allow named foreach loops: >> foreach strided(c, i, MyType, 3) { } >> the `strided` is passed as a template parameter to the opApplly, >> which (depending on what overloads of opApply are available) may be >> optional. > > I don't see a large benefit of this over already existing foreach(c, i; > &MyType.strided) > >> 3. Allow free-function opApply, which could be templated to work with >> any kind of iterable in a specific way: >> int opApply(string name : "strided", Iterable)(Iterable iterable, >> int delegate(ForeachParamsTuple!Iterable) dg, size_t stride) { /* ... >> */ } >> this function would allow you to add stride to any iterable. the >> `ForeachParamsTuple` will return the tuple of parameters of the given >> iterable type. > > Again, enhancement 2498 could be used for this. > >> 4. Allow foreach loops with a single iterator to be specified without >> a body, in which case it would return an input range (or some other >> type of range), lazily evaluating and returning the iterator. >> void printRange(Range)(Range range) { foreach(r, range) { writeln(r); >> } }; >> unittest { printRange(foreach(i; 0..100)); } > > This is not a good idea, since translating from an opApply loop to a range > is not possible without spawning a new thread or copying the data. > > The reason is simple -- foreach loops using opApply execute in the context > of the opApply function, they cannot leave that context, and the context > requires full use of the program stack. > > I don't see a huge benefit of doing this vs.: > > foreach(i; 0..100) writeln(i); > > -Steve >
