首页 文章

“while(true)”循环是如此糟糕吗? [关闭]

提问于
浏览
210

我已经用Java编程了好几年了,但我刚刚回到学校获得正式学位 . 我很惊讶地发现,在我上一次任务中,我使用了如下所示的循环而丢失了分数 .

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

现在我的测试我只是扫描一些控制台输入,但我被告知这种循环是不鼓励的,因为使用 break 类似于 goto ,我们只是不这样做 .

我完全理解 goto 及其Java堂兄 break:label 的陷阱,我很清楚不使用它们 . 我也意识到一个更完整的程序会提供一些其他的逃避手段,例如刚刚结束程序,但这不是我教授引用的原因,所以...

do-while(true) 有什么问题?

21 回答

  • 37

    虽然不一定是为什么不使用 while (true) 的答案,但我总是找到this comic and accompanying author's statement一个简洁的解释,为什么要做而不是做什么 .

    关于你的问题:没有固有的问题

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

    ... if 你知道你在做什么,并确保 exit_time 在某些时候评估为 true .

    老师不鼓励你使用 while(true) ,因为除非你做了,否则这是一个容易犯错误的方法 .

  • 12

    您可以只使用布尔标志来指示何时结束while循环 . Breakgo to 是软件难以维护的原因 - 软件危机(tm) - 应该避免,也很容易 .

    如果你务实与否,这是一个问题 . 务实的编码员可能只是在这种简单的情况下使用休息 .

    但最好不要使用它们,否则你可能会在不适合的情况下使用它们,比如在复杂的嵌套循环中,使用 break 会使代码的可读性和可维护性变得更难 .

  • 1

    我认为这是非常糟糕的......或者至少对许多开发者而言 . 这是没有开发人员的症状想想他们的循环条件 . 因此容易出错 .

  • 215

    我不会 - 但同样我通常至少会寻找替代方案 .

    在我写的第一件事情的情况下,我几乎总是至少尝试将它重构为更清晰的东西 . 有时它无法帮助(或者替代方案是有一个 bool 变量,除了指示循环的结束时,没有任何意义,但不如 break 语句清楚),但至少值得尝试 .

    作为使用 break 比使用标志更清晰的示例,请考虑:

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

    现在让我们强迫它使用一个标志:

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

    我认为后者更复杂:它有一个额外的 else 块, actOnInput 更加缩进,如果你想弄清楚当 testCondition 返回 true 时会发生什么,你需要仔细查看块的其余部分检查 else 块后面是否有东西,无论 running 是否设置为 false 都会发生 .

    break 语句更清楚地传达意图,并让其余的块继续处理它需要做的事情,而不必担心早期的情况 .

    请注意,这与人们对方法中的多个return语句完全相同 . 例如,如果我可以在前几行中计算出方法的结果(例如,因为某些输入为空,或为空,或者为零),我发现直接返回该答案比使用变量存储结果更清楚,然后是整个其他代码块,最后是 return 语句 .

  • 1

    AFAIK没什么,真的 . 教师对 goto 过敏,因为他们听到某些地方真的很糟糕 . 否则你只会写:

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

    这几乎是一回事 .

    也许这更干净(因为所有循环信息都包含在块的顶部):

    for (bool endLoop = false; !endLoop;)
    {
    
    }
    
  • 0

    while(true)break 语句没有任何重大问题,但有些人可能会认为它略微降低了代码的可读性 . 尝试给变量赋予有意义的名称,在适当的位置计算表达式 .

    对于您的示例,执行以下操作似乎更加清晰:

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

    如果do while循环变长,则尤其如此 - 您确切地知道检查的位置是否正在进行额外的迭代 . 所有变量/函数在抽象级别都有适当的名称 . while(true) 语句告诉您处理不在您想到的地方 .

    也许你想通过循环第二次想要不同的输出 . 就像是

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

    这对我来说似乎更具可读性

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

    同样,通过一个简单的例子,两者都是可读的;但如果循环变得非常大或深度嵌套(这意味着你可能已经应该重构),第一种风格可能会更清晰一点 .

  • 1

    道格拉斯·克罗克福德(Douglas Crockford)曾谈到他希望JavaScript包含一个 loop 结构:

    loop
    {
      ...code...
    }
    

    而且我认为 Java 对于拥有 loop 结构也不会更糟 .

    while(true) 循环没有任何内在错误,但教师倾向于阻止它们 . 从教学的角度来看,它从未逃过一劫 .

    但是他们很少提到的是 all 循环机制可以用 while(true) 循环复制 .

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

    是相同的

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

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

    是相同的:

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

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

    是相同的:

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

    只要您能够以某种方式设置循环,您选择使用的构造就不重要了 . 如果恰好适合 for 循环,请使用 for 循环 .

    最后一部分:保持循环简单 . 如果每次迭代都需要执行许多功能,请将其放入函数中 . 您可以在工作完成后随时对其进行优化 .

  • 10

    早在1967年,Dijkstra在一本行业杂志上写了一篇文章,讲述为什么要从高级语言中删除goto以提高代码质量 . 一个称为“结构化编程”的整个编程范例就是出于此问题,但当然并不是每个人都同意goto自动意味着代码不好 . 结构化编程的关键在于,代码的结构应该确定其流程,而不是在必要时确定或中断或继续确定流程 . 类似地,在该范例中也不鼓励对循环或函数具有多个进入和退出点 . 显然,这不是唯一的编程范例,但通常它可以很容易地应用于其他范例,如面向对象编程(ala Java) . 你的老师可能已经被教过了,并且正在努力教你的课,我们最好通过确保我们的代码结构化,并遵循结构化编程的隐含规则来避免“意大利面条代码” . 虽然使用break的实现没有任何固有的“错误”,但有些人认为在while()条件中明确指定循环条件的情况下读取代码要容易得多,并且消除了一些过于棘手的可能性 . 使用似乎在代码中经常弹出的while(true)条件肯定存在缺陷新手程序员,例如意外创建无限循环的风险,或使代码难以阅读或不必要的混淆 .

    具有讽刺意味的是,异常处理是一个区域,当您进一步使用Java编程时,结构化编程的偏差肯定会出现并且可以预期 .

    您的教师也可能希望您能够证明您能够使用特定的循环结构或在该章或教科书中教授的语法,并且您编写的代码在功能上等同,您可能没有证明你应该在那一课中学到的特殊技能 .

  • 14

    用于读取输入的用户Java惯例是:

    import java.io.*;
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    String strLine;
    
    while ((strLine = br.readLine()) != null) {
      // do something with the line
    }
    

    通常用于读取输入的C约定是:

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

    在C中,它是

    #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);
    

    或者,如果您确信您知道文件中最长的文本行有多长,那么您可以这样做

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

    如果您正在测试用户是否输入了 quit 命令,那么's easy to extend any of these 3 loop structures. I'将使用Java为您执行此操作:

    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
    }
    

    因此,虽然肯定存在 breakgoto 是合理的情况,但是如果您只需要 while (true) 循环来完成它 - 您的编程语言已经为您提供了使用输入命令作为循环条件的适当习惯用法 .

  • 1

    这不是一件很糟糕的事情,但在编码时你需要考虑其他开发人员 . 即使在学校 .

    您的开发人员应该能够在循环声明中看到循环的exit子句 . 你没有这样做 . 你在循环中间隐藏了退出子句,为其他人提供了更多的工作并试图理解你的代码 . 这与避免像“休息”这样的事情是一样的 .

    话虽这么说,你仍然会在现实世界中的很多代码中看到这样的事情 .

  • 15

    这是你的枪,你的子弹和你的脚......

    这很糟糕,因为你在寻找麻烦 . 本页面上的您或任何其他海报都不会有短/简单的循环示例 .

    麻烦将在未来的某个非常随机的时间开始 . 它可能是由另一个程序员引起的 . 可能是安装软件的人 . 它可能是最终用户 .

    为什么?我必须找出为什么700K LOC应用程序会逐渐开始燃烧100%的CPU时间,直到每个CPU都饱和为止 . 这是一个惊人的while(真)循环 . 它很大而令人讨厌,但它归结为:

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

    没有最终的其他分支 . 如果该值与if条件不匹配,则循环一直运行直到时间结束 .

    当然,程序员指责最终用户没有选择程序员预期的 Value . (然后我在代码中删除了while(true)的所有实例 . )

    恕我直言,使用像while(true)这样的结构并不是一个好的防御性编程 . 它会回来困扰你 .

    (但我确实记得,如果我们没有对每一行发表评论,即使对于我来说,教授也会评分;)

  • 2

    从某种意义上讲,结构化编程构造比(有点非结构化的)break和continue语句更受欢迎 . 相比之下,它们优选根据该原理“转到” .

    我总是建议尽可能让你的代码尽可能结构化......但是,正如Jon Skeet指出的那样,不要让它比那更有条理!

  • 4

    根据我在大多数情况下的经验,循环具有"main"条件以继续 . 这是应该写入while()运算符本身的条件 . 可能破坏循环的所有其他条件都是次要的,不是那么重要等等 . 它们可以写为附加的 if() {break} 语句 .

    while(true) 经常令人困惑,而且可读性较差 .

    我认为这些规则并不涵盖100%的案例,但可能只有98% .

  • 1

    也许我很不走运 . 或许我只是缺乏经验 . 但每当我回想起 while(true) 里面有 break 时,就有可能改进将Extract Method应用于while-block的代码,它保留了 while(true) 但是(巧合?)将所有的 break 转换为 return s .

    根据我的经验 while(true) 没有休息(即退货或抛出)非常舒适且易于理解 .


    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);
          }
      }
    
  • 3

    我在很多函数中都使用类似的东西,但逻辑相反 .

    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 */
    }
    
  • 99

    它更像是一种美学的东西,更容易阅读代码,你明确地知道为什么循环将在循环的声明中停止 .

  • 3

    1) do -while(true) 没有错

    2)你的老师错了 .

    的NSF !!:

    3)大多数教师是教师而不是程序员 .

  • 2

    我会说,一般来说,它不被认为是一个好主意的原因是你没有使用该结构充分发挥其潜力 . 此外,我倾向于认为当他们的学生带来“ Baggage ”时,很多编程教师都不喜欢它 . 我的意思是,我认为他们喜欢成为他们学生编程风格的主要影响因素 . 所以也许这只是导师的一个小小的烦恼 .

  • 5

    对我来说,问题是可读性 .

    具有真实条件的while语句不会告诉您有关循环的任何信息 . 它使理解它的工作变得更加困难 .

    从这两个片段中可以更容易理解的是什么?

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

    我想让你的老师使用休息就像打破一棵树的树枝来获取水果,使用其他一些技巧(鞠躬分支),这样你就可以获得水果,而树枝仍然活着 . :)

  • 3

    如果你的循环在后台线程上运行可能会很糟糕,所以当你通过终止一个UI线程关闭你的应用程序时,那段代码将继续执行 . 正如其他人已经说过的那样,你应该总是使用某种检查来提供取消方式 .

相关问题