foreach() behavior on ranges

frame frame86 at live.com
Wed Aug 25 08:31:00 UTC 2021


On Tuesday, 24 August 2021 at 21:15:02 UTC, Steven Schveighoffer 
wrote:

> If you have a for loop:
>
> ```d
> int i;
> for(i = 0; i < someArr.length; ++i)
> {
>    if(someArr[i] == desiredValue) break;
> }
> ```
>
> You are saying, "compiler, please execute the `++i` when I 
> break from the loop because I already processed that one". How 
> can that be expected? I would *never* expect that. When I 
> break, it means "stop the loop, I'm done", and then I use `i` 
> which is where I expected it to be.

I get your point, you see foreach() as raw translate to the 
for-loop and I'm fine with that. To automatically popFront() on 
break also is only a suggestion if there is no other mechanism to 
the tell the range we have cancelled it.

>
>> It becomes useless for foreach() because you can't rely on 
>> them if other code breaks the loop and you need to use that 
>> range, like in my case. But also for ranges - there is no need 
>> for a popFront() if it is not called in a logic way. Then even 
>> empty() could fetch next data if needed. It only makes sense 
>> if language system code uses it in a strictly order and 
>> ensures that this order is always assured.
>
> There is no problem with the ordering. What seems to be the 
> issue is that you aren't used to the way ranges work.

Ehm, no...
-> empty()
-> front()
-> popFront()
-> empty()
-> front()
break;

-> empty();
-> front();

clearly violates the order for me.
Well, nobody said that we must move on the range - but come on...

> What's great about D is that there is a solution for you:
>
> ```d
> struct EagerPopfrontRange(R)
> {
>    R source;
>    ElementType!R front;
>    bool empty;
>    void popFront() {
>      if(source.empty) empty = true;
>      else {
>         front = source.front;
>         source.popFront;
>      }
>    }
> }
>
> auto epf(R)(R inputRange) {
>    auto result = EagerPopfrontRange!R(inputRange);
>    result.popFront; // eager!
>    return result;
> }
>
> // usage
> foreach(v; someRange.epf) { ... }
> ```
>
> Now if you break from the loop, the original range is pointing 
> at the element *after* the one you last were processing.

This is nice. But foreach() should do it automatically - avoiding 
this.
foreach() should be seen as a special construct that does that, 
not just a dumb alias for the for-loop. Why? Because it is a 
convenient language construct and usage should be easy. Again, 
there should be no additional popFront() just because I break the 
loop.


> I'm surprised you bring PHP as an example, as it appears their 
> foreach interface works EXACTLY as D does:

Yeah, but the point is, there is a rewind() method. That is 
called every time on foreach().




More information about the Digitalmars-d-learn mailing list