Why is continue inside a loop a bad idea? - loops

Douglas Crockfod says that it is usually better to refactor the continue inside the loop.
Why is continue considered bad within a loop?

The use of continue would mean that you have insufficient conditions written in your while.
You should instead use if inside your while loop, or add the condition into the while loop.

Using goto, break, continue, throw, or return inside the loop body can all have the un-desired effect as well. Here's another example where the loop control and the loop body are tightly interwoven. Does it write 1, 2, and 3 as before? Are you sure?
int value = 1;
for (;;++value)
{
cout << value << endl;
if (value != 4)
continue;
else
break;
}
You might be thinking that advising you not to use return statements inside loop bodies is over zealous. Do I really mean that? Yes I do. Functions that return something should do so via a single return statement at the very end of the function. Here are some practical reasons why:
Link
Disclaimer: Not my material, I have referenced back to the source

The effect of continue is somehow comparable to a goto to the begin of the loop. It therefore makes your code more difficult to understand - like gotos.

Related

Mixed 'switch' and 'while' in C

I've recently read this page about strange C snippet codes. Most of them was understandable. But I can't understand this one:
switch(c & 3) while((c -= 4) >= 0){
foo(); case 3:
foo(); case 2:
foo(); case 1:
foo(); case 0:
}
Can anyone please help me out what logic is behind of this code? And how does it work?
The duff's device comment should explain the background well enough, so I ll try to explain this very case:
The switch checks the last 2 bits of c, and jumps to the respective case-statement inside the while loop. The code below the case statement is also executed. Control then reaches the end of the while loop, so it jumps to the beginning again to check if the condition is still true. If it is, all the statements inside the loop are executed, and the loop is repeated until the condition is false. The initial switch usually ensures that c will be a multiple of 4 when the while loop runs for the first time.
Edit: duff's device on Wikipedia. Adding link to make more obvious what I meant with "the duff's device comment". Please consider upvoting interjay's comment should you upvote this answer.

Why is for(;;) used?

I have seen code (in C) which contains something similar to:
for(;;){
}
How would this work, and why is it used in any instance?
It is the idiomatic way to have an infinite loop.
In the C Programming Language book, by Kernighan & Ritchie book it was introduced in section 3.5:
for (;;) {
...
}
is an infinite loop, presumably to be broken by other means, such as a break or return.
is an infinite loop something like
while(true)
{}
This is equivalent to an infinite loop, as many other have explained. However, few of them explained why this executes as an infinite loop.
A for loop can be broken down into three parts:
for(initialization(s); condition(s); looping command(s))
None of these fields are actually required. Without a condition provided, there's nothing to stop the command from running. This is because the for loop looks for a false statement. Without conditions provided, nothing is false, therefore the loop runs indefinitely.
Therefore to cause a for loop to be infinite, all you need is to not provide a condition. This means that this is also a valid infinite loop:
for(int i = 0;; i++)
printf("Iteration: %i\n", i);
For readability, and to make sure that the second semi-colon isn't a typo, some programmers might put a space between them, or put true as the condition.
Honestly, I prefer while(true), as this is a clear infinite loop. Using while(1) works as well, but '1' is an integer, not a boolean. While it is equivalent to true, it does not always mean true.
Between these three types of infinite loops, for(;;) has the fewest characters (7). while(1) comes second at 8 characters, and while(true) at 11.
I suppose that certain programmers prefer a low byte count over a readable program, but I wouldn't recommend using for(;;). While equivalent, I believe that using while(true) is better practice.
A for loop needs three expressions, which are separated by semicolons, and are completely optional:
An initialization (e.g. int i=0)
A condition (e.g. i < 10)
An afterthought (e.g. i++)
In this case, the three expressions are empty, and thus there's no condition that will make the loop stop, thus creating an infinite loop, unless a flow control instruction like break (which will exit the loop) or return is used.
Its an infinite loop. Its equivalent to:
while (true) {
}
The C# compiler directly translates for (; ;) into the exact same construct as while (true).
Infinite loop
same as
while(true){}
the code for(;;){} or while(true){} or while(1){} all represent infinite loops.
An infinite loop is something to be expected in a software system that is expected to run and "unlimited" amount of time. Every OS has at least one - it's how a background task or idle task is implemented.
Real Time systems use infinite loops as well because the system has to handle events which are asynchronous;
Basically anything that runs software uses infinite loops in one way or another.
I don't know why no one answered why people do this instead of while(true): It's because while(true) will often generate a compilation warning that the condition of the loop is constant.
This kind of infinite loop can be used in microcontroleurs. In your main function, you initialize the basic functions of your microcontroleur, then put a while (1).
void MAIN(void)
{
Init_Device();
while(1);
}
The microcontroleur will then do nothing but wait for interrupts of internal or external devices (like a timer overflow, or UART data ready to be read), and react to these interrupts.

What does the following for loop syntax mean in C?

I came across this syntax, not sure what it means.
for( ; ; )
{
//do stuff like read from a handle etc.
}
I am still on my learning curve in C so vote down if you want if it's a lame question.
It's an infinite loop. Same as while(1)
Really the only important thing to look at is that for loops look like for (initialize vars; continue condition; counters) . Since there's no continue condition it just keeps going (unless there's a break or return statement in there somewhere).

for loop, leaving start value blank?

I'm writing a for loop for a variable start whose value has already been calculated elsewhere in the program.
Doing for(start; start<end; start++) gives a warning, and
for(start=start; start<end; start++) seems like an unnecessary assignment.
The other option I can think of would be the following--is this okay, or would you classify it as poor coding style?
for(; start<end; start++){
//do stuff
}
That's not poor coding style IMHO, but perhaps you want to use a while instead?
while (start < end)
{
//do stuff
++start;
}
It's just a matter of taste, really.
Leaving any part of the for loop out is OK. Leaving all parts out is OK too - in fact, it's the idiomatic way of expressing an infinite loop as shown in the K&R book.
You should carefully consider your other options though; it is possible that a while or a do / while loop presents a more readable alternative.
Yes, you can do this. However, I would suggest assigning start into a variable like i or something, because now you are changing the value of start as you iterate.
You can write your code as the following for the desired result:
while(start++<end){
// your code
}
You get to take advantage of the fact that adding "++" after the variable name increments it after it is checked in the condition statement.

Do Perl loop labels count as a GOTO?

Generally, it is good practice to avoid GOTOs. Keeping that in mind I've been having a debate with a coworker over this topic.
Consider the following code:
Line:
while( <> ) {
next Line if (insert logic);
}
Does using a loop label count as a goto?
Here is what perlsyn in perldoc has to say:
Here's how a C programmer might code up a particular algorithm in Perl:
for (my $i = 0; $i < #ary1; $i++) {
for (my $j = 0; $j < #ary2; $j++) {
if ($ary1[$i] > $ary2[$j]) {
last; # can't go to outer :-(
}
$ary1[$i] += $ary2[$j];
}
# this is where that last takes me
}
Whereas here's how a Perl programmer more comfortable with the idiom might do it:
OUTER: for my $wid (#ary1) {
INNER: for my $jet (#ary2) {
next OUTER if $wid > $jet;
$wid += $jet;
}
}
My take on this is no because you are explicitly telling a loop to short circuit and advance however my coworker disagrees, says that it is just a fancy GOTO and should be avoided. I'm looking for either a compelling argument or documentation that explains why this is or is not a GOTO. I'll also accept an explanation for why this is or is not considered good practice in perl.
Dijkstras intent was never that anything resembling goto is to be considered harmful. It was that the structure of code where gotos are used as the main construct for almost any kind of program flow change will result in what we today call spaghetti code.
You should read the original article and keep in mind that it was written in 1968 when labeled jumps was the main flow control constructs in just about all programming languages.
https://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF
The danger of GOTO labels is that they create spaghetti code and make the logic unreadable. Neither of those will happen in this case. There is a lot of validity in using GOTO statements, much of the defense coming from Donald Knuth [article].
Delving into the differences between your C and Perl example... If you consider what is happening at the assembly level with your C programs, it all compiles down to GOTOs anyway. And if you've done any MIPS or other assembly programming, then you've seen that most of those languages don't have any looping constructs, only conditional and unconditional branches.
In the end it comes down to readability and understandability. Both of which are helped an enormous amount by being consistent. If your company has a style guide, follow that, otherwise following the perl style guide sounds like a good idea to me. That way when other perl developers join your team in the future, they'll be able to hit the ground running and be comfortable with your code base.
Who cares whether it counts as goto as long as it makes the code easier to understand? Using goto can often be MORE readable than having a bunch of extra tests in if() and loop conditions.
IMO, your code comparison is unfair. The goal is readable code.
To be fair, you should compare an idiomatic Perl nested loop with labels against one without them. The C style for and blocked if statement add noise that make it impossible to compare the approaches.
Labels:
OUTER: for my $wid (#ary1) {
INNER: for my $jet (#ary2) {
next OUTER if $wid > $jet;
$wid += $jet;
}
}
Without labels:
for my $wid (#ary1) {
for my $jet (#ary2) {
last if $wid > $jet;
$wid += $jet;
}
}
I prefer the labeled version because it is explicit about the effect of the condition $wid > $jet. Without labels you need to remember that last operates on the inner loop and that when the inner loop is done, we move to the next item in the outer loop. This isn't exactly rocket-science, but it is real, demonstrable, cognitive overhead. Used correctly, labels make the code more readable.
Update:
stocherilac asked what happens if you have code after the nested loop. It depends on whether you want to skip it based on the inner conditional or always execute it.
If you want to skip the code in the outer loop, the labeled code works as desired.
If you want to be sure it is executed every time, you can use a continue block.
OUTER: for my $wid (#ary1) {
INNER: for my $jet (#ary2) {
next OUTER if $wid > $jet;
$wid += $jet;
}
}
continue {
# This code will execute when next OUTER is called.
}
I think the distinction is somewhat fuzzy, but here's what the goto perldoc states about the (frowned upon) goto statement:
The goto-LABEL form finds the statement labeled with LABEL and resumes execution there.
...
The author of Perl has never felt the need to use this form of goto (in Perl, that is; C is another matter). (The difference is that C does not offer named loops combined with loop control. Perl does, and this replaces most structured uses of goto in other languages.)
The perlsyn perldoc, however, says this:
The while statement executes the block as long as the expression is true. The until statement executes the block as long as the expression is false. The LABEL is optional, and if present, consists of an identifier followed by a colon. The LABEL identifies the loop for the loop control statements next, last, and redo. If the LABEL is omitted, the loop control statement refers to the innermost enclosing loop. This may include dynamically looking back your call-stack at run time to find the LABEL. Such desperate behavior triggers a warning if you use the use warnings pragma or the -w flag.
The desperate behaviour bit doesn't look too good to me, but I may be misinterpreting its meaning.
The Learning Perl book (5th edition, page 162) has this to say:
When you need to work with a loop block that's not the innermost one, use a label.
...
Notice that the label names the entire block; it's not marking a target point in the code. [This isn't goto after all.]
Does that help clear things up? Probably not... :-)
Labeled loop jumps in Perl are GOTOs as much as C's break and continue are.
I would answer it like this, and I'm not sure if this is sufficiently different from what others have said:
Because you can only only move inside of the current scope, or to a parent scope, they're much less dangerous than what is typically implied by goto, observe:
if (1) {
goto BAR;
die 'bar'
}
BAR:
This should work obviously, but this won't (can't move in this direction).
if (0) {
BAR:
die 'bar'
}
goto BAR;
Many use cases of labels differ from goto in that they're just more explicit variants of core flow control. To make a statement that they're categorically worse would be to imply that:
LOOP: while (1) {
next LOOP if /foo;
}
is somehow worse than
while (1) {
next if /foo/;
}
which is simply illogical if you exclude style. But, speaking of style, the latter variant is much easier to read - and it does stop you from having to look up for the properly named label. The reader knows more with next (that you're restarting the loop in the current scope), and that is better.
Let's look at another example
while (1) {
while (1) {
last;
}
stuff;
}
-vs-
FOO: while (1) {
BAR: while (1) {
next FOO;
}
stuff;
}
In the latter example here next FOO, skips stuff -- you might desire this, but it is bad idea. It implies that the programmer has read a parent scope to completion which is an assumption probably better avoided. In summary, label isn't as bad as goto and sometimes they can simplify code; but, in most cases they should be avoided. I usually rewrite loops without labels when I encounter them on CPAN.
gotos are bad because they create hard to understand code--particularly, what is often called "Spaghetti Code". What's hard to understand about next Line...??
You can call it a loop "name", and it really is something to help emphasize loop boundaries. You're not jumping into an arbitrary point in relation to the loop; you're going back to the top of a loop.
Sadly enough, if it is a group or house standard, there might be nothing to convince the group that it's not a goto. I had a manager who absolutely insisted that a ternary operator made things hard to read, and preferred I use if-blocks for everything. I had a pretty good argument anything can be done in the clauses of an if-else, but that a ternary made it explicit that you were looking for a particular value. No sale.
This kind of jump is a disciplined used of a goto-like statement. So it's certainly less harmful than undisciplined use of goto. (As kasperjj wrote, "Dijkstras intent was never that anything resembling goto is to be considered harmful.")
IMO, this Perl kind of jump is even better design than C's "break" and "continue", because it makes clear what loop we break or continue, and it makes it more solid in the face of code changes. (Besides, it also allows to break or continue an outer loop.)
There are pundits who don't like break/continue and the like, but at some point there is a tradeoff to make between rules of thumb and readability, and a well-chosen break/continue or even goto may become more readable than "politically correct" code.
break/last and continue/next ARE gotos. I don't understand why anyone would go to such lengths to avoid a keyword yet use a different keyword that does the same thing...
4.4.4. Loop Control
We mentioned that you can put a LABEL on a loop to give it a name. The loop's LABEL identifies the loop for the loop-control operators next, last, and redo. The LABEL names the loop as a whole, not just the top of the loop. Hence, a loop-control operator referring to the loop doesn't actually "go to" the loop label itself. As far as the computer is concerned, the label could just as easily have been placed at the end of the loop. But people like things labeled at the top, for some reason.
Programming Perl

Resources