开发者

Are "while(true)" loops so bad? [closed]

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance. Closed 10 years ago.

I've been programming in Java for several years now, but I just recently returned to school to开发者_Go百科 get a formal degree. I was quite surprised to learn that, on my last assignment, I lost points for using a loop like the one below.

do{
     //get some input.
     //if the input meets my conditions, break;
     //Otherwise ask again.
} while(true)

Now for my test I'm just scanning for some console input, but I was told that this kind of loop is discouraged because using break is akin to goto, we just don't do it.

I understand fully the pitfalls of goto and its Java cousin break:label, and I have the good sense not to use them. I also realize that a more complete program would provide some other means of escape, say for instance to just end the program, but that wasn't a reason my professor cited, so...

What's wrong with do-while(true)?


I wouldn't say it's bad - but equally I would normally at least look for an alternative.

In situations where it's the first thing I write, I almost always at least try to refactor it into something clearer. Sometimes it can't be helped (or the alternative is to have a bool variable which does nothing meaningful except indicate the end of the loop, less clearly than a break statement) but it's worth at least trying.

As an example of where it's clearer to use break than a flag, consider:

while (true)
{
    doStuffNeededAtStartOfLoop();
    int input = getSomeInput();
    if (testCondition(input))
    {
        break;
    }
    actOnInput(input);
}

Now let's force it to use a flag:

boolean running = true;
while (running)
{
    doStuffNeededAtStartOfLoop();
    int input = getSomeInput();
    if (testCondition(input))
    {
        running = false;
    }
    else
    {
        actOnInput(input);
    }
}

I view the latter as more complicated to read: it's got an extra else block, the actOnInput is more indented, and if you're trying to work out what happens when testCondition returns true, you need to look carefully through the rest of the block to check that there isn't something after the else block which would occur whether running has been set to false or not.

The break statement communicates the intent more clearly, and lets the rest of the block get on with what it needs to do without worrying about earlier conditions.

Note that this is exactly the same sort of argument that people have about multiple return statements in a method. For example, if I can work out the result of a method within the first few lines (e.g. because some input is null, or empty, or zero) I find it clearer to return that answer directly than to have a variable to store the result, then a whole block of other code, and finally a return statement.


AFAIK nothing, really. Teachers are just allergic to goto, because they heard somewhere it's really bad. Otherwise you would just write:

bool guard = true;
do
{
   getInput();
   if (something)
     guard = false;
} while (guard)

Which is almost the same thing.

Maybe this is cleaner (because all the looping info is contained at the top of the block):

for (bool endLoop = false; !endLoop;)
{

}


Douglas Crockford had a remark about how he wished JavaScript contained a loop structure:

loop
{
  ...code...
}

And I don't think Java would be any worse for having a loop structure either.

There's nothing inherently wrong with while(true) loops, but there is a tendency for teachers to discourage them. From the teaching perspective, it's very easy to have students create endless loops and not understand why the loop isn't ever escaped.

But what they rarely mention is that all looping mechanisms can be replicated with while(true) loops.

while( a() )
{
  fn();
}

is the same as

loop
{
  if ( !a() ) break;
  fn();
}

and

do
{
  fn();
} while( a() );

is the same as:

loop
{
  fn();
  if ( !a() ) break;
}

and

for ( a(); b(); c() )
{
  fn();
}

is the same as:

a();
loop
{
  if ( !b() ) break;
  fn();
  c();
}

As long as you can set up your loops in a way that works the construct that you choose to use is unimportant. If it happens to fit in a for loop, use a for loop.

One last part: keep your loops simple. If there's a lot of functionality that needs to happen on every iteration, put it in a function. You can always optimize it after you've got it working.


Back in 1967, Edgar Dijkstra wrote an article in a trade magazine about why goto should be eliminated from high level languages to improve code quality. A whole programming paradigm called "structured programming" came out of this, though certainly not everyone agrees that goto automatically means bad code.

The crux of structured programming is essentially that the structure of the code should determine its flow rather than having gotos or breaks or continues to determine flow, wherever possible. Similiarly, having multiple entry and exit points to a loop or function are also discouraged in that paradigm.

Obviously this is not the only programming paradigm, but often it can be easily applied to other paradigms like object oriented programming (ala Java).

Your teachers has probably been taught, and is trying to teach your class that we would best avoid "spaghetti code" by making sure our code is structured, and following the implied rules of structured programming.

While there is nothing inherently "wrong" with an implementation that uses break, some consider it significantly easier to read code where the condition for the loop is explicitly specified within the while() condition, and eliminates some possibilities of being overly tricky. There are definitely pitfalls to using a while(true) condition that seem to pop up frequently in code by novice programmers, such as the risk of accidentally creating an infinite loop, or making code that is hard to read or unnecessarily confusing.

Ironically, exception handling is an area where deviation from structured programming will certainly come up and be expected as you get further into programming in Java.

It is also possible your instructor may have expected you to demonstrate your ability to use a particular loop structure or syntax being taught in that chapter or lesson of your text, and while the code you wrote is functionally equivalent, you may not have been demonstrating the particular skill you were supposed to be learning in that lesson.


The ususal Java convention for reading input is:

import java.io.*;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String strLine;

while ((strLine = br.readLine()) != null) {
  // do something with the line
}

And the usual C++ convention for reading input is:

#include <iostream>
#include <string>
std::string data;
while(std::readline(std::cin, data)) {
  // do something with the line
}

And in C, it's

#include <stdio.h>
char* buffer = NULL;
size_t buffer_size;
size_t size_read;
while( (size_read = getline(&buffer, &buffer_size, stdin)) != -1 ){
  // do something with the line
}
free(buffer);

or if you're convinced you know how long the longest line of text in your file is, you can do

#include <stdio.h>
char buffer[BUF_SIZE];
while (fgets(buffer, BUF_SIZE, stdin)) {
  //do something with the line
}

If you're testing to see whether your user entered a quit command, it's easy to extend any of these 3 loop structures. I'll do it in Java for you:

import java.io.*;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;

while ((line = br.readLine()) != null  && !line.equals("quit") ) {
  // do something with the line
}

So, while there certainly are cases where break or goto is justified, if all you're doing is reading from a file or the console line by line, then you shouldn't need a while (true) loop to accomplish it -- your programming language has already supplied you with an appropriate idiom for using the input command as the loop condition.


It's not such a terrible thing, but you need to take into consideration other developers when coding. Even in school.

Your fellow developers should be able to see the exit clause for your loop, at the loop declaration. You didn't do that. You hid the exit clause in the middle of the loop, making more work for someone else who comes along and tries to understand your code. This is the same reason that things like "break" are avoided.

That being said, you'll still see things like this in a LOT of code out there in the real world.


It's your gun, your bullet and your foot...

It's bad because you are asking for trouble. It won't be you or any of the other posters on this page who have examples of short/simple while loops.

The trouble will start at some very random time in the future. It might be caused by another programmer. It might be the person installing the software. It might be the end user.

Why? I had to find out why a 700K LOC app would gradually start burning 100% of the CPU time until every CPU was saturated. It was an amazing while (true) loop. It was big and nasty but it boiled down to:

x = read_value_from_database()
while (true) 
 if (x == 1)
  ...
  break;
 else if (x ==2)
  ...
  break;
and lots more else if conditions
}

There was no final else branch. If the value did not match an if condition the loop kept running until the end of time.

Of course, the programmer blamed the end users for not picking a value the programmer expected. (I then eliminated all instances of while(true) in the code.)

IMHO it is not good defensive programming to use constructs like while(true). It will come back to haunt you.

(But I do recall professors grading down if we did not comment every line, even for i++;)


It's bad in the sense that structured programming constructs are preferred to (the somewhat unstructured) break and continue statements. They are, by comparison, preferred to "goto" according to this principle.

I'd always recommend making your code as structured as possible... although, as Jon Skeet points out, don't make it more structured than that!


According to my experience in most cases loops have the "main" condition to continue. This is the condition that should be written into while() operator itself. All other conditions that may break the loop are secondary, not so important etc. They can be written as additional if() {break} statements.

while(true) is often confusing and is less readable.

I think that these rules do not cover 100% of cases but probably only 98% of them.


While not necessarily an answer as to why not to use while (true), I've always found this comic and accompanying author's statement a succinct explanation as to why to do while instead of do-while.

With regards to your question: There is no inherent problem with

while(true) {
   do_stuff();
   if(exit_time) {
      break;
   }
}

... if you know what you're doing and making sure that exit_time will at some point evaluate to true.

Teachers discourage you from using while(true) because until and unless you're at the point that you know exactly what you're doing, it's an easy way to make a critical mistake.


There's no major problem with while(true) with break statements, however some may think its slightly lowers the code readability. Try to give variables meaningful names, evaluate expressions in the proper place.

For your example, it seems much clearer to do something like:

do {
   input = get_input();
   valid = check_input_validity(input);    
} while(! valid)

This is especially true if the do while loop gets long -- you know exactly where the check to see if there's an extra iteration is occurring. All variables/functions have appropriate names at the level of abstraction. The while(true) statement does is tell you that processing isn't in the place you thought.

Maybe you want different output on the second time through the loop. Something like

input = get_input();
while(input_is_not_valid(input)) {
    disp_msg_invalid_input();
    input = get_input();
}

seems more readable to me then

do {
    input = get_input();
    if (input_is_valid(input)) {
        break;
    }
    disp_msg_invalid_input();
} while(true);

Again, with a trivial example both are quite readable; but if the loop became very large or deeply nested (which means you probably should already have refactored), the first style may be a bit clearer.


You might just use a Boolean flag to indicate when to end the while loop. Break and go to were reasons for software being to hard to maintain - the software-crisis(tm) - and should be avoided, and easily can be too.

It's a question if you are pragmatic or not. Pragmatic coders might just use break in that simple situation.

But it's good to get the habbit of not using them, else you might use them out of habbit in unsuitable situations, like in complicated nested loops where readability and maintainability of your code becomes harder by using break.


Maybe I am unlucky. Or maybe I just lack an experience. But every time I recall dealing with while(true) having break inside, it was possible to improve the code applying Extract Method to while-block, which kept the while(true) but (by coincidence?) transformed all the breaks into returns.

In my experience while(true) without breaks (ie with returns or throws) are quite comfortable and easy to understand.


  void handleInput() {
      while (true) {
          final Input input = getSomeInput();
          if (input == null) {
              throw new BadInputException("can't handle null input");
          }
          if (input.isPoisonPill()) {
              return;
          }
          doSomething(input);
      }
  }


I think that yes it is pretty bad... or at least, for many developers. It is symptomatic of developers that don't think about their loop conditions. Consequently there is error prone.


It's more of an aesthetics thing, much easier to read code where you explicitly know why the loop will stop right in the declaration of the loop.


I would say that generally the reason it's not considered a good idea is that you are not using the construct to it's full potential. Also, I tend to think that a lot of programming instructors don't like it when their students come in with "baggage". By that I mean I think they like to be the primary influence on their students programming style. So perhaps that's just a pet peeve of the instructor's.


To me, the problem is readability.

A while statement with a true condition tells you nothing about the loop. It makes the job of understanding it much more difficult.

What would be easier to understand out of these two snippets?

do {
  // Imagine a nice chunk of code here
} while(true);

do {
  // Imagine a nice chunk of code here
} while(price < priceAllowedForDiscount);


I use something similar, but with opposite logic, in a lot of my functions.

DWORD dwError = ERROR_SUCCESS;

do
{
    if ( (dwError = SomeFunction()) != ERROR_SUCCESS )
    {
         /* handle error */
         continue;
    }

    if ( (dwError = SomeOtherFunction()) != ERROR_SUCCESS )
    {
         /* handle error */
         continue;
    }
}
while ( 0 );

if ( dwError != ERROR_SUCCESS )
{
    /* resource cleanup */
}


I guess using break to your teacher is like breaking a branch of tree to get the fruit,use some other tricks (bow the branch) so that you get the fruit and the branch is still alive.:)


1) Nothing is wrong with a do -while(true)

2) Your teacher is wrong.

NSFS!!:

3) Most teachers are teachers and not programmers.


It might be bad if your loop runs on a background thread, so when you close your application by terminating a UI thread, that piece of code will continue to execute. As others already said, you should always use some kind of check to provide a way for cancellation.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜