我已经用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”这类词汇的原因。

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

其他回答

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

对我来说,问题在于可读性。

带有true条件的while语句不会告诉您任何关于循环的信息。这使得理解它的工作更加困难。

从这两个片段中更容易理解什么?

do {
  // Imagine a nice chunk of code here
} while(true);

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

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

1) do -while没有错(正确)

2)你的老师错了。

NSFS ! !:

3)大多数老师是老师而不是程序员。

Douglas Crockford说过,他多么希望JavaScript包含一个循环结构:

loop
{
  ...code...
}

我也不认为Java会因为拥有循环结构而变得更糟糕。

while(true)循环本身并没有什么错,但是老师们倾向于不鼓励它们。从教学的角度来看,很容易让学生创造出无穷无尽的循环,而不理解为什么循环没有被转义。

但是他们很少提到的是,所有的循环机制都可以用while(true)循环复制。

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

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

and

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

等于:

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

and

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

等于:

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

只要你能以一种工作的方式设置你的循环,你选择使用的结构是不重要的。如果它恰好适合for循环,则使用for循环。

最后一点:保持循环的简单性。如果在每次迭代中都需要执行许多功能,那么就将其放入一个函数中。在你让它工作之后,你总是可以优化它。