View mode: basic / threaded / horizontal-split · Log in · Help
August 04, 2005
foreach annoyance
Simple question.

Does anyone else find themselves with this problem?

//contrived example, actual values/data not important
char[] string = "abcdefgh";
foreach(char c; string)
{
  if (c == 'e') break;
}
//at this point I'd like to do something if the item wasn't found.. how  
can I tell?


I can tell when using a for loop:

int i;
for(i = 0; i < string.length; i++)
{
  if (string[i] == 'e') break;
}
if (i == string.length) {} //not found

But I thought the idea was to use foreach so that I can change the  
container type at a later date with no other changes required.


Of course, I could add 'i' to the foreach in the same way, eg.

int i = 0;
foreach(char c; string)
{
  if (c == 'e') break;
  i++;
}
if (i == string.length) {} //not found


But it would be nice if there was a more elegant solution. Something like:

foreach(char c; string)
{
  if(c == 'e') break;
}
overflow
{
  //not found
}

However "overflow" doesn't seem like quite the right term.

Regan
August 04, 2005
Re: foreach annoyance
On Fri, 05 Aug 2005 00:27:48 +1200, Regan Heath wrote:

> Simple question.
> 
> Does anyone else find themselves with this problem?
> 
> //contrived example, actual values/data not important
> char[] string = "abcdefgh";
> foreach(char c; string)
> {
>    if (c == 'e') break;
> }
> //at this point I'd like to do something if the item wasn't found.. how  
> can I tell?

I know this is not a real fast way to do it, but I have been know to do ...

char[] string = "abcdefgh";
foreach(int i, char c; string)
{
   if (c == 'e') break;
   if (i == string.length-1) ...
}

But in most cases this is too much of an overhead.

-- 
Derek Parnell
Melbourne, Australia
4/08/2005 10:59:11 PM
August 04, 2005
Re: foreach annoyance
Regan Heath says...
>
>Simple question.
>
>Does anyone else find themselves with this problem?
>
>//contrived example, actual values/data not important
>char[] string = "abcdefgh";
>foreach(char c; string)
>{
>   if (c == 'e') break;
>}
>//at this point I'd like to do something if the item wasn't found.. how  
>can I tell?
>
>
>I can tell when using a for loop:
>
>int i;
>for(i = 0; i < string.length; i++)
>{
>   if (string[i] == 'e') break;
>}
>if (i == string.length) {} //not found
>
>But I thought the idea was to use foreach so that I can change the  
>container type at a later date with no other changes required.
>
>
>Of course, I could add 'i' to the foreach in the same way, eg.
>
>int i = 0;
>foreach(char c; string)
>{
>   if (c == 'e') break;
>   i++;
>}
>if (i == string.length) {} //not found
>
>
>But it would be nice if there was a more elegant solution. Something like:
>
>foreach(char c; string)
>{
>   if(c == 'e') break;
>}
>overflow
>{
>   //not found
>}
>
>However "overflow" doesn't seem like quite the right term.

I know, this is NOT what you want, but this is the best way to tell, anyway.
Using the good ole found variable. :-)

|bit found = false;
|foreach (char c; string)
|{
|  if(c == 'e')
|  {
|    found = true;
|    break;
|  }
|}
|
|if (found)
|{
|// found it
|}

However, there is another way you could do this:

|int ii;
|foreach (int i; char c; string)
|{
|  ii = i;
|  if(c == 'e')
|    break;
|}

After the loop is processed, ii will have the value that i had at the time of
encounter or loop completion.

just a thought...

josé
August 04, 2005
Re: foreach annoyance
> But it would be nice if there was a more elegant solution. Something like:
>
> foreach(char c; string)
> {
>   if(c == 'e') break;
> }
> overflow
> {
>   //not found
> }

Another option is to make foreach return the value of the opApply (or the 
builtin loop if there is no opApply) and turn foreach into an expression 
instead of a statement. So for example
 int found = foreach(char c; string){
   if (c == 'e') break;
 };
 if (found) {
   // found 'e' in string
 } else {
   //. not found case
 }

Note making foreach an expression would break existing code since the 
trailing ; would be needed to turn the expression into a statement. It also 
might be confusing that people might think they could have "return" 
statements in the foreach body instead of "break". People would also 
probably want to return values from the foreach like the index at the break.

Given those problems if making foreach an expression is too wacky the result 
can be stored in an implicit variable _foreachAborted or something:
 foreach(char c; string){
   if (c == 'e') break;
 }
 if (_foreachAborted) {
   // found 'e' in string
 } else {
   //. not found case
 }
August 04, 2005
Re: foreach annoyance
Hi Ben,

>Another option is to make foreach return the value of the opApply (or the 
>builtin loop if there is no opApply) and turn foreach into an expression 
>instead of a statement. So for example
>  int found = foreach(char c; string){
>    if (c == 'e') break;
>  };
>  if (found) {
>    // found 'e' in string
>  } else {
>    //. not found case
>  }

Fantastic! This would be simply awesome. It would pave the way for the
expressionisation of all constructs just like current GNU C extensions allow. A
vote for expression-foreach is a vote for the future. 

>Note making foreach an expression would break existing code since the 
>trailing ; would be needed to turn the expression into a statement. It also 
>might be confusing that people might think they could have "return" 
>statements in the foreach body instead of "break". People would also 
>probably want to return values from the foreach like the index at the break.

Yeah, the trailing ; could break things, but it would be well worth it.
Alternatively, we could have both versions (expression foreach w/ the ; and
regular foreach w/o ;. Then, regular foreach could be deprecated over time).

>Given those problems if making foreach an expression is too wacky the result 
>can be stored in an implicit variable _foreachAborted or something:
>  foreach(char c; string){
>    if (c == 'e') break;
>  }
>  if (_foreachAborted) {
>    // found 'e' in string
>  } else {
>    //. not found case
>  } 

On the other hand, this part I don't like that much.
--AJG.
August 04, 2005
Re: foreach annoyance
Regan Heath wrote:
> Simple question.
> 
> Does anyone else find themselves with this problem?
> 
> //contrived example, actual values/data not important
> char[] string = "abcdefgh";
> foreach(char c; string)
> {
>   if (c == 'e') break;
> }
> //at this point I'd like to do something if the item wasn't found.. how  
> can I tell?

I've found that this problem is symptomatic of over-complex control flow 
and should be delegated into a function (inner or outer) which tells you 
whether it found any entries and possibly acts on found entries if it 
can handle more than one.  A method which goes along this path will 
become harder and harder to manipulate easily.  Inner functions aren't 
just for doing a task more than once; they also act as 
self-documentation and control flow, reducing the core function to a 
series of modular parts to manipulate at will.

This reminds me that I need to barge in on Parcel code and do some 
refactoring, I even use goto a couple times.

> I can tell when using a for loop:
> 
> int i;
> for(i = 0; i < string.length; i++)
> {
>   if (string[i] == 'e') break;
> }
> if (i == string.length) {} //not found
> 
> But I thought the idea was to use foreach so that I can change the  
> container type at a later date with no other changes required.

It's no substitute for interfaces or templating.
August 04, 2005
Re: foreach annoyance
On Thu, 4 Aug 2005 13:26:31 +0000 (UTC), jicman  
<jicman_member@pathlink.com> wrote:
> However, there is another way you could do this:
>
> |int ii;
> |foreach (int i; char c; string)
> |{
> |  ii = i;
> |  if(c == 'e')
> |    break;
> |}
>
> After the loop is processed, ii will have the value that i had at the  
> time of
> encounter or loop completion.
>
> just a thought...

In this code ii is string.length-1 if it finds nothing, or matches the  
last element.

Regan
August 04, 2005
Re: foreach annoyance
On Thu, 04 Aug 2005 08:23:29 -0700, Burton Radons  
<burton-radons@smocky.com> wrote:
> Regan Heath wrote:
>> Simple question.
>>  Does anyone else find themselves with this problem?
>>  //contrived example, actual values/data not important
>> char[] string = "abcdefgh";
>> foreach(char c; string)
>> {
>>   if (c == 'e') break;
>> }
>> //at this point I'd like to do something if the item wasn't found..  
>> how  can I tell?
>
> I've found that this problem is symptomatic of over-complex control flow  
> and should be delegated into a function (inner or outer) which tells you  
> whether it found any entries and possibly acts on found entries if it  
> can handle more than one.  A method which goes along this path will  
> become harder and harder to manipulate easily.  Inner functions aren't  
> just for doing a task more than once; they also act as  
> self-documentation and control flow, reducing the core function to a  
> series of modular parts to manipulate at will.
>
> This reminds me that I need to barge in on Parcel code and do some  
> refactoring, I even use goto a couple times.

Ok, can you give me a quick example?

We're you thinking...

void func()
{
  char[] string = "abcdefg";

  bool findItem(char item)
  {
    foreach(char c; string)
    {
      if (c == item) return true;
    }
    return false;
  }

  if (!findItem('e')) {
  }

  ..etc..
}

>> I can tell when using a for loop:
>>  int i;
>> for(i = 0; i < string.length; i++)
>> {
>>   if (string[i] == 'e') break;
>> }
>> if (i == string.length) {} //not found
>>  But I thought the idea was to use foreach so that I can change the   
>> container type at a later date with no other changes required.
>
> It's no substitute for interfaces or templating.

I thought the idea was that foreach was a template for iterating over a  
container.

Regan
August 04, 2005
Re: foreach annoyance
import std.stdio;
void main() {
 char[] string = "abcdfgh";
 foreach(char c; string)
   {
     if (c == 'e') goto found;
   }
 writefln("not found");
found:
 writefln("done");
}


Hehe..  Might just be an acceptable use of "goto".  Doesn't require the
use of an extra variable, or an extra operator within the loop.


On Fri, 2005-08-05 at 00:27 +1200, Regan Heath wrote:
> char[] string = "abcdefgh";
> foreach(char c; string)
> {
>    if (c == 'e') break;
> }
August 05, 2005
Re: foreach annoyance
Hi,

>import std.stdio;
>void main() {
>  char[] string = "abcdfgh";
>  foreach(char c; string)
>    {
>      if (c == 'e') goto found;
>    }
>  writefln("not found");
> found:
>  writefln("done");
>}

I'm guessing you wanna throw a return in there. Otherwise the "not found" case
also reaches the "found" case. No?

--AJG.
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home