Is while (true) with break bad programming practice?

后端 未结 22 2188
-上瘾入骨i
-上瘾入骨i 2020-11-27 04:38

I often use this code pattern:

while(true) {

    //do something

    if() {
        break;
    }

}   

Another progr

相关标签:
22条回答
  • 2020-11-27 04:57

    while (true) might make sense if you have many statements and you want to stop if any fail

     while (true) {
         if (!function1() ) return;   
         if (!function2() ) return;   
         if (!function3() ) return;   
         if (!function4() ) return;  
       }
    

    is better than

     while (!fail) {
         if (!fail) {
           fail = function1()
         }           
         if (!fail) {
           fail = function2()
         }  
         ........
    
       }
    
    0 讨论(0)
  • 2020-11-27 04:57

    The first is OK if there are many ways to break from the loop, or if the break condition cannot be expressed easily at the top of the loop (for example, the content of the loop needs to run halfway but the other half must not run, on the last iteration).

    But if you can avoid it, you should, because programming should be about writing very complex things in the most obvious way possible, while also implementing features correctly and performantly. That's why your friend is, in the general case, correct. Your friend's way of writing loop constructs is much more obvious (assuming the conditions described in the preceding paragraph do not obtain).

    0 讨论(0)
  • 2020-11-27 04:57

    The problem is that not every algorithm sticks to the "while(cond){action}" model.

    The general loop model is like this :

    loop_prepare
     loop:
      action_A
      if(cond) exit_loop
      action_B
      goto loop
    after_loop_code
    

    When there is no action_A you can replace it by :

    loop_prepare
      while(cond)
      action_B
    after_loop_code
    

    When there is no action_B you can replace it by :

    loop_prepare
      do action_A
      while(cond)
    after_loop_code
    

    In the general case, action_A will be executed n times and action_B will be executed (n-1) times.

    A real life example is : print all the elements of a table separated by commas. We want all the n elements with (n-1) commas.

    You always can do some tricks to stick to the while-loop model, but this will always repeat code or check twice the same condition (for every loops) or add a new variable. So you will always be less efficient and less readable than the while-true-break loop model.

    Example of (bad) "trick" : add variable and condition

    loop_prepare
    b=true // one more local variable : more complex code
     while(b): // one more condition on every loop : less efficient
      action_A
      if(cond) b=false // the real condition is here
      else action_B
    after_loop_code
    

    Example of (bad) "trick" : repeat the code. The repeated code must not be forgotten while modifying one of the two sections.

    loop_prepare
    action_A
     while(cond):
      action_B
      action_A
    after_loop_code
    

    Note : in the last example, the programmer can obfuscate (willingly or not) the code by mixing the "loop_prepare" with the first "action_A", and action_B with the second action_A. So he can have the feeling he is not doing this.

    0 讨论(0)
  • 2020-11-27 04:59

    Javier made an interesting comment on my earlier answer (the one quoting Wordsworth):

    I think while(true){} is a more 'pure' construct than while(condition){}.

    and I couldn't respond adequately in 300 characters (sorry!)

    In my teaching and mentoring, I've informally defined "complexity" as "How much of the rest of the code I need to have in my head to be able to understand this single line or expression?" The more stuff I have to bear in mind, the more complex the code is. The more the code tells me explicitly, the less complex.

    So, with the goal of reducing complexity, let me reply to Javier in terms of completeness and strength rather than purity.

    I think of this code fragment:

    while (c1) {
        // p1
        a1;
        // p2
        ...
        // pz
        az;
    }
    

    as expressing two things simultaneously:

    1. the (entire) body will be repeated as long as c1 remains true, and
    2. at point 1, where a1 is performed, c1 is guaranteed to hold.

    The difference is one of perspective; the first of these has to do with the outer, dynamic behavior of the entire loop in general, while the second is useful to understanding the inner, static guarantee which I can count on while thinking about a1 in particular. Of course the net effect of a1 may invalidate c1, requiring that I think harder about what I can count on at point 2, etc.

    Let's put a specific (tiny) example in place to think about the condition and first action:

    while (index < length(someString)) {
        // p1
        char c = someString.charAt(index++);
        // p2
        ...
    }
    

    The "outer" issue is that the loop is clearly doing something within someString that can only be done as long as index is positioned in the someString. This sets up an expectation that we'll be modifying either index or someString within the body (at a location and manner not known until I examine the body) so that termination eventually occurs. That gives me both context and expectation for thinking about the body.

    The "inner" issue is that we're guaranteed that the action following point 1 will be legal, so while reading the code at point 2 I can think about what is being done with a char value I know has been legally obtained. (We can't even evaluate the condition if someString is a null ref, but I'm also assuming we've guarded against that in the context around this example!)

    In contrast, a loop of the form:

    while (true) {
        // p1
        a1;
        // p2
        ...
    }
    

    lets me down on both issues. At the outer level, I am left wondering whether this means that I really should expect this loop to cycle forever (e.g. the main event dispatch loop of an operating system), or whether there's something else going on. This gives me neither an explicit context for reading the body, nor an expectation of what constitutes progress toward (uncertain) termination.

    At the inner level, I have absolutely no explicit guarantee about any circumstances that may hold at point 1. The condition true, which is of course true everywhere, is the weakest possible statement about what we can know at any point in the program. Understanding the preconditions of an action are very valuable information when trying to think about what the action accomplishes!

    So, I suggest that the while (true) ... idiom is much more incomplete and weak, and therefore more complex, than while (c1) ... according to the logic I've described above.

    0 讨论(0)
  • 2020-11-27 05:00

    It depends on what you’re trying to do, but in general I prefer putting the conditional in the while.

    • It’s simpler, since you don't need another test in the code.
    • It’s easier to read, since you don’t have to go hunting for a break inside the loop.
    • You’re reinventing the wheel. The whole point of while is to do something as long as a test is true. Why subvert that by putting the break condition somewhere else?

    I’d use a while(true) loop if I was writing a daemon or other process that should run until it gets killed.

    0 讨论(0)
  • 2020-11-27 05:00

    I prefer the while(!) approach because it more clearly and immediately conveys the intent of the loop.

    0 讨论(0)
提交回复
热议问题