我已经用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(正确)怎么了?
我不会说这很糟糕——但同样地,我通常至少会寻找另一种选择。
如果这是我写的第一件事,我几乎总是至少试着把它重构成更清晰的东西。有时这是没有办法的(或者另一种选择是使用bool变量,它除了指示循环的结束之外没有任何意义,比break语句更不清楚),但至少值得一试。
作为一个使用break比使用flag更清楚的例子,考虑:
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语句的争论完全相同。例如,如果我可以在前几行内计算出一个方法的结果(例如,因为一些输入是空的,或者是空的,或者是零),我发现直接返回答案比用一个变量来存储结果,然后是一整个代码块,最后是一个返回语句更清楚。
通常用于读取输入的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命令作为循环条件的适当习惯用法。
我不会说这很糟糕——但同样地,我通常至少会寻找另一种选择。
如果这是我写的第一件事,我几乎总是至少试着把它重构成更清晰的东西。有时这是没有办法的(或者另一种选择是使用bool变量,它除了指示循环的结束之外没有任何意义,比break语句更不清楚),但至少值得一试。
作为一个使用break比使用flag更清楚的例子,考虑:
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语句的争论完全相同。例如,如果我可以在前几行内计算出一个方法的结果(例如,因为一些输入是空的,或者是空的,或者是零),我发现直接返回答案比用一个变量来存储结果,然后是一整个代码块,最后是一个返回语句更清楚。