我已经用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循环的例子。
麻烦将在未来某个非常随机的时间开始。可能是其他程序员造成的。可能是安装软件的人。可能是最终用户。
为什么?我必须找出为什么700K的LOC应用程序会逐渐开始消耗100%的CPU时间,直到每个CPU都饱和。这是一个神奇的while (true)循环。这件事又大又恶心,但归结起来就是:
x = read_value_from_database()
while (true)
if (x == 1)
...
break;
else if (x ==2)
...
break;
and lots more else if conditions
}
没有最后的其他分支。如果值不匹配If条件,循环将一直运行到时间结束。
当然,程序员指责最终用户没有选择程序员期望的值。(然后我在代码中删除了while(true)的所有实例。)
恕我直言,使用while(true)这样的结构并不是很好的防御性编程。它会回来困扰你的。
(但我确实记得,如果我们不评论每一行,教授就会扣分,即使是i++;)
早在1967年,Edgar Dijkstra在一份行业杂志上写了一篇文章,讲述了为什么应该从高级语言中删除goto以提高代码质量。一种叫做“结构化编程”的编程范式由此而来,尽管当然不是每个人都同意goto自动意味着糟糕的代码。
结构化编程的关键本质上是代码的结构应该决定它的流,而不是在任何可能的情况下使用goto或break或continue来决定流。类似地,在该范例中也不鼓励有多个循环或函数的入口和出口点。
显然,这不是唯一的编程范式,但通常它可以很容易地应用到其他范式,如面向对象编程(比如Java)。
你的老师可能已经被教导过,并且正试图教导你的班级,我们最好通过确保我们的代码是结构化的,并遵循结构化编程的隐含规则来避免“意大利面条代码”。
While there is nothing inherently "wrong" with an implementation that uses break, some consider it significantly easier to read code where the condition for the loop is explicitly specified within the while() condition, and eliminates some possibilities of being overly tricky. There are definitely pitfalls to using a while(true) condition that seem to pop up frequently in code by novice programmers, such as the risk of accidentally creating an infinite loop, or making code that is hard to read or unnecessarily confusing.
具有讽刺意味的是,异常处理是一个与结构化编程有偏差的领域,并且随着您对Java编程的深入了解,这种偏差肯定会出现。
也有可能你的老师希望你展示你使用特定循环结构或语法的能力,而你写的代码在功能上是等价的,你可能没有展示你应该在那一课中学习的特定技能。
在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);
同样,通过一个简单的例子,两者都很容易理解;但是如果循环变得非常大或嵌套很深(这意味着您可能已经重构过了),那么第一种样式可能会更清晰一些。