Been having a discussion on whirlpool about using break
statements in for
loops. I have been taught and also read elsewhere that break
statements should only be used with switch
statements and with while
loops on rare occasions.
My understanding is that you should only use for
loops when you know the number of times that you want to loop, for example do work on x elements in an array, and while
loops should be used every other time. Thus a for
loop with a break
can be easily refactored into a while
loop with a condition.
At my university, you will instantly fail an assignment if you use break
anywhere but in a switch
statement as it breaks the coding guideline of the university. As I'm still completing my software engineering degree I would like to know from people in the real world.
These generalized rules are rubbish as far as I'm concerned. Use what the language allows in the real world as long as it aids (or doesn't degrade) readability. The guideline against using break
is no different to that against using goto
. The reason behind people not liking them is because it may lead to spaghetti code, hard to follow.
Note the use of two phrases in that sentence above: The first was "guideline" instead of rule - the only rules are those imposed by the standards. Guidelines are for best practices but you have to understand the reasons behind them, not just follow them blindly.
The second was "may lead to" rather than "does lead to". There are situations where break
and its brethren actually lead to more readable code than the alternative (which is often a hugely ugly condition in the looping statement).
For example, they make a lot of sense in finite state machines.
As some have pointed out, break
can lead to post-conditions of a loop being variable. By that, I mean that:
for (i = 0; i < 50; i++) {
if (someCondition) {
break;
}
}
can lead to i
holding an indeterminate value after the loop.
But you should keep in mind that only matter if you actually care what i
is set to after the loop. If the next statement is:
for (i = 0; i < 50; i++) { ... }
then it doesn't matter at all.
A piece of code like:
while (x != 0) {
y = doSomethingWith (x);
if (y == 0) break;
process (y);
z = doSomethingElseWith (x);
if (z == 0) break;
process (z);
x--;
}
violates this guideline and can be refactored into something that doesn't but there is nothing unreadable about this piece of code. You can clearly see all flows of control at a single glance.
You should use the language features that make sense to your situation. Where guidelines should be enforced and where they should be ignored comes with experience.
McConnell, Steve. Code Complete, Second Edition. Microsoft Press © 2004. Chapter 16.2: Controlling the Loop.
- Dustin Getz
process (y);
or process (z);
have been executed for the last value of x
? Or how to know what happened to the last x
processed? Something? Or SomethingElse as well??? Of course, in a real scenario, you might exactly want to have such a behavior (and maybe you would have method names that are self-evident), but it is hard to see what is really going on especially if those methods have side-effects. As Dustin already said, such a loop just screams to be refactored, at least it is a code smell. - 0xA3
break
altogether. Rather he says to be careful which is sensible. If you're just taking up C coding, you shoud be careful - it's like using a chainsaw. But maybe 30-odd years of cutting code has made me confident (or complacent). I think we may just have to leave it there, I'm getting too old to change my ways now :-) - paxdiablo
while (x != 0 &&
((y = doSomethingWith (x))? (process(y), true) : false) &&
((z = doSomethingWith (x))? (process(z), true) : false))
x--;
... and Evan is right about the difference between break and goto. People don't understand Dijkstra's point, or the context of the time he was writing in, when people were coding in assembler and in FORTRAN II with its 2-way branching if statement. - Jim Balter
I've been told by professors and peers at my university that using the break statement is bad practice
Come visit Tufts and our professors will tell you otherwise.
The arguments against break
boil down to one principle: break
requires non-local reasoning, and a language with break
requires a much more complicated semantic framework than a language without break
. (For the experts in the room, instead of using simple tools like predicate transformers or Hoare logic, you have to reach for something like continuations, or at the very least, a context semantics.)
The problem with this argument is that it puts simplicity of semantics ahead of programmers' real needs. There are lots of programs with natural loops that have more than one exit. Programming languages need to support these loops in a way that is more effective than introducing extra Boolean variables to govern the control flow.
For some expert testimony on the value of multiple exits from control-flow constructs, I recommend two papers:
Structured Programming With goto Statements
[1] by Donald E. Knuth. Don goes to great length to explain why certain kinds of gotos should be allowed in Pascal. Most of these gotos are equivalent to some form of break
, which hadn't quite been invented yet when Don wrote the paper.
Exceptional Syntax
[2] by Nick Benton and Andrew Kennedy. The topic may seem unrelated, but throwing an exception is a nonlocal exit, just like break
. (In Modula-3, break
was defined to be an exception.) It's a great paper showing how language designers need to be more imaginative in designing syntax to support multiple exits.
If you really want to annoy your professors, ask them if the return
statement is bad practice. If they say "no", you've got them: "But isn't return
a control operator, just like break
? And isn't it the case that introducing return
intro a structured program creates all the same semantic difficulties that introducing break
does?" Watch them squirm.
Is using the break statement bad practice?
No. The break
statement is a valuable tool in your toolbox, just like return
or exceptions. Like other tools, it can be misused, but there is nothing inherently bad about it, and in fact the break
statement is pretty easy to use in sane and sensible ways.
Your professors should learn some more powerful semantic methods that can tame the break
statement.
break
as the last statement in a loop, but that would be pointless because you could just incorporate it into a loop continuation condition. So practically, "using break" isn't analogous to "using return", it's more analogous to multiple returns. The anti-break people are almost certainly also anti-multiple-return, and anti-goto. And anti-denormed-data. - Steve Jessop
return
as an arbitrary control operator, and IMO languages should not have a return
statement, they should have named return parameters and drop off the end of the function, and/or return the last expression evaluated". At which point you give up and go write some assembler for a while to remind yourself that goto may be "wrong", but it's fun ;-) - Steve Jessop
return
only in the last statement of a block. But that's a lot more powerful than the last statement of a whole function. I pity the programmer who can't write if type(t) ~= "table" then return nil, "table expected" end
at the beginning of a long function. - Norman Ramsey
deal_with_something
, deal_with_something_that's_definitely_a_table
, deal_with_a_non_empty_table
, and so on, and have each call the next. It's a consistent world-view, just not a very pragmatic one... - Steve Jessop
This comes from the idea that there should be one way IN a method and one way OUT. Same with loops. I've had some instructors tell me that I shouldn't use more than one return or any break/continue because it creates "spaggetti code" and it's hard to follow the path. Instead, they say to set a flag and use an if statement rather than just break out. I completely disagree with this idea. I think in a lot of cases having more than one return or a break/continue statement is much more readable and easier to follow.
break
in a for
loop when a nicely-written while
loop would suffice. - Vivin Paliath
break
always does the same thing. While I agree break
should be avoided, I don't think it's a case of break versus flags. Excessive use of break
is usually indicative of a larger problem, like infinite loops, which are much much worse than break
. - Billy ONeal
I've been told by professors and peers at my university that using the break statement is bad practice
The first thing to realize is that many of those people have never actually been professional software engineers, and never had to work on a large code base written by many developers over many years. If you do this, you learn that simplicity, clarity, consistency and use of accepted idioms are more important in making code maintainable than dogma like avoiding break/continue/multiple return.
I personally have no problems reading and understanding code that uses break to get out of loops. The cases where I find a break unclear tend to be cases where the code needs to be refactored; e.g. methods with high cyclomatic complexity scores.
Having said that, your professors have the right motivation. That is, they are trying to instill in you the importance of writing clear code. I hope they are also teaching you about the importance of consistent indentation, consistent line breaking, consistent white space around operators, identifier case rules, meaningful identifiers, comments and so on ... all of which are important to making your code maintainable.
I don't see any harm in using break
- it's useful and simple.
The exception is when you have a lot of messy code inside your loop, it can be easy to miss a break tucked away in 4 levels of if
s, but in this case you should probably be thinking about refactoring anyway.
Edit: IMHO it's much more common to see break
in a while
than a for
(although seeing continue
in a for
is pretty common) but that doesn't mean it's bad to have one in a for
.
I think it's a completely pompous and ridiculous rule to enforce.
I often use break within a for loop. If i'm searching for something in an array and don't need to keep searching once I find it, I will break out of that loop.
I agree with @Konrad Rudolph above, that any and all features should be used as and when the developer sees fit.
In my eye, a for loop is more obvious at a glance than a while. I will use a for over a while any day unless a while is specifically needed. And I will break from that for if logic requires it.
My rule is to use any and all features of the language where it doesn't produce obscure or unreadable code.
So yes, I do on occasion use break, goto, continue
I often use break inside a for loop.
The advantage of a for loop is that the iterator variable is scoped within the expression. If a language feature results in less lines of code, or even less indented code then IMHO it is generally a good thing and should be used to improve readability.
e.g.
for (ListIt it = ...; it.Valid(); it++)
{
if (it.Curr() == ...)
{
.. process ...
break;
}
}
Rewriting this using a for loop would require several more lines, and leak the iterator out of the scope of the loop.
(Pedantic points: I only want to act on the first match, and the condition being evaluated isn't suitable for any Find(...) method the list has).
Break is useful for avoiding nesting. Also there are many times that it is useful to prematurely exit a loop. It also depends on the languages. In languages like C and Java a for loop basically is a while loop with an initialization and increment expression.
is it better to do the following (assume no short circuit evaluation)
list = iterator on something while list.hasItem() item = list.next() if item passes check if item passes other check do some stuff if item passes other check do some more stuff if item is not item indicating end of list do some more stuff end if end if end if end if end while or is it better just to say while list.hasItem() item = list.next() if check fails continue ..... if checkn fails continue do some stuff if end of list item checks break end while
For me it is better to keep the nesting down and break/continue offer good ways to do that. This is just like a function that returns multiple times. You didn't mention anything about continue, but in my opinion break and continue are of the same family. They help you to manually change loop control and are great at helping to save nesting.
Another common pattern (I actually see this in university classes all the time for reading files and breaking apart strings) is
currentValue = some function with arguments to get value while (currentValue != badValue) { do something with currentValue currentValue = some function with arguments to get value } is not as good as while (1) { currentValue = some function with arguments to get value if (currentValue == badValue) break do something with currentValue }
The problem is that you are calling the function with arguments to create currentValue twice. You have to remember to keep both calls in sync. If you change the arguments for one but not the other you introduce a bug. You mention you are getting a degree in software engineering, so I would think there would be emphasis on not repeating yourself and creating easier to maintain code.
Basically anyone who says any control structure is bad and completely bans it is being closed minded. Most structures have a use. The biggest example is GOTO. A lot of people abused it and jumped in the middle of other sub procedures, and basically jumped forwards/backwards all over the code and gave it a bad name. But GOTO has its uses. Using GOTO to exit a loop early was a good use, now you have break. Using GOTO to centralize exception handling was another good use. Now you have try/catch exception handling in many languages. In assembly there is only GOTO for the most part. And using that you can create a disaster. Or you can create our "structured" programming structures. In truth I generally don't use GOTO except in excel VBA because there is no equivalent to continue (that I know of) and error handling code in VB 6 utilizes goto. But I still would not absolutely dismiss the control structure and say never...
Unfortunately the reality is that if you don't want to fail, you will have to avoid using break. It is unfortunate that university doesn't have more open minded people in it. To keep the level of nesting down you can use a status variable.
status variable = true while condition and status variable = true do stuff if some test fails status variable = false if status variable = true do stuff if some test fails status variable = false .... end while
That way you don't end up with huge nesting.
goto
to understand that his context was languages in which goto was the only control structure. break
was an element of structured programming that replaced the unconstrained, indiscriminate goto
. - Jim Balter
Makes switch statements a whole lot easier.
switch(version) { case 1 : migrate_to_version_2; case 2 : migrate_to_version_3; ... case latest-1: migrate_to_version_latest; case latest: printf("done"); }
, if the actions to "migrate" are too dissimilar for a loop to be of any use. - Steve Jessop
if (version == 1) migrate_to_2; if (version == 2) migrate_to_3;
etc, assuming of course that "migrate_to_N" modifies version
. My switch statement is close to something that you might write in assembly, but that doesn't necessarily make it a good thing, just makes it easily comprehensible to that kind of programmer. - Steve Jessop
break
becoming an unusual termination much like break
in a loop. The difference is obvious (to me) in context. Lint can keep its nose out. - Steve Jessop
fallthru
keyword to denote (to the compiler and to humans, something comments can't do) that you intentionally don't want to break
in a switch
statement? "Explicit is better than implicit." - badp
case(foo) {stuff();}
(note lack of :
) equiv to case(foo): {stuff();} break;
. (Could complicate parsing though.) - David X
case
"block" what happens after it ends? - badp
if
and while
as well: break
vs continue
at the end of block (in the sense of "what do we do next"). Could also add a new keyword (select
?) for the whole switch block, but case(foo){}
avoids invalidating any existing code (except maybe some really insane macro abuse). - David X
I would argue your teachers' prohibition is just plain poor style. They are arguing that iterating through a structure is a fundamentally different operation than iterating through the same structure but maybe stopping early, and thus should be coded in a completely different way. That's nuts; all it's going to do is make your program harder to understand by using two different control structures to do essentially the same thing.
Furthermore, in general avoiding breaks will make your program more complicated and/or redundant. Consider code like this:
for (int i = 0; i < 10; i++)
{
// do something
if (check on i)
break;
// maybe do something else
}
To eliminate the break, you either need to add an additional control boolean to signal it is time to finish the loop, or redundantly check the break condition twice, once in the body of the loop and once in the loop's control statement. Both make the loop harder to understand and introduce more opportunities for bugs without buying you any additional functionality or expressiveness. (You also need to hoist the declaration of i
out of the loop's control structure, adding another scope around the entire mess.)
If the loop is so big you cannot easily follow the action of the break statement, then you'd be better off refactoring the loop than adding to its complexity by removing the break statement.
while(i < 10 && !(check on i))
? It gets rid of the if
and the break
. I can tell the exit condition from the loop definition. - Vivin Paliath
Edit: First of all, sorry if this answer seems somewhat long-winded. However, I'd like to demonstrate where my expressed opinion about the break
statement (bold text at the end of my answer) comes from.
One very good use case for the break
statement is the following. In loops, you can usually check a break condition in either of two places: Either at the loop's beginning, or at the loop's end:
while (!someBreakCondition)
{
...
}
do
{
...
} while (!someBreakCondition)
Now, what do you do when, for some reason, you cannot put your break condition in either of these places, because e.g. you first need to retrieve a value from somewhere before you can compare it to some criterion?
// assume the following to be e.g. some API function that you cannot change:
void getSomeValues(int& valueA, int& valueB, int& valueC)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// out parameters
{
...
}
while (true)
{
int a, b, c;
getSomeValues(a, b, c);
bool someBreakCondition = (b = ...);
... // <-- we do some stuff here
}
In the above example, you could not check for b
directly inside the while
header, because it must first be retrieved from somewhere, and the way to do that is via a void
function. No way to put that into the while
break condition.
But it may also be too late to wait until the end of the loop for checking the condition, because you might not want to execute all the code in the loop body:
do
{
int a, b, c;
getSomeValues(a, b, c);
bool someBreakCondition = (b = ...);
... // <-- we want to do some stuff here, but only if !breakCondition
} while (!someBreakCondition);
In this situation, there's two approaches how to avoid executing the ...
when someBreakCondition
is true:
1) without using break
... // as above
if (!someBreakCondition)
{
... // <-- do stuff here
}
} while (!someBreakCondition);
2) with break
... // as above
if (someBreakCondition) break;
... // <-- do stuff here
} while (true);
Your professor would obviously favour option 1) (no break
). I personally think that's not a nice option, because you have to check the break condition in two places. Option 2) (use break
) resolves that problem. Admittedly though, option 1) has the advantage that it becomes more easily recognisable through code indentation under what condition the ...
code will execute; with option 2), you have to go up in your source code to find out that program execution might never actually get to some place further down.
In conclusion, I think using or not using break
in this (quite frequent) scenario it really comes down to personal preference about code legibility.
break
might circumvent clean-up code, and that it might be problematic for this reason. In languages that have the try..finally
idiom, this is not so. You can specify clean-up code with a finally
block, and it will always get executed (provided there's no previous abnormal program termination). - stakx
break
. - Billy ONeal
break
approach. For this reason, some prefer writing for (;;)
instead of while (true)
because this "reeks" less of an infinite loop. But it's clearly a potential source for bugs. -- The other thing is, in programming languages that support a more functional programming style, one can often get rid of loops completely and replace them through constructs such as map projections or reductions. With the C language though, this would probably not work in a neat way. - stakx
break
is an extremely valuable optimization tool and is especially useful in a for
loop. For instance:
-- Lua code for finding prime numbers
function check_prime (x)
local max = x^0.5;
for v in pairs(p) do
if v > max then break end;
if x%v==0 then
return false
end
end
p[x] = true;
return x
end
In this case, it isn't practical to set up the for
loop to terminate at the right moment. It is possible to re-write it as a while
loop, but that would be awkward and doesn't really buy us anything in terms of speed or clarity. Note that the function would work perfectly well with out the break
, but it would also be much less efficient.
The huge advantage of using break
rather than refactoring into a while
loop is that the edge cases are moved to a less important location in the code. In other words, the main condition for breaking out of a loop should be the only condition to avoid confusion. Multiple conditions are hard for a human to parse. Even in a while
loop, I'd consider using break
in order to reduce the number of break conditions to just one.
I'm aware that this is not the most efficient prime checker, but it illustrates a case where break
really helps both performance and readability. I have non-toy code that would illustrate it, but require more background information to set it up.
If it's a guideline that's enforced by the corrector, you don't really have the choice.
But as guidelines go, this one seems to be excessive. I understand the rationale behind it. Other people argue (in the same vein) that functions must only have one single exit point. This may be helpful because it can reduce control flow complexity. However, it can also greatly increase it.
The same is true for break
in for
loops. Since all loop statements are basically idempotent, one kind can always be substituted for any other. But just because you can doesn't mean that this is good. The most important coding guideline should always be “use your brain!”
for
loop can be rewritten as a while
loop and vice versa. Even foreach
loops can always be substituted or used as substitute. CS 101. - Konrad Rudolph
In a way I can understand the professor's point of view in this matter, but only as a way to teach the students how to solve problems in a (some kind of) standard fashion. Learn to master these rules, then you are free to break against them as you wish, if that will make the code easier to understand, more effective, or whatever.
I tend to shy away from breaks. Most of the time, I've found that a flag is sufficient. Of course, this doesn't mean that breaks are always bad. They do have their uses. Your teachers and peers are right about it being a "get out of jail free" card. This is because breaks, like gotos, have a very high potential for abuse. It's often easier to break out of a loop than to figure out how to structure the loop correctly. In most cases, if you think about your algorithm and logic, you will find that you do not need a break.
Of course, if your loop ends up having a whole bunch of exit conditions, then you need to either rethink your algorithm or use breaks. I tend to really sit and think about my algorithms before deciding to use a break. When you write code and you get that voice in your head that says There has to be a better way than this!, you know it's time to rewrite your algorithm.
As far as loops are concerned, I use for
loops when want to iterate over a collection of items with a known bound and I know that there are no other exit conditions. I use a while
loop when I want to run a series of statements over and over again until something happens. I usually use while
loops if I am searching for something. In this case, I don't break out of the while
. Instead, I use a flag because I think the while
loop in its entirety reads more like English and I can tell what it's doing. For example:
int i = 0;
while(i < size && !found) {
found = (value == items[i]);
i++;
}
The way I read that in English in my head is While i is lesser than the total count of items and nothing is found. That, versus:
int i = 0;
for(int i = 0; i < count; i++) {
if(value == items[i]) {
break;
}
}
I actually find that a bit harder to read because I can't tell immediately from the first line what the loop is actually doing. When I see a for
loop, I think Ok, I'm running over a known list of items no matter what. But then I see an if
in that loop and then a break
inside the if
block. What this means is that your loop has two exit conditions, and a while
would be a better choice. That being said, don't do this either:
int i = 0;
while(i < count) {
if(value == items[i]) {
break;
}
i++;
}
That's not much better than the for
with the break. To sum it all up, I'd say use break
as a last resort, and only if you are sure that it actually will make your code easier to read.
break;
the reason has nothing to do with break
. The reason is a loop like that should be broken into it's own function, and you should use return
instead of break
there. - Billy ONeal
return
out of a loop isn't that good of an idea either. Unless it is a very short function. - Vivin Paliath
for
should be extracted into it's own 5 or 6 line function. If the body of the for
is too long for that, than the body of the for
should be extracted as well. Too bad there's no std::find
in C or Java :( - Billy ONeal
Those who claim that it is bad say that it's a "get out of jail free card" and that you can always avoid using it.
They may claim that, but if that's their actual argument then it's nonsense. I'm not in jail, and as a programmer I'm not in the business of avoiding things just because they can be avoided. I avoid things if they're harmful to some desired property of my program.
There's a lot of good discussion here, but I suggest that your professors are not idiots (even if they're wrong), and they probably have some reason in mind when they say not to use break
. It's probably better to ask them what this is, and pay attention to the answer. You've given your "guess", but I propose to you that they would state their case better than you state it. If you want to learn from them, ask them to explain the exact details, purpose, and benefits of their coding guideline. If you don't want to learn from them, quit school and get a job ;-)
Admittedly, I don't agree with their case as you've stated it, and neither do many others here. But there's no great challenge in knocking down your straw-man version of their argument.
Here is a good use-case in PHP:
foreach($beers as $beer) {
$me->drink($beer);
if ($me->passedOut) {
break; //stop drinking beers
}
}
for ($i=0; !$me->passedOut && $i<count($beers); ++$i) { $me->drink($beers[$i]); }
:) - cHao
I also learned at uni that any functions should have only a single point of exit, and the same of course for any loops. This is called structured programming, and I was taught that a program must be writable as a structogram because then it's a good design.
But every single program (and every single structogram) I saw in that time during lectures was ugly, hardly readable, complex and error-prone. The same applies to most loops I saw in those programs. Use it if your coding guidelines require it, but in the real world, it's not really bad style to use a break, multiple returns or even continue. Goto has seen much more religious wars than break.
Most of the uses of break
are about stopping when you've find an item that matches a criteria. If you're using C#, you can step back and write your code with a little more intent and a little less mechanism.
When loops like this:
foreach (var x in MySequence)
{
if (SomeCritera(x))
{
break;
}
}
start to look like:
from x in mySequence
where x => SomeCriteria(x)
select x
If you are iterating with while
because the thing you're working on isn't an IEnumerable<T>
, you can always make it one:
public static IEnumerable<T> EnumerateList<T>(this T t, Func<T, T> next)
{
while (t != null)
{
yield return t;
t = next(t);
}
}
The rule makes sense only in theory. In theory for loops are for when you know how many iterations there are, and while loops are for everything else. But in practice when you are accessing something for which sequentil integers are the natural key, a for loop is more useful. Then if you want to terminate the loop before the final iteration (because you've found what you are looking for) then a break is needed.
Obey your teacher's restriction while you are writing assignments for him. Then don't worry about it.
I understand the issue. In general you want to have the loop condition define the exit conditions and have loops only have a single exit point. If you need proof of correctness for your code these are invaluable. In general, you really should try to find a way to keep to these rules. If you can do it in an elegant way, then your code is probably better off. However, when your code starts to look like spaghetti and all the gymnastics of trying to maintain a single exit point get in the way of readability, then opt for the "wrong" way of doing it.
I have some sympathy for your instructor. Most likely he just wants to teach you good practices without confusing the issue with the conditions under which those practices can be safely ignored. I hope that the sorts of problems he's giving you easily fit into the paradigm he wants you to use and thus failing you for not using them makes sense. If not, then you get some experience dealing with jerks and that, too, is a valuable thing to learn.
You are still in school. Time to learn the most important mantra that colleges require of you:
Cooperate and Graduate.
It's good that your school has a guideline, as any company you work for (worth a plugged nickel) will also have a coding guideline for whatever language you will be coding in. Follow your guideline.
Another view: I've been teaching programming since 1986, when I was teaching assistant for the first time in a Pascal course, and I've taught C and C-like languages since, I think, 1991. And you would probably not believe some of the abuses of break
that I have seen. So I perfectly understand why the original poster's university outlaws it. It is also a good thing to teach students that just because you can do something in a language, that doesn't mean that you should. This comes as a surprise to many students. Also, that there is such a thing as coding standards, and that they may be helpful -- or not.
That aside, I agree with many other posters that even if break can make code worse, it can also make it better, and, like any other rule, the no-breaks rule can and (sometimes) should be broken, but only if you know what you're doing.
I don't think there is anything wrong with using breaks. I could see how using a break can be seen as skipping over code but it's not like a goto statement where you could end up anywhere. break has better logic, "just skip to the end of the current block".
slightly off topic...(couldn't resist)
http://xkcd.com/292/
@lodle.myopenid.com
In your answer the examples do not match. Your logic is as follows in the example in the equation and example A in your answer:
while X != 0 loop set y if y == 0 exit loop set z if z == 0 exit loop do a large amount of work if some_other_condition exit loop do even more work x = x -1 example b: while X != 0 loop set y if y == 0 set z elseif z == 0 do a large amount of work elseif (some_other_condition) do even more work x--
This is absolutely not the same. And this is exactly why you need to think about using break.
First of all in your second example you probably meant for the if var == 0 to be if var != 0, that is probably a typo.
So basically given your two examples the important thing to realize is that Example A is completely different from Example B. In trying to eliminate the break you completely botched up the code. I'm not trying to insult you or say you are stupid. I'm trying to overemphasize that the two examples don't match and the code is wrong. And below I give you the example of the equivalent code. To me the breaks are much easier to understand.
The equivalent of example A is the following
done = 0; while X != 0 && !done { set y if y != 0 { set z if z != 0 { do large amount of work if NOT (some_other_condition { do even more work x = x - 1 } else done = 1; } else done = 1; } else done = 1; }
As you can see what I wrote is completely different from what you wrote. I'm pretty sure mine is right but there may be a typo. This is the problem with eliminating breaks. A lot of people will do it quickly like you did and generate your "equivalent code" which is completely different. That's why frankly I'm surprised a software engineering class taught that. I would recommend that both you and your professor read "Code Complete" by Steve McConnell. See http://cc2e.com/ for various links. It's a tough read because it is so long. And even after reading it twice I still don't know everything in it. But it helps you to appreciate many software implementation issues.
break typically does make loops less readable. once you introduce breaks, you can no longer treat the loop as a black box.
while (condition)
{
asdf
if (something) break;
adsf
}
cannot be factored to:
while (condition) DoSomething();
From Code Complete:
A loop with many breaks may indicate unclear thinking about the structure of the loop or its role in the surrounding code. Excessive breaks raises often indicates that the loop could be more clearly expressed as a series of loops. [1]
Use of break eliminates the possibility of treating a loop as a black box1. Control a loop's exit condition with one statement to simplify your loops. 'break' forces the person reading your code to look inside to understand the loop's control, making the loop more difficult to understand. [1]
condition && !stop
and the break
becomes stop=true;continue;
(modulo adding braces as necessary). It doesn't make things clearer. - Donal Fellows
Out of curiosity, I took a little tour of the codebase I'm working on - about 100,000 lines of code - to see how I'm actually using this idiom.
To my surprise, every single usage was some version of this:
foreach (SomeClass x in someList)
{
if (SomeTest(x))
{
found = x;
break;
}
}
Today, I'd write that:
SomeClass found = someList.Where(x => SomeText(x)).FirstOrDefault();
which, through the miracle of LINQ deferred execution, is the same thing.
In Python, it would be:
try:
found = (x for x in someList if SomeTest(x)).next()
except StopIteration:
found = None
(It seems like there should be a way to do that without catching an exception, but I can't find a Python equivalent of FirstOrDefault.)
But if you're not using a language that supports this kind of mechanism, then of course it's OK to use the break
statement. How else are you going to find the first item in a collection that passes a test? Like this?
SomeClass x = null;
for (i = 0; i < SomeList.Length && x == null; i++)
{
if (SomeTest(SomeList[i]))
{
x = SomeList[i];
}
}
I think break
is just a wee bit less crazy.
In general, anything that makes execution jump around and isn't a function call has the potential to make your code more confusing and harder to maintain. This principle first gained widespread acceptance with the publication of Dijkstra's Go To Statement Considered Harmful [1] article in 1968.
break is a more controversial case, since there are many common use cases and is often pretty clear what it does. However, if you're reading through a three- or four-deep nested loop and you stumble upon a break (or a continue), it can be almost as bad. Still, I use it sometimes, as do many others, and it's a bit of a personal issue. See also this previous StackOverflow question: http://stackoverflow.com/questions/57600/continue-considered-harmful
[1] http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.htmlbreak
. Garbage code is garbage code. It matters not what kind of statements are used to produce that code. - Billy ONeal
I believe your professor's are just trying (wisely) to instil go coding practices in you. Break's, goto's, exit(), etc can often be the cause behind extraneous bugs throughout code from people new to programming who don't really have a true understanding of what's going on.
It's good practice just for readability to avoid intruding possible extra entrances and exit's in a loop/code path. So the person who reads your code won't be surprised when they didn't see the break statement and the code doesn't take the path they thought it would.
for
, while
, and do..while
belong in that category. Also, I don't see what exit
has to do with anything given that it results in immediate termination of the program. - Billy ONeal
In the real world, few people care about style. However, break
from loop is an okay thing by strictest coding guidelines, such as that of Google, Linux kernel and CppCMS.
The idea of discouraging break comes from a famous book, Structured Programming by Dijkstra http://en.wikipedia.org/wiki/Structured_programming that was the first one to discourage goto. It suggested an alternative to goto, and suggested principles which might have misled your professors.
Since then, a lot changed. Nobody seriously believes in one point of return, but, the goto - a popular tool at the time of the book - was defeated.
Ok, thanks for the answers its good to know what other people think. I use it in special occasions as pointed out already but most times using break can be avoided.
The example given was:
while (x != 0) {
// Do some calculations to set y.
if (y == 0) break;
// Do some calculations to set z.
if (z == 0) break;
// Do a large amount of work.
if (some_other_condition) break;
// Do even more work.
x--;
}
which can be refactored using else if statements
while (x != 0) {
// Do some calculations to set y.
if (y == 0)
// Do some calculations to set z.
else if (z == 0)
// Do a large amount of work.
else if (some_other_condition)
// Do even more work.
x--;
}
if (y == 0) { ...nothing... } else { // Do some calculations to set z, etc. }
' and the code nests horribly fast. - Jonathan Leffler
See the popular response above. The rules you are taught are garbage, hardly worth the time to speak them much less write them down and talk about them.
What is important here is that to complete your degree you have to follow a set of programming guidelines. follow those guidelines. In the real world it is quite possible that you will find jobs with programming rules or guidelines (unfortunately culled from the garbage taught at school).
1) code within the guidelines for that job/task
2) rise to a level of power within that job/task that you can change the programming guideline to something that is not garbage.
For the most part those programming guidelines are there to aid the professor in grading your work. Also to teach you a certain subject. If you use calculus to solve an algebra problem in an algebra class I would hope you fail the class. You are there to learn algebra.
Once you have the time to compile your programs to assembler and inspect what is REALLY going on, you will see the light. The break, continue, and goto are all directly mapped to jumps/branches, almost one to one, these are the cleanest coding structures you can find. While, for, etc these are abstract and bulky. Count how many "gotos" (branches) it takes in the assembler to avoid multiple return paths in a function. Count how many branches it takes if you use breaks or returns or continues throughout your code. Notice how much cleaner the code is with breaks and returns and continues? gotos are sketchy, although the single best operation in a programming language, because of the focus it has had from universities, not all compilers handle it properly. You may have to avoid it only because the programmer writing the compiler really doesnt understand them. breaks and continues are very good though, use them as much as possible (once you graduate).
Some languages enforce break usage. For example, in Python (a very readable language, isn't it?), it is a valid idiom the following:
while True: .. do something .. if loop_condition: break
This code is equivalent to do-while loop in other languages (which is hard to express in Python because of the block syntax constraints). It has also an obvious advantage that you can just move loop condition anywhere else in the loop. So I would argue this is really more readable than having two (or three) different syntaxes depending on whether your loop starts, ends with condition or has it in the middle.
If you keep this rule, then the point of exit is only where the loop condition is tested. Not having to sift through code looking for breaks makes the code a lot more readable. If everybody kept this rule then the code written is much easier to maintain and the world is a better place.
Rules are meant to be broken, so for short loops I don't see what's wrong with break. (I like to keep my loops 5-10 lines short)
Continue and Break jump to specific places - they don't create spaghetti code. On the other hand, continue and break can obscure the fact that not all of the code in the loop will necessarily be executed. In many cases, a better solution is to write other code to avoid the need to use them, such as using the Null Object Pattern when iterating over a collection.
Focusing specifically on the loop - circumstances should dictate.
The alternative for continue is often to enclose the contents of the whole loop in a conditional - I would argue in favor of using continue to avoid the extra nesting.
Sometimes a while loop can be structured to eliminate the need for a break, such as by using a flag variable - that should be preferred, unless doing so makes the intent of the code less apparent. Specifically, a break would be preferable to setting a flag and then using continue to avoid processing the rest of the loop.
goto
is bad" statement is somewhat overused IMO. If you look at old code, e.g. old BASIC code with line numbers and tons of GOTO
because functions and subprocedures didn't yet exist, you can see where this statement about goto
's badness comes from. But that doesn't mean that today, every single use of goto
is inherently bad. -- I agree though that it's rarely needed if the program code is structured well. - stakx
break
and continue
should not be put under the same hammer as goto
. - Billy ONeal
Breaks have their place. I keep getting back to it when linearly searching through a list. Snippets like this are not uncommon
SomeClass found = null;
for(SomeClass c : listOfSomeClass) {
if (sometest(c)) {
found = c;
break;
}
}
if (found != null) {
System.out.println("there is something here");
}
The boolean flag alternative is way less expressive and confusing and can not be used easily with the forEach construct.
std::for_each
is almost useless in practice and std::find_if
rarely gives much improvement, simply because in C++03 any kind of "closure" substitute is non-local and verbose and generally more trouble than it's worth for a simple expression. - Steve Jessop
Okay -- I know I'm posting an answer to a question that already has a large number of responses, but I think this is important.
Is using the break statement bad practice?
Yes and no. There is absolutely no language feature that should never be used.
If there was a feature that should never be used, that feature would not be in
the language. Language designers aren't stupid, and the fact that break
and
continue
have existed in some form in pretty much every C derived programming
language shows that language designers still believe it's a useful feature.
Consider the goto
statement, which has been removed in languages like Java
,
which provide language features that (almost) completely replace the need for
goto
.
Like all language features, break
has very good uses, and very bad ones. There
is one very specific use for break
: You are iterating over a loop and have
encoutered some condition that forces you to stop looping. That's it. That's the
only reason you should ever see it.
In that sense, I disagree with your Professor. There are legitimate uses of
break
. It'd not be there otherwise.
However, I will agree with your Professor that relying on break is usually indicative of bad design. Consider the following (admittedly contrived) implementation of strlen:
size_t strlen(const char * str)
{
size_t idx = 0;
for(;;idx++) {
if (!*str)
break;
}
return idx;
}
What is the problem here? Is it break
? I don't think so. The problem is the
infinite loop. I have to read the body of the loop in order to understand its
termination condition. That's bad. it should instead be something like:
size_t strlen(const char * str)
{
const char * begin = str;
while(*str) str++;
return str - begin;
}
Break
used as a replacement for the loop's termination condition is a sign
that you have not thought about the termination condition well enough.
Use break when it's appropriate. But consider excessive use of it a code
smell. Consider extracting your loop containing the break and using a return
statement instead. While the return has some of the same problems as the break,
forcing yourself to use return
instead of break
here encourages you to keep
these kinds of loops short and localized -- as they should be anyway.
Note that all of this applies to flag variables as well -- break statements and flag variables are essentially the same with regards to the above advice. They are indicative that something else is wrong with your design if they are used excessively.
Never say never, and if your intent is clear and you can make a reasoned argument for its use and why it is better than not using it then why not?
However it is a good idea to keep such advice in mind, because there are good reasons for it, even if there are also (a few) situations when it could be disregarded; and even if your teachers cannot tell you the reasons, and are just repeating received wisdom without truly understanding why it may be important. Spaghetti code is not the most compelling argument.
Consider a large and complex loop body, it is useful to know that the loop will always exit from one point (the bottom); that way you only have one place (the loop condition) to consider when debugging the loop. Say you place a breakpoint after the loop, and then wish to determine why it exited, observing the loop condition expression and its variables is all you need to know. If the loop contains breaks, you may have much more to consider post-mortem to determine where it came from. Even worse in this case would be the use of return
or goto
to exit the loop since not only will you not know directly where it came from but also where it will go to; so you will need multiple breakpoints to trap loop exit.
Even for short, simple loops, the use of a break may be ill-advised since under maintenance, the loop may not remain small and simple. This may be the case on a large, long-term project with multiple developers for example. It is seldom an issue in the kind of academic exercises you might be involved with at college, but it may be wise to get into good habits, even when it does not matter.
The point is that the person debugging or maintaining the code may not be the person who wrote it in the first instance; or you may be debugging it long after you wrote it. Making it easy to debug and maintain in these situations is useful, and what looked reasonable to you when you were intimately familiar with the code, may look like spaghetti in the long term, even for relatively simple constructs; "someone else's code" is almost always incomprehensible on first sight, and maintaining simplicity and adhering to a few rules and standards can help immensely in these situations.
There might be other things to consider that don't directly relate to the programming task. At some point the student might have to formally prove the correctness of their algorithm (i. e. Hoare logic [1]). And you may well run out of your tools to do this task.
I still don't think that this formal verification is any useful in real world, but someone probably think it's worthwhile to teach for other reasons.
[1] http://en.wikipedia.org/wiki/Hoare_logic@Pax Diablo
i agree with your premise ("do what increases readability"). i disagree with your conclusion ("breaks increase readability"). 'continue' increases readability by increasing statement locality and emphasizing linear execution. 'break' causes execution flow jumps and shatters loop encapsulation.
the loop you used as an example just begs to be refactored. At first glance, I would reasonably presume that when the loop is finished, x==0 and all indexes 0 < i < Xinitial have been processed. To realize that this is not the case, I would have to parse the entire loop.
@Jay Bazuzi, you said this was good:
foreach (var x in MySequence)
if (SomeCritera(x))
break;
this is equivalent and more elegant:
var x;
while (valid(x) and not SomeCriteria(x))
next x;
@Quarrelsome, you commented:
foreach(Thing stuff in things)
{
if(stuff.Id == thingImLookingFor) { // do stuff break; }
}
this is more readable:
var thing;
while (valid(x) and not IsThingImLookingFor(x)) next x;
do stuff
@Rob Walker, you said this was the best:
for (ListIt it = ...; it.Valid(); it++)
{
if (it.Curr() == ...)
{
.. process ...
break;
}
}
how about this (exact same paradigm as the above two refactorings)
ListIt it = ...;
while (it.Valid() && it.Curr() != ...) it++;
Process(it);
@Cervo - one of your examples is weak, but the other example seems ok - the one necessary use of 'break' so far in this thread.
while list.hasItem()
item = list.next()
if check fails continue
.....
if checkn fails continue
do some stuff
if end of list item checks break
end while
i don't understand exactly what you mean by if end of list item checks break
, but it seems that it isn't even necessary - the loop would naturally finish because there are no more items. if you don't mean to loop until the list is empty, why not use if NOT end of list item checks
as your loop condition? no break, preserves loop encapsulation (the entire loop body could be factored into another function, with 'continue' mapped to early returns.
@Cervo - this is a good example.
currentValue = some function with arguments to get value
while (currentValue != badValue) {
do something with currentValue
currentValue = some function with arguments to get value
}
versus the better:
while (1) {
currentValue = some function with arguments to get value
if (currentValue == badValue)
break
do something with currentValue
}
I could only come up with this response, which is specific to a subset of languages, i think it's worse than your while(1).
while ( (input = getch()) != ‘\0’)
Process(input);
foreach(Thing stuff in things)
{
if(stuff.Id == thingImLookingFor) { // do stuff break; }
}
-- good code, stuff
is local in scope to the loop. var thing;
while (valid(x) and not IsThingImLookingFor(x)) next x;
do stuff
-- awful, incompetent code that gives one variable three different names and makes it global to the loop. - Jim Balter
Maybe you are mixing break
with return
? Especially since you are talking about cleanup code. There is no cleanup code in loops that break could skip.
There is no reason to avoid break, and I have not heard any professor or textbook to recommend avoiding breaks. However, it is often recommended that multiple return points in a function should be avoided.
Break
is an important construct in C. For example switch...case
construct requires break on each branch. (Higher level languages implicitly add break at end of each case branch so you do not need to write them.)
In loops, break is needed when you need to exit from middle of the loop. (Of course you should not use break to exit at the beginning or end of loop, since there are other constructs for that purpose.)
If you use tricks (e.g. flags and if's) to avoid using break, the resulting code is more unreadable. Using tricks instead of natural method is always bad.
Using return
is more debatable. It is often said that each function should only have a single exit point. Return at the middle of function makes the code more difficult to follow, and may cause some cleanup code to be skipped. A bad mistake is to use return inside a loop, it should always be avoided.
However, there are some cases when return near the begin of function is a good idea. For example when you can instantly check the parameters and notice that there is no reason to run the actual function. In such situation, return near the begin of function can be both more efficient and more readable than multiple nested if's.
In the real-world, I look at every break
statement critically as a potential bug. Not an actual bug, but a potential bug. I challenge the programmers I work with on every break
statement to justify its use. Is it more clear? Does it have the expected results?
Every statement (especially every composite statement) has a post-condition. If you can't articulate this post-condition, you can't really say much about the program.
Example 1 -- easy to articulate.
while not X:
blah blah blah
assert X
Pretty easy to check that this loop does that you expected.
Example 2 -- harder to articulate.
while not X:
blah
if something I forgot:
break
blah blah
if something else that depends on the previous things:
break
blah
assert -- what --?
# What's true at this point? X? Something? Something else?
# What was done? blah? blahblah?
Not so easy to say what the post-condition is at the end of that loop. Hard to know if the next statements will do anything useful.
Sometimes (not always, just sometimes) break
can be bad. Other times, you can make the case that you have loop which is simpler with a break
. If so, I challenge programmers to provide a simple, two-part proof: (1) show the alternative and (2) provide some bit of reasoning that shows the post-conditions are precisely the same under all circumstances.
Some languages have features that are ill-advised. It's a long-standing issue with language design. C, for example, has a bunch of constructs that are syntactically correct, but meaningless. These are things that basically can't be used, even though they're legal.
break
is on the hairy edge. Maybe good sometimes. Maybe a mistake other times. For educational purposes—it makes sense to forbid it. In the real world, I challenge it as a potential quality issue.
for
loop makes sense and is the best solution for the problem. But then, these can easily be justified and would thus pass your challenge. - Konrad Rudolph
for
loop ... is the best solution" would you post one of these cases? - Dustin Getz
(X or (Something and blah) or (Something else and blah and not Something))
. Breaks make it hard to write a post-condition (or assert statement) that summarizes the loop as a whole. - S.Lott
break
statements can add complexity if they're used poorly. It's easy to prove that they add complexity. The consequences of break
are unpleasant to contemplate. Hence the downvotes. - S.Lott
break
as a potential evil or a god-send. - tvanfosson
break
conditions in which one cannot have any level of confidence. Break is a potential bug. Most folks who say a "clear or simple break is a good thing" are right. If the break is obvious, it's good. That means you looked at it as a potential bug and reasoned that it was correct. If it isn't obvious, it's a potential bug. - S.Lott
break
either because it caused your test to pass (and maintained all other passing tests) or because you refactored to something better while maintaining your passing tests. Obviously, this is dependent on writing sufficient tests to cover your required behavior. - tvanfosson
break
sparingly". break
definitely has its uses. I think I have such an aversion to it because I've mostly seen break
abused rather than used. So when I think I need to use a break
, I think really hard to see if I need to use it. I don't think the OP supports a dogmatic statement of "don't use break
". I think he's trying to say "use break
carefully". - Vivin Paliath
break
as a potential bug appears to scare many people. Don't know why a potential bug is so terrifying. But there it is: 47 downvotes because I said there was a "potential" bug in their software. - S.Lott
break
rather than use them. It's often an excuse for poor coding rather than actually figuring out if it is ok to use a break
in a specific situation. It's the same reason why goto
is considered harmful. Does it mean it doesn't have uses? Not at all. But part of being a good programmer is known when to use the right tools in your toolset. - Vivin Paliath
switch
. I can't see that in the posting. The use of break
in switch
is an irrelevant quirk of C that I never mentioned. - S.Lott
break
s) are the mark of a careless programmer while assertions are the mark of a careful programmer. - Artelius
break
can sometimes make it murky rather than clear. "de-couple logical conditionals from the loop definition" is the problem -- the post-condition really exists (the post-condition is the meaning or semantics of the loop). But the expression is scattered around in various places inside the loop syntax. - S.Lott
break
is a potential bug because it obscures the post-condition. That's all. You're free to use all the break statements you want. I'm free to view them as potential bugs until proven otherwise. - S.Lott
foreach(Thing stuff in things) { if(stuff.Id == thingImLookingFor) { // do stuff break; } }
-- here, stuff
is scoped locally to the loop. This is good code. var thing; while (not IsThingImLookingFor(x)) next x; do stuff
-- here, stuff
got turned into thing
, x
, and stuff
. Whichever one we settle on for the loop control variable, it has to be global in scope relative to the loop. This is awful code. - Jim Balter