tags:

views:

703

answers:

13

Suppose I need to break out of three or four nested for loops at once at the occurence of some event inside the innermost loop. What is a neat way of doing that?

what I do is use flags like this:

int i, j, k;
int flag1 = 0;
int flag2 = 0;

for (i = 0; i < 100; i++) {
    for (j = 0; j < 100; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50) {
                flag1 = 1;
                flag2 = 1;
                break;
            }
        }
        if (flag1 == 1)break;
    }
    if (flag2 == 1)break;
}

I don't think this is particularly neat.

How would you accomplish the same thing? (w/o using jumps)

+16  A: 

If you're using Java, you can associate labels with each for block and then reference the label after a continue statement. For example:

outerfor:
for (int i=0; i<5; i++) {
    innerfor:
    for (int j=0; j<5; j++) {
        if (i == 1 && j == 2) {
             continue outerfor;
        }
    }
}
Asaph
Many languages (like Perl) allow this kind of label behavior, which is a good thing, and it should be used where applicable. So I would +1 if this applied to C. (I know OP is interested in other languages, but he's probably more interested in C.)
Chris Lutz
@Chris Lutz: fair enough. FWIW: When the OP originally posted, there was no mention of what language he/she was using. Hence all the comments asking what language. The c tag was added after I posted.
Asaph
This same trick works in JavaScript too :)
ephemient
+7  A: 

If you're using PHP, you can specify how many levels you want to break out of by placing a number right after the break command.

break 3;

The above will break you out of three (3) levels of the loop.

random
OP cleared up later in the game about the language they were using. Though they still wondered about other languages supporting breaking out of loops.
random
i voted up though he clearly is not using PHP in his example
Steve Obbayi
There wasn't a downvote on this answer, just pointing out what in Baal's name PHP is doing showing its face here.
random
This sounds like a fun thing to debug if you were to refactor your code so that the depth of your if/for-statements change.
Simon Svensson
Sounds like a disaster waiting to happen...
Buggabill
+5  A: 

Just a wee bit better.

int i, j, k;
int flag1 = 0;
int flag2 = 0;

for (i = 0; i < 100 && !flag2; i++) {
    for (j = 0; j < 100 && !flag1; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50) {
                flag1 = 1;
                flag2 = 1;
                break;
            }
        }
    }
}

But if you really need to have those loops, then it makes sense explicitly declaring in each loop what conditions must hold for it to continue, for readability.

moogs
In my opinion, this isn't really better. The sheer volume of stuff being put into the `for` statement starts to look like line noise.
Chris Lutz
What's the point of having multiple flags? Just have one. Call it `done`. Much clearer.
Alan
just copied the sample. i'm assuming the flags are not that simple and just a representative of the actual code.
moogs
then again, if it's as simple as what he said (breaking out immediately). you're right
moogs
+61  A: 

use goto. it's clean and simple.

EffoStaff Effo
Seriously? Ever heard of Edsger Dijkstra's "Go To Considered Harmful" -- http://en.wikipedia.org/wiki/Considered_harmful
Asaph
So many people think that a bunch of flags is somehow better than goto just because of some rhetoric they have heard sometime
gnibbler
no, no gotos please. its clean and simple but **unhealthy**.
Amoeba
@Asaph, They are discussing excessive use of goto in an unstructured way.
gnibbler
@Asaph - Ever heard of fanaticism? Dijkstra said "Harmful", not "The Spawn of Satan." You're missing his point entirely.
Chris Lutz
if the code is as simple as above, then goto is nice and readable and i'm all for it. But if it's as simple as above, then it can be done with only 1 loop.
moogs
@hanifr. At least read the whole paper by Dijkstra before your write goto off.
gnibbler
@hanifr - What's unhealthy about simplicity, cleanliness, clarity and readability? Normally `goto` is _none_ of those things, but every once in a while it becomes the ideal solution to your problem. And nearly every other language has special-case usages for builtin keywords like `continue` or `next` to hide what is essentially this kind of `goto` behind a nicer and more controllable visage, which is identical to what `if` and `while` and `for` statements do. `goto` here is _not bad in any way_
Chris Lutz
+1 this is the only place where i'd give java a point over c++ or c#. Java has a `break <label>` statement that breaks the loop named with a certain label. Neither C nor C# have anything similar, so the only clean way to do it is with `goto`. And @ asaph, think for yourself from time to time, it won't hurt!
Blindy
Yup, `goto` it is. I'm often surprised by lengths some people are willing to go to avoid writing that "cursed" keyword even when it clearly is the best - as in, most obvious, most readable, most maintainable - solution to the problem at hand.
Pavel Minaev
Agreed - goto has its place. Just keep it to where it really does help and not hinder.
Michael Burr
`goto` for the win. Kneejerk reactionary attitudes about a language keyword are ridiculous.
Carl Norum
Put it un a function and use a return. Essentially similar to a break, but it is far cleaner code I think.
Jeremy
Flags < Goto < Refactor + Functions + Return.
GMan
+1 for goto. goto is not evil when used correctly, though being liberal with it is probably bad...
Chinmay Kanchi
I read the question and I were ready to answer goto aswell, though I'd put it in an <irony>-tag :) I personally like flags better here, why? Cause with a goto you ruin the flow of the code, and might not call a destructor or some other essential method ..
cwap
In case anyone wants it, here's a link to Knuth's paper "Structured Programming with go to Statements"http://pplab.snu.ac.kr/courses/adv_pl05/papers/p261-knuth.pdf
bbg
+39  A: 

Put all the loops in a function and just return instead of break.

Foole
Note that, while this neatly avoids an explicit `goto` it's essentially doing the same thing, so if you think this is a good solution you really can't make any argument against the `goto` solution. And note that `goto` may be preferable in the event of complex looping behavior that might make a function call less clear or simple.
Chris Lutz
The difference is that you can't abuse return in the way you can abuse goto. By the way, Java has a labeled break statement that allows you to break out of nested loops, but no goto.
starblue
In C++ return is better because it will respect stack-unwinding - and so destroy any locally scoped variables declared within any of the loops.
Phil Nash
I would definately prefer the return approach to a goto, as in my experience it tends to create cleaner overall code. Not because that goto-s cannot be clean, but if you use a return, you have also isolated the function to one responsibility, executing the loop. If yousing gotos, then you are probably putting more separate concerns in the same function. Of course, this is not a universal truth, and may not be so in this case, but that would be my general position.
Pete
They may be essentially the same thing but code-wise, I think return is far cleaner.
Jeremy
Agreed, this is the correct solution. goto is second best, but if you have three nested for loops and need to break out, you need to refactor your code.
GMan
Return has an added benefit of carrying a value "we've exited because of condition X". With goto you'd have to set up some flag variable for it, and remember that there is a variable.
che
+8  A: 

How would you accomplish the same thing? (w/o using jumps)

Why? Nothing is universally evil, and every put-upon tool has its uses (except gets()). Using goto here makes your code look cleaner, and is one of the only choices we have (assuming C). Look:

int i, j, k;

for (i = 0; i < 100; i++) {
    for (j = 0; j < 100; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50) {
                goto END;
            }
        }
    }
}
END:

Much cleaner than all those flag variables, and it even shows more clearly what your code is doing.

Chris Lutz
And how would you refactor this? You not REALLY want 3 nested loops in your code?
DroidIn.net
I don't know how to refactor this, because the OP isn't being very specific on what his three nested loops are _doing_ in his code. He just wants to break out of all of them from inside one of them, so I provide the answer. Some operations _require_ O(n^3) time to accomplish, but end up being fast enough in practice because they'll usually operate on small data sets and/or the operation performed is simple and easily optimized. Don't try to refactor that which is clear and clean and works just fine.
Chris Lutz
+1  A: 

sometimes you can use trick like this:

for (i = 0; i < 100 && !flag2; i++) {
for (j = 0; j < 100 && !flag1; j++) {
    for (k = 0; k < 100; k++) {
        if (k == 50) {
            k = 100;
            i = 100;
            j = 100;
        }
    }
}

}

or declare addition flag in your loop:

bool end = false;
for(int i =0; i < 1000 && !end; i++) {
   //do thing
   end = true;
}

it costs only a line but clean, I think.

justin

thethanghn
A: 

One way to do it is a state machine. But i would still use goto. It's much simpler. :)

state = 0;
while( state >= 0){
    switch(state){
        case 0: i = 0; state = 1; // for i = 0
        case 1:
            i++; 
            if (i < 100)   // if for i < 100 not finished
                state = 2; // do the inner j loop
            else
                state = -1; // finish loop
        case 2: j = 0; state = 3; // for j = 0
        case 3: 
            j++;
            if (j < 100)  // if j < 100 not finished
                state = 4 // do the inner k loop
            else
                state = 1; // go backt to loop i
            break;
        case 4: k = 0; state = 5;
        case 5:
            k++;
            if (k == 50){
                state = -1;
                break;
            }
            if (k < 100) // if k loop not finished
                state = 5; // do this loop
            else
                state = 3; // go back to upper loop
            break;
        default : state = -1;
    }
}
egon
What is the advantage of a state-machine over a goto?
Alphaneo
Actually non. It's just another way doing these loops and breaking out without goto. I would do this with goto.In some cases it's better to use this. For example if you have a lot jumping from one loop to the other like regExp. RegExp parsers compile these machines because they run fast and they can be optimized.
egon
+3  A: 

goto. This is one of the very few places where goto is the appropriate tool, and is usually the argument presented why goto isn't complete evil.

Sometimes, though, I do this:

void foo() {
    bar_t *b = make_bar();
    foo_helper(bar);
    free_bar(b);
}

void foo_helper(bar_t *b) {
    int i,j;
    for (i=0; i < imax; i++) {
        for (j=0; j < jmax; j++) {
            if (uhoh(i, j) {
                return;
            }
        }
    }
}

The idea is that I get a guaranteed free of bar, plus I get a clean two-level break out of the switch via return.

ts4z
A: 

Dividing by 0 is the surest method I know that will break you out of any number of loops. This works because the DIV assembly instruction doesn't like such silliness.

So, you can try this:

int i, j, k;
int flag1 = 0;
int flag2 = 0;

for (i = 0; i < 100; i++) {
    for (j = 0; j < 100; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50) {
                flag1 = 1;
                flag2 = 1;
                int z = 1 / 0;  // we're outta here!!!
            }
        }
        if (flag1 == 1)break;
    }
    if (flag2 == 1)break;
}

Getting back from the trap that happens on such events left as an exercise for the reader (it's trivial).

DV
A good enough compiler should not compile this (as it is an invalid constant expression), beside this exceptions shall never be used as a replacement for control logic. Advising people to do so seems DIVilish to me :-)
jdehaan
hahaha, golden.
Carson Myers
I'm voting this up because its so funny.
Justicle
A: 

If premature completion of any cycle always means that you have to break the enclosing cycle as well, then you don't need any extra flags. The whole thing might just look as follows

int i, j, k;
for (i = 0; i < 100; i++) {
    for (j = 0; j < 100; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50)
                break;
        }
        if (k < 100) break;
    }
    if (j < 100) break;
}

In my experience, this is what is needed in majority of cases.

AndreyT
+2  A: 

a little bit of silly self-documenting:

int i, j, k;
int done = 0;

for (i = 0; i < 100 && ! done; i++) {
    for (j = 0; j < 100 && ! done; j++) {
        for (k = 0; k < 100 && ! done; k++) {
            if (k == 50) we_are(done);
        }
    }
}

//...

void we_are(int *done) {
    *done = 1;
}

but really, you shouldn't have three nested for-loops. You should consider refactoring into different functions and improving your program's logic instead of doing this.

While I agree that sometimes goto really is the best solution, I think that any problem to which goto is the solution is a result of poor code.

Carson Myers
+1 for the great code. nice.
elcuco
A: 

I'd do something like:

  int i, j, k;

  for (i = 0; i < 100; i++) {
      for (j = 0; j < 100; j++) {
          for (k = 0; k < 100; k++) {
              if (k == 50) {
                  return;
              }
          }
      }
  }
Paul