我已经用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(正确)怎么了?


当前回答

虽然不一定是为什么不使用While的答案(正确),但我一直觉得这个漫画和伴随作者的声明简明地解释了为什么用While而不是do- While。

关于你的问题:没有内在的问题

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

... 如果您知道自己在做什么,并确保exit_time在某个时刻的值为true。

老师不鼓励你使用while(正确),因为除非你知道自己在做什么,否则很容易犯重大错误。

其他回答

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

根据我的经验,在大多数情况下循环有“主要”条件继续。这是应该写入while()操作符本身的条件。所有其他可能打破循环的条件都是次要的,不那么重要。它们可以被写成额外的if() {break}语句。

而(正确)往往令人困惑,可读性较差。

我认为这些规则并不能覆盖100%的情况,而可能只有98%。

这并不是一件可怕的事情,但是在编码时您需要考虑其他开发人员。甚至在学校。

其他开发人员应该能够在循环声明中看到循环的退出子句。你没有那样做。您将退出子句隐藏在循环的中间,为其他试图理解您的代码的人增加了更多的工作。这也是避免使用“break”这类词汇的原因。

话虽如此,在现实世界中,您仍然会在大量代码中看到类似的东西。

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

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

通常用于读取输入的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
}

如果您正在测试用户是否输入了退出命令,那么很容易扩展这3个循环结构中的任何一个。我用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
}

因此,虽然在某些情况下break或goto是合理的,但如果您所做的只是一行一行地从文件或控制台读取,那么您不应该需要while (true)循环来完成它——您的编程语言已经为您提供了使用input命令作为循环条件的适当习惯用法。