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
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
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
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
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.
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
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
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
}