15

I came across this question a second ago, and I'm pulling some of the material off of there: Is there a name for the 'break n' construct?

This appears to be a needlessly complex way for people to have to instruct the program to break out of a double-nested for loop:

for (i = 0; i < 10; i++) {
    bool broken = false;
    for (j = 10; j > 0; j--) {
        if (j == i) {
            broken = true;
            break;
        }
    }
    if (broken)
        break;
}

I know textbooks like to say goto statements are the devil, and I'm not fond of them at all myself, but does this justify an exception to the rule?

I'm looking for answers that deal with n-nested for loops.

NOTE: Whether you answer yes, no, or somewhere in between, completely close-minded answers are not welcome. Especially if the answer is no, then provide a good, legitimate reason why (which is not too far from Stack Exchange regulations anyway).

6
  • 2
    If you can write the inner loop condition as for (j = 10; j > 0 && !broken; j--) then you wouldn't need the break; statement. If you move the declaration of broken to before the outer loop begins, then if that could be written as for (i = 0; i < 10 && !broken; i++) then I think no break;s are necessary. Feb 6, 2014 at 21:59
  • 8
    Given the example is from C# - the C# reference on goto: goto (C# Reference) - "The goto statement is also useful to get out of deeply nested loops."
    – user40980
    Feb 6, 2014 at 22:27
  • OMG, I never knew c# had a goto statement! The things you learn on StackExchange. (I can't remember the last time I wished for a goto statement capability, it doesn't seem to ever come up).
    – John Wu
    Feb 6, 2014 at 23:40
  • 6
    IMHO it really depends on your "programming religion" and current moonphase. For me (and most people around me) its fine to use a goto to get out of deep nests if the loop is rather small, and the alternative would be a superfluous check of a bool variable introduced just to have the loop condition break. This more so when you want to break out halfway through the body, or when after the innermost loop there is some code that would need to check the bool too.
    – PlasmaHH
    Feb 7, 2014 at 10:29
  • 1
    @JohnWu goto is actually required in a C# switch to fall-through to another case after executing any code inside the first case. That is the only case I have used goto, though.
    – Dan Lyons
    Feb 7, 2014 at 18:17

7 Answers 7

34

The apparent need for a go-to statement arises from you choosing poor conditional expressions for the loops.

You state that you wanted the outer loop to continue as long i < 10 and the innermost one to continue as long as j > 0.

But in reality that's not what you wanted, you simply didn't tell the loops the real condition you wanted them to evaluate, then you want to solve it by using break or goto.

If you tell the loops your true intentions to begin with, then no need for breaks or goto.

bool alsoThis = true;
for (i = 0; i < 10 && alsoThis; i++)
{    
    for (j = 10; j > 0 && alsoThis; j--)
    {
        if (j == i)
        {
            alsoThis = false;
        }
    }
}
5
  • See my comments on the other answers. The original code prints i at the end of the outer loop, so short-circuiting the increment is important to making the code look 100% equivalent. I'm not saying anything in your answer is wrong, I just wanted to point out that breaking the loop immediately was an important aspect of the code.
    – p.s.w.g
    Feb 7, 2014 at 0:36
  • 1
    @p.s.w.g I don't see any code printing i at the end of the outer loop in OP's question. Feb 7, 2014 at 0:43
  • OP referenced the code from my question here, but left out that part. I didn't downvote you, BTW. The answer is entirely correct in regard to correctly choosing loop conditions.
    – p.s.w.g
    Feb 7, 2014 at 0:47
  • 3
    @p.s.w.g I guess if you really really really really want to justify a goto, you can device a problem that needs one, on the other hand, I've been programming professionally for two decades and haven't a single real world problem that needs one. Feb 7, 2014 at 0:52
  • 1
    The purpose of the code was not provide a valid use case for goto (I'm sure there are better ones), even if it spawned a question which seems to use it as such, but to illustrate the basic action of break(n) in languages which didn't support it.
    – p.s.w.g
    Feb 7, 2014 at 0:58
33

In this instance, you could refactor the code into a separate routine, and then just return from the inner loop. That would negate the need to break out of the outer loop:

bool isBroken() {
    for (i = 0; i < 10; i++)
        for (j = 10; j > 0; j--)
            if (j == i) return true;

    return false;
}
3
  • 2
    Which is to say that yes, what you show is a needlessly complex way to break from nested loops, but no, there is a way to refactor that doesn't make a goto more appealing.
    – Roger
    Feb 6, 2014 at 22:12
  • 2
    Just a side note: in the original code it prints i after the end of the outer loop. So to really reflect that i is the important value, here the return true; and return false; should both be return i;.
    – p.s.w.g
    Feb 6, 2014 at 23:15
  • +1, was just about to post this answer, and believe this should be the accepted answer. I can't believe the accepted answer got accepted because that method only works for a specific subset of nested loop algorithms, and doesn't necessarily make the code much cleaner. The method in this answer works generally.
    – Ben Lee
    Feb 23, 2014 at 2:18
6

No, I don't think a goto is necessary. If you write it like this:

bool broken = false;
saved_i = 0;
for (i = 0; i < 10 && !broken; i++)
{
    broken = false;
    for (j = 10; j > 0 && !broken; j--)
    {
        if (j == i)
        {
            broken = true;
            saved_i = i;
        }
    }
}

Having &&!broken on both loops lets you break out of both loops when i==j.

To me, this approach is preferable. The loop conditions are extremely clear: i<10 && !broken.

A working version can be seen here: http://rextester.com/KFMH48414

Code:

public static void main(String args[])
{
    int i=0;
    int j=0;
    boolean broken = false;
    for (i = 0; i < 10 && !broken; i++)
    {
        broken = false;
        for (j = 10; j > 0 && !broken; j--)
        {
            if (j == i)
            {
                broken = true;
                System.out.println("loop breaks when i==j=="+i);
            }
        }
    }
    System.out.println(i);
    System.out.println(j);
}

Output:

loop breaks when i==j==1
2
0
7
  • Why do you even use broken in your first example at all? Just use break on the inner loop. Also, if you absolutely must use broken, why declare it outside the loops? Just declare it inside the i loop. As for the while loop, please don't use that. I honestly think it manages to be even less readable than the goto version.
    – luiscubal
    Feb 6, 2014 at 22:30
  • @luiscubal: A variable named broken is used because I don't like using the break statement (except in switch-case, but that's about it). It's declared outside the outer loop in case you want to break ALL loops when i==j. You're right, in general, broken only needs to be declared before the outermost loop that it will break. Feb 6, 2014 at 22:37
  • In your update, i==2 at the end of the loop. The success condition should also short-circuit the increment if it is to be 100% equivalent to a break 2.
    – p.s.w.g
    Feb 6, 2014 at 22:54
  • 2
    I personally prefer broken = (j == i) rather than the if and, if the language allows it, putting the broken declaration inside the outer loop initialisation. Though it's hard to say these things for this specific example, as the entire loop is a no-op (it returns nothing and has no side effects) and if it does something, it possibly does that inside the if (though I still like broken = (j ==i); if broken { something(); } better personally)
    – Martijn
    Feb 6, 2014 at 22:55
  • @Martijn In my original code it prints i after the end of the outer loop. In other words the only really important think is when exactly it breaks out of the outer loop.
    – p.s.w.g
    Feb 6, 2014 at 23:10
3

The short answer is "it depends". I advocate to choose regarding legibility and understandability of your code. The goto way might be more legible than tweaking the condition, or vice versa. You might also take into account least surprise principle, guideline used in the shop/project and consistency of the code base. For instance, goto to leave switch or for error handling is a common practice in the Linux kernel code base. Also note that some programmers might have problems reading your code (either raised with the "goto is evil" mantra, or just not learned because their teacher think so) and a few of them might even "judge you".

Generally speaking, a goto going further in the same function is often acceptable, especially if the jump is not too long. Your use case of leaving a nested loop is one of the known example of "not so evil" goto.

2

In your case, goto is enough of an improvement that it looks tempting, but it's not so much of an improvement that it's worth breaking the rule against using them in your code base.

Think of your future reviewer: s/he will have to think, "Is this person a bad coder or is this in fact a case where the goto was worth it? Let me take 5 minutes to decide this for myself or research it on the internet." Why on earth would you do that to your future coder when you can not use goto entirely?

In general this mentality applies to all "bad" code and even defines it: if it works now, and is good in this case, but creates effort to verify it's correct, which in this era a goto will always do, then don't do it.

That being said, yes, you produced one of the slightly thornier cases. You may:

  • use more complex control structures, like a boolean flag, to break out of both loops
  • put your inside loop inside its own routine (likely ideal)
  • put both loops in a routine and return instead of breaking

It's very hard for me to create a case where a double-loop isn't so cohesive that it shouldn't be its own routine anyway.

By the way, the best discussion of this I've seen is Steve McConnell's Code Complete. If you enjoy thinking of these problems critically I strongly recommend reading, even cover-to-cover despite its immensity.

2
  • 1
    It's our job programmers to read and understand code. Not just simplistic code unless that is the nature of your codebase. There are, and will always be exceptions to the generality (not rule) against using gotos because of the understood cost. Whenever the benefit outweighs the cost is when goto should be used; as is the case for all coding decisions in software engineering.
    – snakehiss
    Feb 7, 2014 at 9:41
  • 1
    @dietbuddha there is cost in violating accepted software coding conventions that should be adequately taken into account. There is cost in increasing the diversity of control structures used in a program even if this control structure may be a good fit for a situation. There is cost in breaking static analysis of code. If this is all still worth it then who am I to argue.
    – djechlin
    Feb 7, 2014 at 13:02
1

TLD;DR: No in specific, yes in general

For the specific example you used I would say no for the same reasons many other have pointed out. You can easily refactor the code into an equivalently good form which eliminates the need for the goto.

In general the proscription against goto is a generality based on the understood nature of goto and the cost of using it. Part of software engineering is making implementation decisions. Justification of those decisions occurs by weighing the cost against the benefits and whenever the benefits outweigh the cost the implementation is justified. Controversy arises because of different valuations, both of cost and benefit.

The point is there will always be exceptions where a goto is justified, but only for some because of different valuations. The pity is many engineers simply exclude techniques in purposeful ignorance because they read it on the internet rather than taking the time to understand why.

1
  • Can you recommend any articles on the goto statement further explaining the cost?
    – Thizzer
    Aug 10, 2016 at 21:18
-1

I don't think this case justifies an exception, as this can be written as:

def outerLoop(){
    for (i = 0; i < 10; i++) {
        if( broken?(i) )
          return; // broken
    }
}

def broken?(i){
   for (j = 10; j > 0; j--) {
        if (j == i) {
            return true; // broken
        }
   }
   return false; // not broken
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.