我经常使用这个代码模式:
while(true) { //do something if() { break; } }
另一位程序员告诉我,这是不好的做法,我应该用更标准的替换它:
while(!) { //do something }
他的理由是你可以"轻易地忘记休息"并拥有无限循环.我告诉他,在第二个例子中你可以很容易地放入一个永远不会返回真实的条件,因此很容易有一个无限循环,所以两者都是同样有效的做法.
此外,我经常更喜欢前者,因为当你有多个断点时,即当多个条件退出循环时,它使代码更容易阅读.
任何人都可以通过为一方或另一方添加证据来丰富这一论点吗?
两个例子之间存在差异.第一个将每次至少执行一次"做某事",即使该陈述永远不是真的.当语句评估为true时,第二个只会"做某事".
我认为你要找的是一个do-while循环.我100%同意这while (true)
不是一个好主意,因为它使得维护这段代码变得困难,并且你正在逃避循环的方式非常goto
糟糕,这被认为是不好的做法.
尝试:
do { //do something } while (!something);
查看您的个人语言文档以获取确切的语法.但是看看这个代码,它基本上做了什么,然后检查while部分,看它是否应该再做一次.
引用那些着名的开发者的消息,华兹华斯:
......
事实上,监狱,我们
自己毁灭,没有监狱; 因此,对于我来说,
在各种各样的情绪中,"
在十四行诗的地面内,两个人的消遣将被束缚;
如果有些灵魂(因为他们的需要必须是这样),那些
已经感受到太多自由的重量的人会感到高兴,
应该在那里找到简短的慰借,正如我所发现的那样.
华兹华斯接受了十四行诗作为一个解放框架的严格要求,而不是作为一件紧身衣.我建议"结构化编程"的核心是放弃构建任意复杂的流程图的自由,以支持易于理解的解放.
我自由地同意,有时提前退出是表达行动的最简单方式.然而,我的经验是,当我强迫自己使用最简单的控制结构(并且真的考虑在这些约束内进行设计)时,我经常发现结果更简单,代码更清晰.缺点
while (true) { action0; if (test0) break; action1; }
是很容易让action0
和action1
越来越大的代码块,或加上"只是多了一个"测试突破的动作序列,直到它变得难以指向特定的线和回答这个问题,"条件做什么,我知道这一点吗?" 因此,在没有为其他程序员制定规则的情况下,我尽可能避免while (true) {...}
在我自己的代码中使用成语.
当您可以在表单中编写代码时
while (condition) { ... }
要么
while (!condition) { ... }
与没有出口(break
,continue
,或goto
在体内),该形式是优选的,因为对方能读取的代码,并只通过观察头部理解终止条件.非常好.
但是很多循环不适合这个模型,中间有明确退出的无限循环是一个光荣的模型.(循环continue
通常比循环更难理解break
.)如果你想要一些证据或权威来引用,那么Don Knuth关于Goto语句的结构化编程的着名论文就是最好的选择; 你会找到你想要的所有例子,论点和解释.
成语的一个小问题:将while (true) { ... }
你作为一个旧的Pascal程序员写作品牌,或者现在可能是Java程序员.如果您使用C或C++编写,那么首选的习惯用语就是
for (;;) { ... }
这没有充分的理由,但你应该这样写,因为这是C程序员期望看到它的方式.
我更喜欢
while(!) { //do something }
但我认为这更多的是可读性问题,而不是"忘记休息"的可能性.我认为忘记这break
是一个相当弱的论点,因为这将是一个错误,你会立即找到并修复它.
我反对使用a break
来摆脱无限循环的论点是你基本上使用break
语句作为goto
.我并不虔诚地反对使用goto
(如果语言支持它,这是公平的游戏),但如果有更可读的选择,我会尝试替换它.
在多break
点的情况下,我会替换它们
while( !|| ! || ! ) { //do something }
以这种方式整合所有停止条件使得更容易看到将结束此循环的内容. break
陈述可以洒在周围,这是可读的.
虽然(true)可能有意义,如果你有很多语句,你想要停止,如果有任何失败
while (true) { if (!function1() ) return; if (!function2() ) return; if (!function3() ) return; if (!function4() ) return; }
比...更好
while (!fail) { if (!fail) { fail = function1() } if (!fail) { fail = function2() } ........ }
哈维尔对我之前的回答(引用华兹华斯的一篇文章)做了一个有趣的评论:
我认为while(true){}是一个比'while(condition){}更"纯粹"的构造.
我无法用300个字符作出充分回应(对不起!)
在我的教学和指导中,我非正式地将"复杂性"定义为"为了能够理解这一行或表达,我需要在其余的代码中使用多少代码?" 我必须记住的东西越多,代码就越复杂.代码告诉我的越多,就越不复杂.
因此,为了降低复杂性,让我回答Javier的完整性和力量,而不是纯度.
我想到了这段代码:
while (c1) { // p1 a1; // p2 ... // pz az; }
同时表达两件事:
只要c1
保持真实,(整个)身体将重复,并且
在点1,其中a1
执行,c1
是保证持有.
差异是透视之一; 第一个问题与整个循环的外部动态行为有关,而第二个则有助于理解我a1
特别考虑的内在静态保证.当然,净效应a1
可能会失效c1
,要求我更难以理解我在第2点可以依赖的东西等.
让我们用一个特定的(微小的)例子来考虑条件和第一个动作:
while (index < length(someString)) { // p1 char c = someString.charAt(index++); // p2 ... }
"外部"问题是,循环显然正在做一些事情someString
,只有在index
定位的时候才能完成someString
.这就建立了一种期望,我们将在身体内部index
或someString
在身体内部进行修改(在我检查身体之前不知道的位置和方式),以便最终终止.这给了我思考身体的背景和期望.
"内在"问题是我们保证第1点之后的操作是合法的,所以在第2点阅读代码时,我可以考虑用我知道合法获得的char值做什么.(如果someString
是null ref,我们甚至无法评估条件,但我也假设我们已经在这个示例的上下文中保护了它!)
相比之下,形式的循环:
while (true) { // p1 a1; // p2 ... }
让我了解这两个问题.在外层,我想知道这是否意味着我真的应该期望这个循环永远循环(例如操作系统的主事件调度循环),或者是否还有其他事情发生.这既没有给出阅读身体的明确背景,也没有给出对(不确定的)终止进展的预期.
在内在层面,我绝对没有明确保证可能在第1点出现的任何情况.这个条件true
在任何地方都是正确的,是关于我们在程序中任何一点可以知道的最弱的陈述.在尝试考虑行动完成时,了解行动的先决条件是非常有价值的信息!
因此,我认为这个while (true) ...
成语比while (c1) ...
我上面描述的逻辑更加不完整和弱,因此更复杂.