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


当前回答

从某种意义上说,结构化编程构造优先于(有些非结构化的)中断和继续语句,这是不好的。相比之下,根据这一原则,他们更喜欢“去”。

我总是建议让你的代码尽可能结构化……不过,正如乔恩·斯基特指出的那样,不要让它更有条理!

其他回答

在break语句中使用while(正确)没有大问题,但是有些人可能会认为它会略微降低代码的可读性。试着给变量起个有意义的名字,在合适的位置求值表达式。

对于你的例子,这样做似乎更清楚:

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

同样,通过一个简单的例子,两者都很容易理解;但是如果循环变得非常大或嵌套很深(这意味着您可能已经重构过了),那么第一种样式可能会更清晰一些。

这更像是一个美观的东西,更容易阅读代码,你明确地知道为什么循环会在循环的声明中停止。

从某种意义上说,结构化编程构造优先于(有些非结构化的)中断和继续语句,这是不好的。相比之下,根据这一原则,他们更喜欢“去”。

我总是建议让你的代码尽可能结构化……不过,正如乔恩·斯基特指出的那样,不要让它更有条理!

通常用于读取输入的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命令作为循环条件的适当习惯用法。

您可以使用一个布尔标志来指示何时结束while循环。中断和转向是软件难以维护的原因——软件危机(tm)——应该避免,也可以很容易地避免。

这是一个你是否务实的问题。实用的程序员在这种简单的情况下可能只使用break。

但是最好养成不使用它们的习惯,否则可能会在不合适的情况下使用它们,比如在复杂的嵌套循环中,代码的可读性和可维护性因使用break而变得更加困难。