views:

1355

answers:

15

Hello, I want to know if it is possible to end a for loop in C++ when an ending condition (different from the reacheing right number of iterations) is verified. For instance:

for (int i = 0; i < maxi; ++i)
    for (int j = 0; j < maxj; ++j)
        // But if i == 4 < maxi AND j == 3 < maxj, 
        // then jump out of the two nested loops.

I know that this is possible in Perl with the next LABEL or last LABEL calls and labeled blocks, is it possible to do it in C++ or I should use a while loop?

Thank you.

+1  A: 

You can use labels, something along the lines of:

Outer:
for(...)
{
    Inner:
    for(...)
    {
    if(condition)
        {
        goto End;
        }
    }
}
End:

In Java you can pass labels to break I think?

Edit - Changed the goto to End rather than Outer, however I don't think the negative rep is justified. This answer gives the simplest way to do it.

Dynite
http://xkcd.com/292/ :)
Gerald Kaszuba
Now it's legal, but it will be an infinite loop. Your goto just starts the outer loop over again
brien
(answer previously said goto Outer) - You cannot say whether it will be an infinite loop until you know the condition expression which i've not included!
Dynite
+8  A: 

You can't jump out of two loops with a single break instruction, but you could use a goto to jump from the inner loop right outside.

If the goto is localized, and means there is less logic than otherwise I think it is perfectly acceptable code. Having extra flag variables, or hoisting the iterator variable out of the inner loop so you can compare it in the outer loop does not make for easier to understand code IMHO.

Rob Walker
+4  A: 
for (int i = 0; i < maxi; ++i)
{
    int j = 0;
    for (j = 0; j < maxj; ++j)
    {
         if (i == 4 && j == 3) // i < maxi and j < maxj otherwise we would not be here
             break; // exit inner loop
    }
    if (i == 4 && j == 3) // i < maxi and j < maxj otherwise we would not be here
        break; // exit outer loop
}
Marcin Gil
+4  A: 

You could use a goto statement, but that's generally considered a bad practice.

Your other option is to do something like this

int i;
int j = 0;
for (i = 0; i < maxi && !(i==4 && j==3); ++i)
    for (j = 0; j < maxj && !(i==4 && j==3); ++j)
brien
+4  A: 

You can't jump out like this in C/C++:

for (...)
{
  for (...)
  {
    // from here...
  }
}
// ...to here

without the use of goto. You need a construct like:

for (...)
{
  bool
    exit = false;

  for (...)
  {
    if (do_exit)
    {
      exit = true; // or set outer loop counter to end value
      break;
    }
  }
  if (exit)
  {
    break;
  }
}

Alternatively, use throw and catch - but that's not great since throw should really be used for exceptions and not flow control.

A clean way is to make the inner loop a function:

bool F ()
{
  if inner loop terminates, return false else return true
}

void G ()
{
  for (...)
  {
    if (!F ())
    {
      break;
    }
  }
}

Skizz

Skizz
An even cleaner way would be to put both loops inside a function and return (rather than break) from the inner loop when the stopping condition has been reached.
RobH
RobH - if you're happy with mutliple returns then that would also work. I personally try to avoid multiple returns.
Skizz
+1  A: 

I have a few suggestions:

  1. throw.... put the two loops inside a "try {}" and then "catch" the "throw" on the condition.

  2. put the two loops in a method and return on the condition.

  3. Goto isn't evil its the use people put it too.... You can use "goto" it can be the clearest code, especially when handling errors. I haven't used one in 20 years.

Tony

Tony Lambert
Exception handling is expensive in terms of performance, so you should only use try/catch for things that are not under your control (e.g., to catch hardware failures or 'end of file' on disk reads). Option 3 is clear but may not be allowed under some coding standards. ...
RobH
... Option 2 is clear but adds the cost of a function call. However that cost is nowhere near the cost of using try/catch; so of these 3 choices, use either 2 or 3.
RobH
+11  A: 

Let me say this as emphatically (but politely ;-) as I can: The for construct in c-like language is not about counting.

The test expression which determines whether to continue can be anything which is relevant to the purpose of the loop; the update expression doesn't have to be "add one to a counter".

for (int i = 0, j = 0; i < maxi && j < maxj && i != 4 && j != 3;) {
    if (j < maxj) {
        ++j;
    } else {
        j = 0;
        ++i;
    }
}

would be one (fairly arbitrary) way to rewrite.

The point is that if establishing some condition is the point of an interation, it's usually possible to write a loop (using either while or for) in a way that states the continue/terminate condition more explicitly.

(If you could post a description of what's actually happening, it would likely to write something that doesn't look as arbitrary as the above.)

joel.neely
Then again you could use a while loop and it would be more readable in most cases. Though it is good to illustrate that one doesn't have to use the for loop in the normal strict fashion.
Daemin
I agree completely about the use of `while`. My example was artificial due to not knowing what the questioner's loop was really doing.
joel.neely
Needless conditionl to test which loop index you need to increment, which may slow down the loop significantly.
MSalters
@MSalters: Not needless at all. The sample replaced two loops with one, so each iteration either continues within the same "row" of the two-dimensional space being covered, or else starts a new row. That's a decision.
joel.neely
+46  A: 

You can use the return keyword: move the nested loop into a subroutine, invoke the subroutine to run the nested loops, and 'return' from the subroutine to exit [all] the loops.

ChrisW
I like this one possibly even better than the goto statement one...
Alex Baranosky
Why this strikes me as preferable when it is completely equivalent to a goto, I don't know.
Chris Conway
This is by far the best answer, in my opinion. Encapsulating code flow within methods is usually vastly superior to creating one overly complex code block.
Wedge
@Chris every control flow mechanism is equivalent to a goto. At the hardware level there is nothing other than a goto. But the control flow mechanisms we use (for, while, if/else, return) have restrictions which make them more usable than gotos.
Wedge
I think if you're going to do this answer you might as well refactor the code to try and get rid of the two loops, or the need to break out of them.
Daemin
+39  A: 

Despite the "goto considered harmful" arguments, this seems like the perfect place for goto. That's essentially what you are doing in Perl. Seriously... consider the alternatives:

Extra State Variables


for (int i=0; i<maxi; ++i) {
    bool leaveLoop = false;
    for (int j=0; j<maxj; ++j) {
        if (i == 4 && j == 3) {
            leaveLoop = true;
            break; // leave the inner loop
        }
    }
    if (leaveLoop) {
        break; // leave the outside loop
    }
}

Leave by Exception


try {
    for (int i=0; i<maxi; ++i) {
        for (int j=0; j<maxj; ++j) {
            if (i == 4 && j == 3) {
                throw leave_loop();
            }
        }
    }
} catch (leave_loop const&) {
}

Complex Logic


int j = 0;
for (int i=0; i<maxi && !(i==4 && j==3); ++i) {
    for (j=0; j<maxj && !(i==4 && j==3); ++j) {
        // inner loop
    }
}

goto


for (int i=0; i<maxi; ++i) {
    for (int j=0; j<maxj; ++j) {
        if (i==4 && j==3) {
            goto leave_loop;
        }
    }
}
leave_loop:

Is the last one less clear? I don't believe that it is. Is it any more fragile? IMHO, the others are quite error prone and fragile compared to the goto version. Sorry to be standing on the soapbox here but this is something that has bothered me for a while ;)

The only thing that you have to be congnizent of is that goto and exceptions are pretty similar. They both open up the opportunity for leaking resources and what not so treat them with care.

D.Shawley
fascinating. The goto version definitely looks most clear.
Alex Baranosky
For completeness, you should probably also add Chris' solution, making a separate function.
tfinniga
You're missing one of the cleaner options which is putting the break condition in the condition statement of the for loops, like @Gilad Naor suggests.
Nathan Fellman
+6  A: 
bool done = false;

for (int i = 0; i < maxi && !done; ++i)
    for (int j = 0; j < maxj && !done; ++j)
        if (i == 4 && i < maxi && j == 3 && j < maxj )
             done = true;
        else {
        }

Or you could just goto. Or not :-)

Gilad Naor
This is what I would do
Nathan Fellman
`bool done = false;` and then `done = true;` please!
Dan
@Dan - correct. I'll update the answer. Spent too much time on C. Or is it too much time in C++ now-a-days? :-)
Gilad Naor
@Gilad Naor: My inner nitpicker feels better now :) When I saw your answer last night I checked to see if the question was tagged C or C++ before commenting... but then again, C has `<stdbool.h>` now so maybe that doesn't matter as much.
Dan
+7  A: 

From all of the suggestions above I would avoid using the try/catch mechanism because exceptions should be reserved for exceptional circumstances, and not normal control flow.

Using two breaks is alright if you can create the second condition appropriately. Using a boolean for this purpose would also be good, and you could even wire it into the condition of each for loop. For example:

bool exit_loops = false;
for (int a = 0; a < A && !exit_loops; ++a)
{
    for (int b = 0; b < B && !exit_loops; ++b)
    {
        if (some_condition) exit_loops = true;
    }
}

Though if you're using more than two loops it might be more appropriate to wrap them in a function and just use return to exit the function (and all of the loops too). Then again you could refactor the code in a way that can eliminate all but one of the loops, be it by calling a function to perform the inner loop code, etc.

Lastly don't be afraid of using a goto in this circumstance, usually goto's are bad unstructured programming, but in some cases (like this) they are very useful.

Daemin
I always do it this way too, simple, readable, why introduce a goto? The only change I'd make is I'd go; for ( int a=0, bool done=false; a<A ++a ) // a little more consistent this way I think
Bill Forster
To be honest personally I'd refactor the algorithm to not require breaking out of nested loops at all.
Daemin
I'd do it this way - it's not simpler but I don't want to spend time justifying using a goto to a bunch of developers.
Martin Beckett
+1  A: 

I've always tried to stay away from goto statements (it was always looked down upon at school and my job for some reason). I would use something like what Daemin suggested.

+2  A: 

Reading code should not be like reading a detective book(which always need to be figured out)...

an example:

Java:

iterate_rows:
for (int i = 0; i < maxi; ++i)
{       
    for (int j = 0; j < maxj; ++j)
    {
        if (i == 4 < maxi && j == 3 < maxj) 
            break iterate_rows;
        else
            continue iterate_rows;
    }   
}

You don't need to figure out what break iterate_rows do, you just read it.

C++:

//iterate_rows:
for (int i = 0; i < maxi; ++i)
{
    for (int j = 0; j < maxj; ++j)
    {
        if (i == 4 < maxi && j == 3 < maxj) 
            goto break_iterate_rows;
        else
            goto continue_iterate_rows;
    }

continue_iterate_rows:;
}
break_iterate_rows:;

*goto break_iterate_rows* is just a visible version of break iterate_rows

If you confine your use of goto and labels on this kind of code only, you will not be figuring out the intent. Limiting your use of goto and labels on this kind of code will make you just read the code, not analyzing or figuring it out. You'll not be accused of being an evil programmer.

And if you really limit your gotos in this kind of code, you'll be able to develop a habit of not needing to figure out what those darn gotos do in your code. Added benefit is you don't have to introduce booleans and track them(which imho, leads you to detecting the code, making it a bit unreadable, which defeats the very purpose of avoiding gotos)

P.S.

Pair those labels with comments(before the loop), by the time you read past those lines with goto statement you already know the intent of those gotos

Michael Buen
+1  A: 

Another reason for reconsidering the for construct entirely is that its scoping prevents access to the controlled variables after the loop has terminated. The value(s) of the variable(s) being mutated in the loop may useful for a variety of reasons (e.g. to distinguish success from failure in a search) which would otherwise require extra variables to preserve that information after scope exit. Here's a small example that searches a square array named a for a target value (assuming that SIZE is non-zero, otherwise no search is necessary!):

int i = 0;
int j = 0;
while (i < SIZE && a[i][j] != target) { // still in array but not at target
    if (SIZE <= ++j) {                  // fallen off the end of a row
        j = 0;
        ++i;
    }
}

The subsequent code can use i < SIZE to determine whether the desired value was located.

Another advantage of the above is the flexibility. Suppose we're now informed that the values in rows of a are ascending, so the remainder of a row is irrelevant if a value larger than target is encountered. It's easy to know exactly what change to make, and where to make it. Because that new information allows us to abandon the current row, only the inner decision is affected, becoming:

    if (target < a[i][j] || SIZE <= ++j) { // can't be in row or fallen off end
    ...

I'm seeing more of the newer languages (especially functionally-oriented ones) abandoning the old "counting" loop construct; that's probably a good thing, as it encourages us to think about the meaning of the loop, rather than simply counting.

joel.neely
+1  A: 

The best way I've seen involves macros and gotos, but it's actually quite nice (the linked to post starts by talking about Perl, but the last paragraph or so introduces the macros).

It allows you to write code like:

named (LOOPS) for (i=1; i<10; i++) {
    for (j=1; j<10; j++) {
        for (j=1; j<10; j++) {
            /* Process data[i][j][k] here */
            if (data[i][j][k] < threshold) break(LOOPS);
        }
    }
}
Max Lybbert
That is truly horrid.
Dan