Best way to break out of nested loops

Pages: 12
I've seen time and time again people asking how to break out of nested loops (mostly for loops but others to), I've even seen some people suggest the following...
1
2
3
4
5
6
7
8
9
try{
  while(/*stuff*/){
    while(/*stuff*/){
      if(/*stuff*/)
        throw;
    }    
  }
}
catch(...){}


Dear god why?
Is it too hard just to set all the loop conditions so that they don't loop, and then break out of your inner most loop to trigger the exit?

Something like this is what I think is best... But what's your opinion?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Make temporary scope boolean
{
  bool stay = true;
  while(/*stuff*/ && stay){
    for(i=0; i</*stuff*/ && stay; i++){
      while(/*stuff*/){
        if(/*stuff*/){
          stay = false;
          break;
        }
      }//Breaks out of while into for loop
    }//For loop condition is now false due to value of "stay"
  }//Also false due to "stay"
}
Last edited on
closed account (z05DSL3A)
Or just use goto.
Sometimes you need to exit immideately: there can be other code in parent loop after nested which should not be executed.

There are some ways to do that:
1) Create some boolean condition, and insert conditional break ewerywhere:
1
2
3
4
5
6
7
8
9
10
11
12
13
bool stop = false;
while(/*...*/) {
    //...
    while(/*...*/) {
        //...
        if (/*...*/) {
            stop = true;
            break;
        }
    }
    if (stop) break
    //...
}

2) Use goto: this is maybe last use allowed by coding standards.
3) Move loops into own (inline) function. Will not work well if there are many parameters to pass and one-use function is not good.
4) Use in-place lambda call. Somewhat ugly, but might be better than previous approach.
5) Use throw-catch mechanics, however it was not created for that.
MiiNiPaa wrote:
Sometimes you need to exit immideately: there can be other code in parent loop after nested which should not be executed.
Yeah fair enough.

MiiNiPaa wrote:
2) Use goto: this is maybe last use allowed by coding standards.
I know generally goto is considered bad practice because it has the capability of exiting its own scope and in some cases (definitely with functions) will skip any automatic cleanup, whether this is cleanup is required in something like a for loop I don't know.

MiiNiPaa wrote:
3) Move loops into own (inline) function. Will not work well if there are many parameters to pass and one-use function is not good.
4) Use in-place lambda call. Somewhat ugly, but might be better than previous approach.

Yes returning is the other common method I use, not used it with lambdas (although I haven't used lambdas at all in anything yet so meh), but this is still a sensible way I think.

MiiNiPaa wrote:
5) Use throw-catch mechanics, however it was not created for that.

This is what's considered exception abuse, mostly because exceptions are quite expensive to perform, and not designed just as a convenience like this.
Last edited on
in some cases (definitely with functions) will skip any automatic cleanup
It is not the case. It handles decstruction well, and it cannot skip initialization of variables. And it is severily restricted (compared to C)
http://en.cppreference.com/w/cpp/language/goto
... will skip any automatic cleanup.

That might be true for C, but the scoping rules for C++ are different: http://en.cppreference.com/w/cpp/language/goto

The main reason goto is discouraged is because lazy people use it as a catch all solution to program flow control where other, far better options are available. It also makes the code incredibly difficult to read. Have you ever had the chance to edit a 50+ line batch file? If the opportunity comes up, do your self a favor and just rewrite the damn thing; it's what you'll end up doing anyway.
Last edited on
closed account (3hM2Nwbp)
int x = 1 / 0 might do it..amongst other things.
Last edited on
abort() also works well, for that matter.
MiiNiPaa wrote:
It is not the case. It handles decstruction well, and it cannot skip initialization of variables. And it is severily restricted (compared to C)
http://en.cppreference.com/w/cpp/language/goto
Computergeek01 wrote:
That might be true for C, but the scoping rules for C++ are different: http://en.cppreference.com/w/cpp/language/goto


Okay, fair enough. I suppose that's not that bad then, thanks for correcting me...
Is it just considered bad practice due to it's previous nature? Or simply because of the "ugly" flow control... C++ is already capable of jumping back on itself from previous parts of code though through the use of loops so it's not that different really, just change "while" for a label and change "continue" for "goto"
goto introduces unexpected flow of the program: with loops you will know where execution can jump (to the beginning or end of the loop) and from where (from inside the loop).
With goto it is less apparent: jump can be made fro anywhere in current functio with some restrictions, and to find those restriction you will have to study whole function and its flow.
Basicly goto can make code harder to read and reason about. Also goto might break optimisation.

C++ is already capable of jumping back on itself from previous parts of code though through the use of loops so it's not that different really,
Try to write two intersecting loops. Luckily it is impossible, but with goto it is quite possible to write construct you will struggle to explain its behavior:
1
2
3
4
5
6
7
L1:
//...
L2:
//...
if(/*...*/) goto L1;
//...
if(/*...*/) goto L2;



I use goto for things like this. Here's an example (C#):
1
2
3
4
5
6
7
8
9
10
11
12
case 'q':
    while (true) {
        Console.Write(Strings.ConfirmQuit);
        switch (Console.ReadLine().ToLower()) {
            case "":  // Fall through.
            case "y": Environment.Exit(0); break;
            case "n": goto out_of_confirm;
            default:  break;
        }
    }
out_of_confirm:
    break;

The rule of thumb I follow is to only go forwards, never back. I have broken that rule once or twice, though.

@MiiNiPaa
That's pretty much what a compiler would generate for any control flow statement and if you're reading or writing assembly directly you will need to be able to parse constructs like that.
Last edited on
> C++ is already capable of jumping back on itself from previous parts of code
> though through the use of loops so it's not that different really

+1

>> Try to write two intersecting loops. Luckily it is impossible, but with goto it is quite possible

What is required for a programming language to be Turing complete is
a. support for conditional branching
b. the ability to modify an arbitrary number of memory locations (maintain an arbitrary number of variables)

The specific construct goto is not a fundamental requirement of Turing-completeness; anything with equivalent expressive power would do. If we have a programming language with variables and assignment, and bounded or unbounded loops, it can do everything that a language with if-else and goto can do.

1
2
3
4
for( int i = 0 ; (i < 1) && !condition ; ++i )
{ 
     // do something
}


is just a another way of writing
1
2
3
4
5
    if( condition ) goto next ;
    { 
         // do something 
    }
next:


If we actually need to write two intersecting loops in C++, doing it without the use of goto is possible; though the misguided and misinformed zeal to avoid 'evil' may result in spaghetti-like code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>

int main()
{
    int i = 6 ;
    int j = 6 ;
    char jump = 'n' ;

    for(  ; i < 10 ; ++i )
    {
        if( i > 5 )
        {
            std::cout << "loop one begin \n" ;
            std::cout << "first part of loop one code\n" ;

            std::cout << "jump to loop two? " ;
            if( std::cin >> jump )
            {
                std::cout << "answer: '" << jump << "'\n" ;

                if( jump == 'y' || jump == 'Y' ) /* goto loop two */ { i = 0 ; j = 6 ; continue ; }
            }

            else return 0 ;

            std::cout << "second part of loop one code\n" ;
            std::cout << "loop one end \n" ;
        }

        else
        {
            for( ; j < 10 ; ++j )
            {
                if( j > 5 )
                {
                    std::cout << "loop two begin \n" ;
                    std::cout << "first part of loop two code\n" ;

                    std::cout << "jump to loop one? " ;
                    if( std::cin >> jump )
                    {
                        std::cout << "answer: '" << jump << "'\n" ;

                        if( jump == 'y' || jump == 'Y' ) /* goto loop one */ { j = 20 ; i = 6 ; continue ; }
                    }

                    else return 0 ;

                    std::cout << "second part of loop two code\n" ;
                    std::cout << "loop two end \n" ;
                }
            }
        }
    }
}

http://coliru.stacked-crooked.com/a/93c0a99f2f1d110a
@crisname however purpose of high level language is to make writing and reading code easier. Reading such construct is harder than simple loops.

JLBorges wrote:
If we actually need to write two intersecting loops in C++, doing it without the use of goto is possible
Yes, such simulation is possible. I meant such construct is not directly supported.

I support allowing usage of goto for things like exiting nested loops.

However I do thing that in general goto decreases readbility and ability to reason about code, so each use of goto should be considered separately and only used if any alternatives like redesigning control flow leads to even worse readability or perfomance.

Your example would be cleaner if we would have counter of consequitive answers, choose between loop bodies depending on some variable and move question (and probably counter manipulation) in own function/function object.

EDIT: changed example without changing behavior (I think): http://coliru.stacked-crooked.com/a/18b9e623487f72c2
I think it is clear enough and goto variant exibiting same behavior will not be easier to understand.

Last edited on
> I think it is clear enough and goto variant exibiting same behavior will not be easier to understand.

At the very best, that is a matter of personal opinion.

I deem the version with goto to be a lot easier to read and a lot easier to understand.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bool jump( const std::string& to ) ;

int main()
{
    one:
        std::cout << "loop one begin \n" ;
        std::cout << "first part of loop one code\n" ;

        if( jump("two") ) goto two ;

        std::cout << "second part of loop one code\n" ;
        std::cout << "loop one end \n" ;
        goto one ;

    two:
        std::cout << "loop two begin \n" ;
        std::cout << "first part of loop two code\n" ;

        if( jump("one") ) goto one ;

        std::cout << "second part of loop two code\n" ;
        std::cout << "loop two end \n" ;
        goto two ;
}

http://coliru.stacked-crooked.com/a/08aa56ed529bf9ec
Your code is simplified and does not exibit same behavior as your original code.

If you add code to handle that, I think both versions would be roughtly equivalent in readability. Choosing one over the other would really be a matter of preference.
I will retract my statement about breaking optimization.
If you find yourself using a "goto", have a re-look at the structure of your code; chances are that it can be improved.

What you wrote is related to what I was trying to say: if you find yourself in situation when you cannot easily write something using normal flow control and want to use goto, you should stop and rethink the situation.

Well, at least there is unanimous consent about using goto to break nested loops.
When faced with this situation I usually find that putting all the loops in a function is fairly easy. Then you can just return from the function.

I've often thought that a nice extension to the language would be the option to add the structure that you want to break out of, .e.g.:
1
2
3
4
5
for (...) {
    ...
    break for;
    ...
}


If you were actually trying to break out of the wrong structure it would be a compiler error. Imagine that the loop above might be dozens of lines of code. Then someone makes a change:
1
2
3
4
5
6
7
8
for (...) {
    ...
    do { // added 10 years later
        ....    
        break for;  // now generates a compiler error
    } while(...); // this was added 10 years later too
    ...
}

Without the language extension, the break would break out of the do-while instead of the for loop. So adding code in one area (remember this loop is very long) causes code in another area to break. With the extension, you can catch errors like this, at least some of the time, which is better than none of the time.

Now you could break out of multiple structures easily:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
for(...) {
    switch (x) {
    case 1:
        ...
        break switch for; // execution resumes at line 14
    case 2:
        ...
        break switch; // execution resumes at line 12 as normal
    default:
        ...
    }
    ...
}
....

You are describing Java's label break: you can name your control structures and break from any one of currently executed ones.
http://stackoverflow.com/a/14960484
I've not seen java breaks but i know VB has this style, which i do find quite useful... VB is "exit [structure]"
If i need to break a loop i typically just use "exit" rather than staying the loop name, and only use the loop name if i have 2 or more nested loops and the style of loop I need to break is unique (Ie you can't break the outer for in a for for nest, but you can break the for or while in a for while nest).

What might be interesting is a break syntax that takes a numeric value to the number of levels to break... of course this isn't required as we've got plenty of other ways out :D

And i understand all that's been mentioned about goto, and other methods, thanks for your time

EDIT: oh right, i didn't look at that java link, but that's pretty cool
Last edited on
PHP uses the break syntax with a number of levels to break. It's a bad idea. If you want to surround some code in a new if statement or loop, you have to then actually go through and properly update the code. Labeled breaks like Java does it are better, in my opinion.
Pages: 12