My current thinking:
- I still think adding index to range foreach is a good idea.
- I realise that scheme #2 isn't workable.
- I still like scheme #1 over tuple expansion as it avoids all the issues
which make scheme #2 unworkable.
- enumerate is not as flexible as many people seem to think.
On Fri, 21 Feb 2014 02:34:28 -0000, Jesse Phillips
<jesse.k.phillip...@gmail.com> wrote:
On Thursday, 20 February 2014 at 11:15:14 UTC, Regan Heath wrote:
I am posting this again because I didn't get any feedback on my idea,
which may be TL;DR or because people think it's a dumb idea and they
were politely ignoring it :p
I certainly have wanted counts of the range iteration, but I do believe
it becomes too complex to support and that even if we state 'i' is to
represent a count and not an index, people will still want an index and
expect it to be an index of their original range even though it makes no
possible sense from the perspective of iterating over a different range
from the original.
I don't understand how this is "complex to support"? It's simple. It's a
count, not an index unless the range is indexable. If people are going to
expect an index here, they will expect one with enumerate as well - and
are going to be equally disappointed. So, they need to be aware of this
regardless.
I also don't find myself needing to count iterations very often, and I
believe when I do, it is because I want to use that count as an index
(possibly needing to add to some global count, but I don't need it
enough to remember).
The justification for this change is the same as for enumerate.
It is common enough to make it important, and when it happens it's
frustrating enough that it needs fixing.
My specific example didn't want an index, or rather it wanted an index
into the result set which I believe is just as common if not more common
than wanting an index into the source - especially given that they are
often the same thing.
For example, I find myself using an index to control loop behaviour, most
often for detecting the first and last iterations than anything else. A
counter will let you do that just as well as an index.
Scheme 1)
As Marc said, "ails backwards-compatibility." A change like this will
never exist if it isn't backwards compatible. There are very few changes
which will be accepted if backwards compatibility isn't preserved.
Sure. I personally find this idea compelling enough to warrant some
breakage, it is simple, powerful and extensible and avoids all the issues
of optional indexes with tuple expansion. But, I can see how someone
might disagree.
Scheme 2)
However, if a type is given and the type can be unambiguously matched
to a single tuple component then do so.
double[string] AA;
foreach (string k; AA) {} // k is "key"
While probably not common, what if one needed to switch key/value
string[double] AA;
or something similar, the type system no longer helps. But again, this
seems pretty much uneventful.
Perhaps I wasn't clear, this would work fine:
string[double] AA;
foreach (string v; AA) {} // v is "value"
foreach (double k; AA) {} // k is "key"
or am I missing the point you're making?
foreach (i, k, v; AA.byPairs.enumerate) {}
foreach (i, k, v; AA) {} // better
Bringing this back to range iteration:
foreach(i, v1, v2; tuple(0,1).repeat(10))
writeln(i, "\t",v1, "\t",v2);
Later the range gets a new value, the foreach would still compile but be
wrong:
foreach(i, v1, v2; tuple(0,1,2).repeat(10))
writeln(i, "\t",v1, "\t",v2);
With enumerate, there is an error.
foreach(i, v1, v2; tuple(0,1,2).repeat(10).enumerate)
writeln(i, "\t", v1, "\t", v2);
Error: cannot infer argument types
Sure, this is an issue with having the optional index/count variable,
which is not something foreach with enumerate allows. This is another
reason I prefer scheme #1, you never have this issue no matter what.
foreach(i, v1, v2; tuple(0,1).repeat(10).enumerate)
writeln(i, "\t", v1, "\t", v2);
This works today! And once enumerate is part of Phobos it will just need
an import std.range to use it.
I don't believe this works today. My understanding of what is currently
supported is..
foreach(index, value; array) { }
foreach(value; range) { } // no support for index/count
foreach(key, value; tuple) { } // no support for index/count
And, my understanding of enumerate is that it simply creates a tuple from
an index and a range value, taking it from the range foreach case above,
to the tuple foreach case.
This is not extensible to more than 2 values. In fact, it's pretty
limited until we get full built-in tuple expansion support.
To test this understanding I pulled down the source for enumerate and
coded this up:
import std.stdio;
import std.range;
import std.typecons;
..paste enumerate here.. // line 5
void main()
{
foreach(i, v1, v2; tuple(0,1,2).repeat(10).enumerate) // line 124
writeln(i, "\t", v1, "\t", v2);
}
I get the following errors:
testtup.d(124): Error: template testtup.enumerate does not match any
function template declaration. Candidates are:
testtup.d(5): testtup.enumerate(Range)(Range range, size_t start =
0) if(isInputRange!Range && (!hasLength!Range || is(Largest!(size_t,
typeof(range.length)) == size_t)))
testtup.d(124): Error: template testtup.enumerate(Range)(Range range,
size_t start = 0) if (isInputRange!Range && (!hasLength!Range ||
is(Largest!(size_t, typeof(range.length)) == size_t))) cannot deduce
template function from argument types !()(Take!(Repeat!(Tuple!(int, int,
int))))
I believe this errors because tuple foreach only allows (value) or (key,
value) not (index, key, value) and certainly not (index, value, value,
value) which is what we're trying to do here.
Am I right?
If so, and given that we currently support:
foreach(index, value; array) { }
foreach(value; range) { } // no support for index/count
foreach(key, value; tuple) { } // no support for index/count
There is absolutely no good reason not to add:
foreach(index, value; range) { }
It is backwards compatible and breaks no code, nor does it interfere with
tuple expansion because that is a separate foreach case.
R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/