May 16, 2012
"Jonathan M Davis" <jmdavisProg@gmx.com> wrote in message news:mailman.828.1337154007.24740.digitalmars-d-learn@puremagic.com...
> On Wednesday, May 16, 2012 03:29:23 Nick Sabalausky wrote:
>> "Jonathan M Davis" <jmdavisProg@gmx.com> wrote in message news:mailman.826.1337151940.24740.digitalmars-d-learn@puremagic.com...
>>
>> > No. That's expected. Your range is a value type, so it got copied when
>> > you
>> > used it with foreach.
>>
>> But foreach isn't a function, it's a flow-control statement.
>
> If it _wasn't_ copied, using foreach would consume your range.

Iterating a range normally *does* consume it. And that's expected, as it should be: Imagine, for example, an input range that read from stdin. Leaving that range unconsumed would make no sense at all. Actually any input range can be expected to *not* leave an uniterated copy: if it *could* have an uniterated copy left behind, it would be a forward range, not an input range.

> It doesn't, and it would really suck if it did.

Like I said above, it would suck if you use the current foreach over an input range: Sometimes it might work by pure luck (as in the original example), but you can expect that for some input ranges it would fail miserably, because an input range is *not* a forward range and by definition cannot reliably save a previous state.

Additionally, if you wanted to still have an un-iterated version (of an actual foreard range, or an input range that you knew to be safe), then that's trivial:

Foo a;
b = a.save();  // Or "b = a;" if you really know what you're doing.
foreach(elem; a) {}
assert(a.empty);
assert(!b.empty);

Note, however, that there is no such simple way to go the other way around: to have the current "foreach over a copy" behavior and have access to the actual iterated range. Maybe if we had "foreach(e; ref range)", but AFAIK we don't.

Maybe it's just me, but I'd intuitively expect foreach over a range to work like this:

Range range;
foreach(e; range) {...}

-->

Range range;
for(; !range.empty(); range.popFront())
{
    auto e = range.front;
    ...
}

I was initially open to the idea I may have just misunderstood something, but I'm becoming more and more convinced that the current "foreach over an implicit copy" behavior is indeed a bad idea.

I'd love to see Andrei weigh in on this. I'm curious if the example you pointed out in TDPL made the copy deliberately, or if the effects of that copy were just an oversight.


May 16, 2012
Actually, I think I'm going to move this over to "digitalmars.D". Seems a more appropriate place at this point.


May 16, 2012
On Wednesday, 16 May 2012 at 20:50:55 UTC, Nick Sabalausky wrote:

> I was initially open to the idea I may have just misunderstood something, but I'm becoming more and more convinced that the current "foreach over an implicit copy" behavior is indeed a bad idea.
>
> I'd love to see Andrei weigh in on this. I'm curious if the example you pointed out in TDPL made the copy deliberately, or if the effects of that copy were just an oversight.

 I remember going over hard and long over that section. I think it's deliberate.

 Range can refer to many things, and I'm assuming array is one of them. So... If the array is consumed when using foreach, that seems dump right? (An array in the end is just a small struct afterall...)

---
int[] x = [1,2,3];
foreach(i; x) {
 //stuff
}

 assert(x.length == 0, "Should have been consumed!");
---

 Structs being value types are by value and so are copied, not referenced. Although referencing a copy does seem a bit silly...

int[] x = [1,2,3];
foreach(ref i; x) {
 i = 10;
}

assert(x == [10,10,10], "But i changed them to 10!!");

--

 That means that foreach(ref; ref) if it's considered (And I think it makes sense to) would work for structs in these few cases. (Course you'd still consume dynamic arrays that way)

 Something stream based as I've seen in std.stream are classes, so they are consumed. I'm probably just rambling...
May 17, 2012
On Wed, 16 May 2012 03:20:39 -0400, Jonathan M Davis <jmdavisProg@gmx.com> wrote:

> On Wednesday, May 16, 2012 09:15:06 bearophile wrote:
>> Nick Sabalausky:
>> > It seems that foreach is iterating over an implicit copy of
>> > 'foo' instead of
>> > 'foo' itself. Is this correct behavior, or a DMD bug?
>>
>> This program prints "[11, 21, 31]" so for fixed-size arrays (that
>> are values as much as structs) it doesn't perform a copy:
>>
>>
>> import std.stdio;
>> void main() {
>>       int[3] a = [10, 20, 30];
>>       foreach (ref x; a)
>>           x++;
>>       writeln(a);
>> }
>
> It probably slices the array, which would mean that it was operating on a
> dynamic array rather than a static one. But it could just generate different
> code for static arrays.

foreach does *not* use range primitives for arrays, they are special.

-Steve
1 2
Next ›   Last »