foreach range with index

Steven Schveighoffer via Digitalmars-d digitalmars-d at puremagic.com
Tue Jun 13 14:44:43 PDT 2017


On 6/13/17 1:15 PM, Luís Marques wrote:
> In the documentation for foreach with range concepts (not x..y ranges)
> (<http://dlang.org/spec/statement.html#foreach-with-ranges>) I did not
> find anything about foreach supporting iteration indices/tuple unpacking
> for ranges, such as `foreach(i, elm; range)`. It is "documented" in an
> example for std.range.enumerate though [1].
>
> I found a bug report asking for that to be documented [2] and one saying
> that it should be removed [3].
>
> One of the things that I like in D is the plasticity of the code. In the
> situation that brought me to this issue, I started with a plain slice
> which I iterated with foreach(i, foo; foos). Then `foos` became a range,
> and the foreach became foreach(i, foo; foos.enumerate); Thus, I didn't
> have to restructure my foreach iteration, moving the index variable
> outside the foreach statement, and manually incrementing the index,
> making this change less disruptive.
>
> Some thoughts about this:
>
> - It doesn't strike me as very realistic to remove support for
> foreach(i, e; range.enumerate) now, as probably a lot of people are
> relying on this. I find it very useful, especially as currently there
> doesn't seem to be a good alternative.

We aren't going to as far as I can tell. I closed that bug report.

> - The plasticity was not perfect in my example; ideally I wouldn't have
> to change the foreach statement at all (i.e., add ..enumerate).
> Consider: my range had random access, why couldn't foreach itself
> generate the index, as it does for slices, instead of relying on
> .enumerate and tuple unpacking? Maybe it would even make sense for input
> ranges, I'm not sure.

The issue here is the difference between foreach on arrays (which has 
nothing to do with ranges), and foreach on a range. In the former, the 
array index is wholly a concept of the foreach loop itself. The compiler 
inserts and maintains the index in the loop, as the array structure has 
no storage or maintenance of an index.

In the latter, the index is maintained by the range, and so stored 
there. AND it's copied and stored as a temporary for your loop via the 
unpacking of the tuple.

I feel a change in the way foreach and ranges interact may be needed to 
make things more efficient and less awkward. opApply has some really 
nice features, one of them being that you can declare loop-specific data 
such as an index, and another being that foreach'ing an opApply 
structure with different types/numbers of parameters works just fine.

But I think leaving the definition of the index up to the range itself 
is paramount -- I don't want every range to be able to have a size_t 
index, as that's not always what you want, and it conflicts with other 
items. What we may need is a smarter way to get from the type parameters 
at the front of the foreach to an iterable type.

> - Since foreach with ranges and unpacking (i.e., indices when using
> .enumerate) seems here to stay for now, please document it. The code I
> was writing was supporting material for an article. But I'm afraid to
> advocate an approach which relies on behavior which is undocumented and
> which people are arguing should be removed. Not exactly confidence
> inspiring...

It should be documented, 100%.

-Steve


More information about the Digitalmars-d mailing list