www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - suggested change to foreach index

reply BCS <BCS pathlink.com> writes:
I often find my self doing something like this:

<code>
int i
char[] arr;

foreach(int j, c; arr)
	if(' ' == c)
	{
		i=j;	// copy j out of foreach's scope
		break;
	}

if(i < arr.length)
{
	arr = arr.dup;
		// use copy of j
	foreach(inout c; arr[i..$])
		if(' ' == c) c = '_';
}
</code>

In this case it would be nice to be able to use a variable in the outer 
scope as the index in the foreach, e.i.

<code>
int i
char[] arr;

foreach(int i, c; arr)
	if(' ' == c)
		break;

// i keeps the value from the last time through
...
</code>


Thoughts comments??
Jun 07 2006
next sibling parent reply "Ameer Armaly" <ameer_armaly hotmail.com> writes:
"BCS" <BCS pathlink.com> wrote in message 
news:e67p90$rol$1 digitaldaemon.com...
I often find my self doing something like this:

 <code>
 int i
 char[] arr;

 foreach(int j, c; arr)
 if(' ' == c)
 {
 i=j; // copy j out of foreach's scope
 break;
 }

 if(i < arr.length)
 {
 arr = arr.dup;
 // use copy of j
 foreach(inout c; arr[i..$])
 if(' ' == c) c = '_';
 }
 </code>

 In this case it would be nice to be able to use a variable in the outer 
 scope as the index in the foreach, e.i.

 <code>
 int i
 char[] arr;

 foreach(int i, c; arr)
 if(' ' == c)
 break;

 // i keeps the value from the last time through
 ...
 </code>


 Thoughts comments??

Jun 07 2006
next sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 7 Jun 2006 20:27:51 -0400, Ameer Armaly <ameer_armaly hotmail.com>  
wrote:
 "BCS" <BCS pathlink.com> wrote in message
 news:e67p90$rol$1 digitaldaemon.com...
 I often find my self doing something like this:

 <code>
 int i
 char[] arr;

 foreach(int j, c; arr)
 if(' ' == c)
 {
 i=j; // copy j out of foreach's scope
 break;
 }

 if(i < arr.length)
 {
 arr = arr.dup;
 // use copy of j
 foreach(inout c; arr[i..$])
 if(' ' == c) c = '_';
 }
 </code>

 In this case it would be nice to be able to use a variable in the outer
 scope as the index in the foreach, e.i.

 <code>
 int i
 char[] arr;

 foreach(int i, c; arr)
 if(' ' == c)
 break;

 // i keeps the value from the last time through
 ...
 </code>


 Thoughts comments??


Requiring you to put the foreach in a function? Probably. At the very least we need some way to indicate we want the behaviour as it really shouldn't use variables external to the foreach by default. eg. int i; char[] arr; foreach(inout int i, c; arr) {} or foreach(inout i, c; arr) {} or something. Something/anything to indicate the 'i' in the loop is the 'i' from outside the loop. Or, perhaps another block which is executed with the foreach variables when the loop doesn't terminate normally: foreach(int i, c; arr) { } else { //comes here if you use 'break' in foreach //i, and c have the last values they had in the loop. //what happens in the 'inout c' cases? } Regan
Jun 07 2006
parent "Ameer Armaly" <ameer_armaly hotmail.com> writes:
"Regan Heath" <regan netwin.co.nz> wrote in message 
news:optasxzhwe23k2f5 nrage.netwin.co.nz...
 On Wed, 7 Jun 2006 20:27:51 -0400, Ameer Armaly <ameer_armaly hotmail.com> 
 wrote:
 "BCS" <BCS pathlink.com> wrote in message
 news:e67p90$rol$1 digitaldaemon.com...
 I often find my self doing something like this:

 <code>
 int i
 char[] arr;

 foreach(int j, c; arr)
 if(' ' == c)
 {
 i=j; // copy j out of foreach's scope
 break;
 }

 if(i < arr.length)
 {
 arr = arr.dup;
 // use copy of j
 foreach(inout c; arr[i..$])
 if(' ' == c) c = '_';
 }
 </code>

 In this case it would be nice to be able to use a variable in the outer
 scope as the index in the foreach, e.i.

 <code>
 int i
 char[] arr;

 foreach(int i, c; arr)
 if(' ' == c)
 break;

 // i keeps the value from the last time through
 ...
 </code>


 Thoughts comments??


Requiring you to put the foreach in a function? Probably.

you of the stopping point, that's the natural place for a function, though I could be overlooking some corner cases.
 At the very least we need some way to indicate we want the behaviour as it 
 really shouldn't use variables external to the foreach by default. eg.

 int i;
 char[] arr;

 foreach(inout int i, c; arr) {}

 or

 foreach(inout i, c; arr) {}

 or something. Something/anything to indicate the 'i' in the loop is the 
 'i' from outside the loop. Or, perhaps another block which is executed 
 with the foreach variables when the loop doesn't terminate normally:

 foreach(int i, c; arr) {
 }
 else {
   //comes here if you use 'break' in foreach
   //i, and c have the last values they had in the loop.
   //what happens in the 'inout c' cases?
 }

 Regan 

Jun 07 2006
prev sibling parent BCS <BCS_member pathlink.com> writes:
In article <e67qth$ugk$1 digitaldaemon.com>, Ameer Armaly says...
"BCS" <BCS pathlink.com> wrote in message 
news:e67p90$rol$1 digitaldaemon.com...
I often find my self doing something like this:

 <code>
 int i
 char[] arr;

 foreach(int j, c; arr)
 if(' ' == c)
 {
 i=j; // copy j out of foreach's scope
 break;
 }

 if(i < arr.length)
 {
 arr = arr.dup;
 // use copy of j
 foreach(inout c; arr[i..$])
 if(' ' == c) c = '_';
 }
 </code>

 In this case it would be nice to be able to use a variable in the outer 
 scope as the index in the foreach, e.i.

 <code>
 int i
 char[] arr;

 foreach(int i, c; arr)
 if(' ' == c)
 break;

 // i keeps the value from the last time through
 ...
 </code>


 Thoughts comments??


access local variables then it needs to be a nested function and therefore could results in a lot of lookup by pointer. Also there is the added function call overhead associated with it. If the code is performance critical all of these are bad things.
Jun 07 2006
prev sibling next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Wed, 07 Jun 2006 17:04:09 -0700, BCS wrote:

 I often find my self doing something like this:
 
 <code>
 int i
 char[] arr;
 
 foreach(int j, c; arr)
 	if(' ' == c)
 	{
 		i=j;	// copy j out of foreach's scope
 		break;
 	}
 
 if(i < arr.length)
 {
 	arr = arr.dup;
 		// use copy of j
 	foreach(inout c; arr[i..$])
 		if(' ' == c) c = '_';
 }
 </code>
 
 In this case it would be nice to be able to use a variable in the outer 
 scope as the index in the foreach, e.i.
 
 <code>
 int i
 char[] arr;
 
 foreach(int i, c; arr)
 	if(' ' == c)
 		break;
 
 // i keeps the value from the last time through
 ...
 </code>
 
 Thoughts comments??

I see what you mean but have you tried this ... int i; char[] arr; foreach(int j, c; arr) if(' ' == c) { arr = arr.dup; foreach(inout c; arr[j..$]) if(' ' == c) c = '_'; break; } -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocracy!" 8/06/2006 11:12:32 AM
Jun 07 2006
parent BCS <BCS_member pathlink.com> writes:
In article <cs4o03emrdkm$.b5qasiolumf7.dlg 40tude.net>, Derek Parnell says...
On Wed, 07 Jun 2006 17:04:09 -0700, BCS wrote:
 I often find my self doing something like this:


 
 <code>
 int i
 char[] arr;
 
 foreach(int i, c; arr)
 	if(' ' == c)
 		break;
 
 // i keeps the value from the last time through
 ...
 </code>
 
 Thoughts comments??

I see what you mean but have you tried this ... int i; char[] arr; foreach(int j, c; arr) if(' ' == c) { arr = arr.dup; foreach(inout c; arr[j..$]) if(' ' == c) c = '_'; break; }

That would work but its not vary expandable. Try doing that for five or six loops. The proposed solution is just a lot cleaner, even in the two loop case. Or how about doing that with something like: char[] arr; int j=0, i, skip; do { foreach(inout i, c; arr[j..$]) if( ' == c) break; // do something and continue j+=(i+skip); }while(i != arr.length)
Jun 07 2006
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
What's weird to me is if I do this:

     void main()
     {
         int    i;
         char[] arr = "abc def";

         foreach( i, c; arr )
         {
             printf( "%i\n", i );
             if(' ' == c)
                 break;
         }
         printf( "\n%i\n", i );
     }

Then the output is:

     0
     1
     2
     3

     0

So the code compiles without a type for i in the foreach, but rather 
than use the instance of i in the surrounding scope a local value is 
used instead.  The spec isn't clear on what should happen here, but I 
would have guessed that the final value displayed would be 3, not 0.


Sean
Jun 07 2006
parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Sean Kelly wrote:
 What's weird to me is if I do this:
 
     void main()
     {
         int    i;
         char[] arr = "abc def";
 
         foreach( i, c; arr )
         {
             printf( "%i\n", i );
             if(' ' == c)
                 break;
         }
         printf( "\n%i\n", i );
     }
 
 Then the output is:
 
     0
     1
     2
     3
 
     0
 
 So the code compiles without a type for i in the foreach, but rather
 than use the instance of i in the surrounding scope a local value is
 used instead.  The spec isn't clear on what should happen here, but I
 would have guessed that the final value displayed would be 3, not 0.
 
 
 Sean

It creates a new scope, just like for loops: int i = 5; for (int i = 0; i < 3; ++i) writefln(i); // 0 to 2 writefln(i); // 5 I don't know about the spec's opinion, but this is how it has always worked, both with for and foreach, and I have a lot of code that relies on this behaviour. The "inout" suggested elsewhere in the thread by Regan Heath seems like a good solution to me.
Jun 08 2006
parent reply Sean Kelly <sean f4.ca> writes:
Deewiant wrote:
 Sean Kelly wrote:
 What's weird to me is if I do this:

     void main()
     {
         int    i;
         char[] arr = "abc def";

         foreach( i, c; arr )
         {
             printf( "%i\n", i );
             if(' ' == c)
                 break;
         }
         printf( "\n%i\n", i );
     }

 Then the output is:

     0
     1
     2
     3

     0

 So the code compiles without a type for i in the foreach, but rather
 than use the instance of i in the surrounding scope a local value is
 used instead.  The spec isn't clear on what should happen here, but I
 would have guessed that the final value displayed would be 3, not 0.


 Sean

It creates a new scope, just like for loops: int i = 5; for (int i = 0; i < 3; ++i) writefln(i); // 0 to 2 writefln(i); // 5

Yes, but my example was equivalent to this (I thought): int i = 5; for (i = 0; i < 3; ++i) writefln(i); // 0 to 2 writefln(i); // 2 ie, the lack of a type makes the "i=0" an assignment, not a declaration with an initializer. The i from the surrounding scope is used. I expected foreach to behave the same way, but apparently it doesn't. Sean
Jun 08 2006
parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Sean Kelly wrote:
 Deewiant wrote:
 Sean Kelly wrote:
     void main()
     {
         int    i;
         char[] arr = "abc def";

         foreach( i, c; arr )
         {
             printf( "%i\n", i );
             if(' ' == c)
                 break;
         }
         printf( "\n%i\n", i );
     }

It creates a new scope, just like for loops: int i = 5; for (int i = 0; i < 3; ++i) writefln(i); // 0 to 2 writefln(i); // 5

Yes, but my example was equivalent to this (I thought): int i = 5; for (i = 0; i < 3; ++i) writefln(i); // 0 to 2 writefln(i); // 2 ie, the lack of a type makes the "i=0" an assignment, not a declaration with an initializer. The i from the surrounding scope is used. I expected foreach to behave the same way, but apparently it doesn't.

I guess it's the type inference that makes it so confusing to some. Earlier, after all, we had to write: foreach (int i, char[] c; arr) Instead of the modern terse version: foreach (i, c; arr) In the longer version, it's presumably more clear that the i is a new "int i".
Jun 08 2006
parent reply BCS <BCS pathlink.com> writes:
Deewiant wrote:
 Sean Kelly wrote:
 
Deewiant wrote:

Sean Kelly wrote:

    void main()
    {
        int    i;
        char[] arr = "abc def";

        foreach( i, c; arr )
        {
            printf( "%i\n", i );
            if(' ' == c)
                break;
        }
        printf( "\n%i\n", i );
    }

It creates a new scope, just like for loops: int i = 5; for (int i = 0; i < 3; ++i) writefln(i); // 0 to 2 writefln(i); // 5

Yes, but my example was equivalent to this (I thought): int i = 5; for (i = 0; i < 3; ++i) writefln(i); // 0 to 2 writefln(i); // 2 ie, the lack of a type makes the "i=0" an assignment, not a declaration with an initializer. The i from the surrounding scope is used. I expected foreach to behave the same way, but apparently it doesn't.

I guess it's the type inference that makes it so confusing to some. Earlier, after all, we had to write: foreach (int i, char[] c; arr) Instead of the modern terse version: foreach (i, c; arr) In the longer version, it's presumably more clear that the i is a new "int i".

The suggested syntax vvv foreach([[inout] index,] value ; ... seems a bit odd to me. How about use "alias" instead? int i; foreach(alias i, v; arr){}
Jun 08 2006
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"BCS" <BCS pathlink.com> wrote in message 
news:e69lah$dmo$1 digitaldaemon.com...

 The suggested syntax
            vvv
 foreach([[inout] index,] value ; ...

 seems a bit odd to me. How about use "alias" instead?

 int i;
 foreach(alias i, v; arr){}

Or, more consistent, make auto foreach indices require "auto"? int i; foreach(auto i, auto j; something) writefln(i); // New local i which overrides outer i foreach(i, auto j; something) writefln(i); // Uses outer i writefln(i); // Writes the value that i last had in the loop This has the (wonderful) side-effect of making it bleedingly obvious that the foreach indices are using type inference; the current index inference form _looks_ like it's trying to use already-defined index variables.
Jun 08 2006
next sibling parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Jarrett Billingsley wrote:
 Or, more consistent, make auto foreach indices require "auto"?
 
 int i;
 
 foreach(auto i, auto j; something)
     writefln(i); // New local i which overrides outer i
 
 foreach(i, auto j; something)
     writefln(i); // Uses outer i
 
 writefln(i); // Writes the value that i last had in the loop
 
 This has the (wonderful) side-effect of making it bleedingly obvious that 
 the foreach indices are using type inference; the current index inference 
 form _looks_ like it's trying to use already-defined index variables. 

++votes; // fully agreed -- Tomasz Stachowiak /+ a.k.a. h3r3tic +/
Jun 08 2006
prev sibling next sibling parent "Derek Parnell" <derek psych.ward> writes:
On Fri, 09 Jun 2006 06:49:11 +1000, Jarrett Billingsley  
<kb3ctd2 yahoo.com> wrote:

 Or, more consistent, make auto foreach indices require "auto"?

This will break some existing code but I think its worth it. It's a good idea. -- Derek Parnell Melbourne, Australia
Jun 08 2006
prev sibling next sibling parent Sean Kelly <sean f4.ca> writes:
Jarrett Billingsley wrote:
 "BCS" <BCS pathlink.com> wrote in message 
 news:e69lah$dmo$1 digitaldaemon.com...
 
 The suggested syntax
            vvv
 foreach([[inout] index,] value ; ...

 seems a bit odd to me. How about use "alias" instead?

 int i;
 foreach(alias i, v; arr){}

Or, more consistent, make auto foreach indices require "auto"? int i; foreach(auto i, auto j; something) writefln(i); // New local i which overrides outer i foreach(i, auto j; something) writefln(i); // Uses outer i writefln(i); // Writes the value that i last had in the loop This has the (wonderful) side-effect of making it bleedingly obvious that the foreach indices are using type inference; the current index inference form _looks_ like it's trying to use already-defined index variables.

I was thinking the same thing. Definitely has my vote. Sean
Jun 08 2006
prev sibling parent reply Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Jarrett Billingsley wrote:
 "BCS" <BCS pathlink.com> wrote in message 
 news:e69lah$dmo$1 digitaldaemon.com...
 
 The suggested syntax
            vvv
 foreach([[inout] index,] value ; ...

 seems a bit odd to me. How about use "alias" instead?

 int i;
 foreach(alias i, v; arr){}

Or, more consistent, make auto foreach indices require "auto"? int i; foreach(auto i, auto j; something) writefln(i); // New local i which overrides outer i foreach(i, auto j; something) writefln(i); // Uses outer i writefln(i); // Writes the value that i last had in the loop This has the (wonderful) side-effect of making it bleedingly obvious that the foreach indices are using type inference; the current index inference form _looks_ like it's trying to use already-defined index variables.

Agreed too, this is the natural/intuitive/consistent way to go. Those other solutions with "alias" and "inout" were awful. -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 10 2006
parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Bruno Medeiros wrote:
 Jarrett Billingsley wrote:
 Or, more consistent, make auto foreach indices require "auto"?

 int i;

 foreach(auto i, auto j; something)
     writefln(i); // New local i which overrides outer i

 foreach(i, auto j; something)
     writefln(i); // Uses outer i

 writefln(i); // Writes the value that i last had in the loop

 This has the (wonderful) side-effect of making it bleedingly obvious
 that the foreach indices are using type inference; the current index
 inference form _looks_ like it's trying to use already-defined index
 variables.

Agreed too, this is the natural/intuitive/consistent way to go. Those other solutions with "alias" and "inout" were awful.

I guess I'm the only one that doesn't like this idea, then. I think "auto" is overloaded enough as it is; I'm still waiting for "var" or some such for type inference. And I always thought it was obvious that foreach() uses its own index, but that might be since I've only recently (say, a few days back) begun to use type inference with it. When you write "size_t i, type x; something" it's obvious enough, and when I write "i, type x" I think of the longer form, realising it's short for just that. Personally, I'd prefer "out" instead of "inout" or "alias", but that's just me. <g>
Jun 10 2006
next sibling parent BCS <BCS_member pathlink.com> writes:
In article <e6ev28$o1k$1 digitaldaemon.com>, Deewiant says...
 Jarrett Billingsley wrote:
 Or, more consistent, make auto foreach indices require "auto"?

 int i;

 foreach(auto i, auto j; something)
     writefln(i); // New local i which overrides outer i

 foreach(i, auto j; something)
     writefln(i); // Uses outer i

 writefln(i); // Writes the value that i last had in the loop

 This has the (wonderful) side-effect of making it bleedingly obvious
 that the foreach indices are using type inference; the current index
 inference form _looks_ like it's trying to use already-defined index
 variables.


I guess I'm the only one that doesn't like this idea, then. I think "auto" is overloaded enough as it is; I'm still waiting for "var" or some such for type inference.

Yup, a type inference keyword would be nice.
And I always thought it was obvious that foreach() uses its own index, but that
might be since I've only recently (say, a few days back) begun to use type
inference with it. When you write "size_t i, type x; something" it's obvious
enough, and when I write "i, type x" I think of the longer form, realising it's
short for just that.

Personally, I'd prefer "out" instead of "inout" or "alias", but that's just me.
<g>

Come to think of it, that would be consistent, the variable never caries in a value at the start of the loop. Also it sounds nice: " this variable is outside this scope" On a different track: given this <code> int i; foreach(out i, char c; "ab"){i=3} writef(i,\n); </code> what is printed? 1, 2 or 3? It is 1 if i exits with the last value the loop sets it to. It is 2 if i exits with the first value that doesn't match It is 3 if i keeps its value at the point the loop is exited. I would go with 1 or maybe 3. 2 becomes undefined with an AA Thoughts? Comments?
Jun 10 2006
prev sibling parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Deewiant" <deewiant.doesnotlike.spam gmail.com> wrote in message 
news:e6ev28$o1k$1 digitaldaemon.com...

 I guess I'm the only one that doesn't like this idea, then. I think "auto" 
 is
 overloaded enough as it is; I'm still waiting for "var" or some such for 
 type
 inference.

I'd really like something else for type inference too, but until that happens, "auto" would make the most sense. I think 'var' or something along those lines would be better; and it would be best if it functioned _as a type_ which would just stand for a "placeholder" type until the type could be determined in the semantic pass.
 And I always thought it was obvious that foreach() uses its own index, but 
 that
 might be since I've only recently (say, a few days back) begun to use type
 inference with it. When you write "size_t i, type x; something" it's 
 obvious
 enough, and when I write "i, type x" I think of the longer form, realising 
 it's
 short for just that.

Well, keep in mind that many of my proposals are based on my virulent hatred of most uses of type inference ;) It's bad enough when people start overusing auto; it's worse when it's not even obvious that a new variable is being declared as well!
 Personally, I'd prefer "out" instead of "inout" or "alias", but that's 
 just me. <g>

That's good too, but the best would be if/when we get a true type-inference keyword for use with index inference, and then nothing when you want to use the outer scope's variable (to be consistent with the behavior of for loops).
Jun 10 2006