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