www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Ranges and indexes with foreach

reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
The lack of indexing for foreach and ranges is a bit of problem (not a huge 
one but definitely an annoying one - it's one of the few reasons to still use 
opApply instead of ranges). The obvious solution is to just have the compiler 
provide a counter. So,

foreach(i, e; range)
{
 //code
}

gets lowered to something like

foreach(size_t i = 0; !range.empty; ++i, range.popFront())
{
 auto e = range.front;
 //code
}

But as I understand it, some have been opposed to that idea due to ranges 
which might have more exotic iteration schemes. If that's the case, would it 
make sense to do the above but then allow for ranges which returned their next 
index from popFront? i.e.

foreach(size_t i = 0; !range.empty; i = range.popFront())
{
 auto e = range.front;
 //code
}

So, in the rare cases where a more control of the iteration scheme is 
desirable, instead of being void, popFront would return size_t where that 
size_t is the next index. Is there a reason why this wouldn't do the trick?

I think that we'd definitely benefit by allowing the first case regardless, but 
if the second solves the problem of the index not being flexible enough, then 
presumably that would make this solution for indexing with ranges more 
acceptable.

Thoughts? Is there anything obvious (or non-obvious) that I'm missing here?

- Jonathan M Davis
Jan 23 2012
parent reply bearophile <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 The lack of indexing for foreach and ranges is a bit of problem (not a huge 
 one but definitely an annoying one - it's one of the few reasons to still use 
 opApply instead of ranges). The obvious solution is to just have the compiler 
 provide a counter.
 ...
 Thoughts? Is there anything obvious (or non-obvious) that I'm missing here?
Time ago I have proposed this, I think this is a tidier solution, that requires no compiler changes: http://d.puremagic.com/issues/show_bug.cgi?id=5550 (and then the front-end is free to recognize the usage of this Phobos range enumerate(), and optimize it much better). Usage example (this also uses the tuple unpacking syntax): foreach ((i, e); enumerate(range)) { // code that uses i and e } Until the tuple unpacking syntax is not present you use: foreach (ie; enumerate(range)) { const i = ie[0]; const e = ie[1]; // code that uses i and e } Bye, bearophile
Jan 23 2012
parent reply kenji hara <k.hara.pg gmail.com> writes:
2012/1/24 bearophile <bearophileHUGS lycos.com>:
 Jonathan M Davis:

 The lack of indexing for foreach and ranges is a bit of problem (not a h=
uge
 one but definitely an annoying one - it's one of the few reasons to stil=
l use
 opApply instead of ranges). The obvious solution is to just have the com=
piler
 provide a counter.
 ...
 Thoughts? Is there anything obvious (or non-obvious) that I'm missing he=
re?
 Time ago I have proposed this, I think this is a tidier solution, that re=
quires no compiler changes:
 http://d.puremagic.com/issues/show_bug.cgi?id=3D5550

 (and then the front-end is free to recognize the usage of this Phobos ran=
ge enumerate(), and optimize it much better).
 Usage example (this also uses the tuple unpacking syntax):

 foreach ((i, e); enumerate(range)) {
 =A0 =A0// code that uses i and e
 }


 Until the tuple unpacking syntax is not present you use:

 foreach (ie; enumerate(range)) {
 =A0 =A0const i =3D ie[0];
 =A0 =A0const e =3D ie[1];
 =A0 =A0// code that uses i and e
 }

 Bye,
 bearophile
Today, foreach can expand the front tuple automatically. foreach (i, e; zip(sequence!"n", range)) { // i =3D 0, 1, 2, ... // e =3D elements of range } So extra unpacking syntax is not need. Kenji Hara
Jan 23 2012
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
kenji hara:

 Today, foreach can expand the front tuple automatically.
 
 foreach (i, e; zip(sequence!"n", range))
 {
     // i = 0, 1, 2, ...
     // e = elements of range
 }
 
 So extra unpacking syntax is not need.
Very good, I didn't know it. This works: import std.stdio, std.range, std.algorithm; void main() { auto range = [1, 2, 3, 4]; foreach (i, e; zip(sequence!"n"(), range)) writeln(i, " ", e); } So it's better to update the zip usage example in the docs: http://www.dlang.org/phobos/std_range.html#zip That currently is (plus newlines that don't get copied in copying&pasting from the site pages): int[] a = [ 1, 2, 3 ]; string[] b = [ "a", "b", "c" ]; // prints 1:a 2:b 3:c foreach (e; zip(a, b)) { write(e[0], ':', e[1], ' '); } But going against what Walter usually says, some shallow higher order functions are not useless in Phobos, so: enumerate(range) is quite better than this for programmers: zip(sequence!"n"(), range) (And maybe it's simpler for a D front-end to recognize the enumerate() and optimize it better). Bye, bearophile
Jan 23 2012
parent reply bearophile <bearophileHUGS lycos.com> writes:
 import std.stdio, std.range, std.algorithm;
 void main() {
     auto range = [1, 2, 3, 4];
     foreach (i, e; zip(sequence!"n"(), range))
         writeln(i, " ", e);
 }
But I was talking about tuple unpacking, not typetuple unpacking: import std.stdio, std.range, std.algorithm, std.typecons; alias Tuple!(int,int) P; void main() { auto ap = [P(1,2), P(3,4), P(5,6)]; foreach ((x, y); ap) // <== std.typecons.tuple unpacking writeln(x, " ", y); } Bye, bearophile
Jan 23 2012
parent reply kenji hara <k.hara.pg gmail.com> writes:
2012/1/24 bearophile <bearophileHUGS lycos.com>:
 import std.stdio, std.range, std.algorithm;
 void main() {
 =A0 =A0 auto range =3D [1, 2, 3, 4];
 =A0 =A0 foreach (i, e; zip(sequence!"n"(), range))
 =A0 =A0 =A0 =A0 writeln(i, " ", e);
 }
But I was talking about tuple unpacking, not typetuple unpacking:
std.range.zip makes a range of std.typecons.Tuple, not std.typetuple.TypeTu= ple. Then foreach statement supports the std.typecons.Tuple unpacking of range.f= ront.
 import std.stdio, std.range, std.algorithm, std.typecons;
 alias Tuple!(int,int) P;
 void main() {
 =A0 =A0auto ap =3D [P(1,2), P(3,4), P(5,6)];
 =A0 =A0foreach ((x, y); ap) =A0// <=3D=3D std.typecons.tuple unpacking
 =A0 =A0 =A0 =A0writeln(x, " ", y);
 }
Therefore, follows would work. =A0 =A0auto ap =3D [P(1,2), P(3,4), P(5,6)]; =A0 =A0foreach (x, y; ap) =A0// =A0 =A0 =A0 =A0writeln(x, " ", y); Kenji Hara
Jan 23 2012
parent reply bearophile <bearophileHUGS lycos.com> writes:
kenji hara:

 std.range.zip makes a range of std.typecons.Tuple, not std.typetuple.TypeTuple.
 Then foreach statement supports the std.typecons.Tuple unpacking of
range.front.
Ah, right.
 Therefore, follows would work.
 
     auto ap = [P(1,2), P(3,4), P(5,6)];
     foreach (x, y; ap)  //
         writeln(x, " ", y);
This program: import std.stdio, std.range, std.algorithm, std.typecons; alias Tuple!(int,int) P; void main() { auto ap = [P(1,2), P(3,4), P(5,6)]; foreach (x, y; ap) writeln(x, " ", y); } To me outputs: 0 Tuple!(int,int)(1, 2) 1 Tuple!(int,int)(3, 4) 2 Tuple!(int,int)(5, 6) Instead of: 1 2 3 4 5 6 If you have an array, foreach puts the index of the items at the first variable. To tell apart tuple unpacking from normal array indexing I have suggested a syntax like: foreach ((x, y); ap) If you fear programmers will miss the (), then requiring something like an auto solves the problem: foreach (auto (x, y); ap) Bye, bearophile
Jan 23 2012
parent reply kenji hara <k.hara.pg gmail.com> writes:
Ok, I understand.

The syntax is similar to my "multiple var declaration" proposal.
http://d.puremagic.com/issues/show_bug.cgi?id=3D6365
https://github.com/D-Programming-Language/dmd/pull/341

If the enhancement would be accepted, I'd like to implement your
proposal to increase consistency.

Kenji Hara

2012/1/24 bearophile <bearophileHUGS lycos.com>:
 kenji hara:

 std.range.zip makes a range of std.typecons.Tuple, not std.typetuple.Typ=
eTuple.
 Then foreach statement supports the std.typecons.Tuple unpacking of rang=
e.front.
 Ah, right.


 Therefore, follows would work.

 =A0=A0 =A0auto ap =3D [P(1,2), P(3,4), P(5,6)];
 =A0=A0 =A0foreach (x, y; ap) =A0//
 =A0=A0 =A0 =A0 =A0writeln(x, " ", y);
This program: import std.stdio, std.range, std.algorithm, std.typecons; alias Tuple!(int,int) P; void main() { =A0 =A0auto ap =3D [P(1,2), P(3,4), P(5,6)]; =A0 =A0foreach (x, y; ap) =A0 =A0 =A0 =A0writeln(x, " ", y); } To me outputs: 0 Tuple!(int,int)(1, 2) 1 Tuple!(int,int)(3, 4) 2 Tuple!(int,int)(5, 6) Instead of: 1 2 3 4 5 6 If you have an array, foreach puts the index of the items at the first va=
riable.
 To tell apart tuple unpacking from normal array indexing I have suggested=
a syntax like:
 foreach ((x, y); ap)
 If you fear programmers will miss the (), then requiring something like a=
n auto solves the problem:
 foreach (auto (x, y); ap)

 Bye,
 bearophile
Jan 23 2012
parent bearophile <bearophileHUGS lycos.com> writes:
kenji hara:

 If the enhancement would be accepted, I'd like to implement your
 proposal to increase consistency.
Thank you :-) And as you say it's wiser to wait to see if Walter&Andrei appreciate what you have already done, first, efore working further on this. Bye, bearophile
Jan 23 2012
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/23/12 6:40 PM, kenji hara wrote:
 Today, foreach can expand the front tuple automatically.

 foreach (i, e; zip(sequence!"n", range))
 {
      // i = 0, 1, 2, ...
      // e = elements of range
 }

 So extra unpacking syntax is not need.

 Kenji Hara
That's awesome! Walter mentioned you made that addition. Is there some precise documentation to it? Thanks, Andrei
Jan 23 2012
parent reply kenji hara <k.hara.pg gmail.com> writes:
Precise documentation is not yet written. The original issue is
http://d.puremagic.com/issues/show_bug.cgi?id=3D6366

Kenji Hara

2012/1/24 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:
 On 1/23/12 6:40 PM, kenji hara wrote:
 Today, foreach can expand the front tuple automatically.

 foreach (i, e; zip(sequence!"n", range))
 {
 =A0 =A0 // i =3D 0, 1, 2, ...
 =A0 =A0 // e =3D elements of range
 }

 So extra unpacking syntax is not need.

 Kenji Hara
That's awesome! Walter mentioned you made that addition. Is there some precise documentation to it? Thanks, Andrei
Jan 23 2012
parent Denis Shelomovskij <verylonglogin.reg gmail.com> writes:
24.01.2012 5:50, kenji hara пишет:
 Precise documentation is not yet written.
Filled an issue: http://d.puremagic.com/issues/show_bug.cgi?id=7361
Jan 24 2012