About foreach loops

Steven Schveighoffer schveiguy at yahoo.com
Wed Jun 15 11:06:55 PDT 2011


On Wed, 15 Jun 2011 13:56:04 -0400, Timon Gehr <timon.gehr at gmx.ch> wrote:

> Steven Schveighoffer wrote:
>> On Wed, 15 Jun 2011 13:09:40 -0400, Timon Gehr <timon.gehr at gmx.ch>  
>> wrote:
>>
>>> Steven Schveighoffer wrote:
>>>> On Wed, 15 Jun 2011 10:35:33 -0400, Caligo <iteronvexor at gmail.com>
>>>> wrote:
>>>>
>>>>> You can create a temporary if you like:
>>>>>
>>>>>   foreach(i; 0..10){
>>>>>     int ii = i + 1;
>>>>>     writeln(ii, " ");
>>>>>   }
>>>>>
>>>>> Which outputs:
>>>>> 1 2 3 4 5 6 7 8 9 10
>>>>>
>>>>>
>>>>> The problem with trying to "fix" foreach is that it would create
>>>>> problems of its own.  The following code would not behave correctly:
>>>>>
>>>>>   foreach(i; 0..10){
>>>>>     if(i & 1)
>>>>>       i += 1;
>>>>>     writeln(i, " is even.");
>>>>>   }
>>>>
>>>> That code relies on undocumented behavior.  It's likely to fail on  
>>>> some
>>>> other D compiler.
>>>>
>>>> We can't worry about existing code that uses undocumented "features"  
>>>> of
>>>> dmd.  It's utilizing the knowledge of how D rewrites the foreach
>>>> statement, not how foreach is documented to work.
>>>> [snip.]
>>>
>>> Actually that *is* exactly as it is documented to work.
>>> Any code exploiting it relies on documented behavior.
>>>
>>> See TDPL p. 74:
>>>
>>> foreach(<symbol>; <expression1> .. <expression2>) <statement>
>>>
>>> is rewritten to:
>>>
>>> {
>>>     auto __n = <expression2>;
>>>     auto symbol = true ? <expression1> : <expression2>;
>>>     for(; <symbol> < __n; ++<symbol>) <statement>
>>> }
>>>
>>> This change would introduce an inconsistency with TDPL.
>>
>> I think it would be worth it.
>>
>> In any instance of foreach *except* this one, rebinding the value does  
>> not
>> change the iteration.  It doesn't fit with all other cases of foreach.
>>
>> -Steve
>
> Well, it does make sense but we have to be careful here. If foreach range
> statements get less efficient underway, it is not worth it.
> I am perfectly fine with foreach(i;0..n){} just being syntactic sugar  
> for for(int
> i=0;i<n;i++){}.

I've seen several cases where the syntactic sugar does not work.

two things:

if the compiler detects you never change i, then it can eliminate the  
temporary variable.
We should favor the case which is most useful, not the one which is  
simplest to implement.  If you want surprising behavior, use a for loop.

When I see foreach(i; xxx), I think i should take on every value contained  
in xxx, whether that be a range, array, or something else.  Being able to  
change the elements iterated during the loop is surprising to say the  
least.

Also, think about this:

foreach(i; 0..10)
{
    i -= 2;
}

This is a loop that counts *down* to int.min from -1.  The idea that this  
should be *expected* behavior is just ludicrous to me.

-Steve


More information about the Digitalmars-d mailing list