'<'与'!='作为'for'循环中的条件?


31

假设您有以下for循环*:

for (int i = 0; i < 10; ++i) {
    // ...
}

通常也可以写成:

for (int i = 0; i != 10; ++i) {
    // ...
}

最终结果是相同的,因此是否有真正的论据来使用一个?我个人使用前者,以防万一i由于某种原因而陷入混乱,并跳过了值10。

*对不起幻数的用法,但这只是一个例子。


18
对于上述任何一种方法,最好的解决方案是使用箭头运算符:int i = 10; while(i --> 0) { /* stuff */ }
corsiKa 2011年

@glowcoder箭头运算符是我最喜欢的
Carson Myers

3
@glowcoder,很好,但是它从后面遍历。您可能并不总是希望如此。

2
@SuperElectric,它解析为while ((i--) > 0)
Kristopher Johnson

17
关于(可能是伪造的)一个工业事故的故事是由对传感器输入的!= MAX_TEMP进行while循环测试引起的。不幸的是,有一天,传感器输入从小于MAX_TEMP变为大于MAX_TEMP,而没有每次都通过MAX_TEMP。该过程过热,没有被发现,随后发生了火灾。有时!=和<之间会有区别。
查尔斯E.格兰特

Answers:


59

选择一个或另一个的原因是出于意图,因此,它增加了可读性

目的:循环的运行时间应i小于10,而不是i不等于10。即使在特定情况下后者可能相同,也并非您的意思,因此不应这样写。

可读性:写下您的意思的结果是它也更易于理解。例如,如果您使用i != 10,则读取代码的人可能会怀疑循环内部是否有某种方法i可能会变得大于10,并且循环应该继续(顺便说一句:在迭代器的头部以外的地方与迭代器打交道是很糟糕的样式) - for语句,但这并不意味着人们不这样做,因此维护人员期望它)。


3
不,循环的运行时间不应i小于“小于10”,而应达到(在这种情况下)上限之前。使用C ++间隔/迭代器范围基本原理时,通过来表达此逻辑要逻辑!=得多<
康拉德·鲁道夫

14
@Konrad我一点也不不同意。重点不是教条,而是选择最能表达条件意图的结构。因此,如果您选择从0开始向上移动的内容,则<可以精确地表达出来。
Deckard

6
@Konrad,您错过了重点。这个循环中的增量运算符使循环条件显然是一个上限,而不是一个标识比较。如果您要递减,那将是一个下限。如果要遍历无序集合,则身份可能是正确的条件。
Alex Feinman

3
@Alex增量不是我的意思。间隔甚至不一定顺序。它只是遍历......嗯,东西直到它到达终点。例如,哈希集中的元素。在C ++中,这是使用迭代器和for循环来完成的。<在这里使用将是愚蠢的。另外,戴卡德似乎也承认这一点。我非常同意他对我的评论。
Konrad Rudolph

1
注意,如果您使用带有追逐指针的旋转缓冲区,则必须使用!=
SF。

48

<即使增量恰好不是1,该模式通常也可用。

这允许执行循环的单一通用方法,而不管其实际执行方式如何。


6
i如果需要,它还允许完全安全地在循环内部进行修改。
马修·沙利

1
还是完全不安全地修改了“ i” ...另一个团队遇到了奇怪的服务器问题。它一直报告100%的CPU使用率,这一定是服务器或监视系统有问题,对吗?不,我发现了一个由“高级专家”编写的循环条件,存在我们正在讨论的相同问题。
jqa

12
除了不是所有的C ++ for循环都可以使用<,例如序列上的迭代器,因此!=这是在C ++中进行循环的一种常见方法。
David Thornley

@SnOrfus:我没有完全解析该评论。通常,您不会可靠地获得过多增加迭代器的异常(尽管您会遇到更具体的情况)。
David Thornley

好吧,我遵循该<模式,因为它需要一个击键而不是>模式的两个击键和四个的击键!=。确实,这是类似于OCD的效率问题。
Spoike 2011年

21

在C ++中,斯科特·迈尔斯(Scott Myers)在“更有效的C ++”(项目6)中的建议始终使用第二个,除非您有理由不这样做,因为这意味着您对迭代器和整数索引具有相同的语法,因此可以在int和迭代器之间无缝交换语法没有任何变化。

在其他语言中,这并不适用,因此我想<可能是可取的,因为ThorbjørnRavn Andersen的观点。

顺便说,有一天,我与另一位开发人员讨论这个,他说喜欢的原因<!=,因为i可能会意外地被一个以上的增加,以及可能导致中断条件未得到满足; 那是IMO的废话。


17
“胡说八道” ...直到那天你在循环体中意外地有了一个额外的i ++。

2
+1,尤其是对于“废话”,因为它是。如果循环主体意外地增加了计数器,那么您将遇到更大的问题。
康拉德·鲁道夫

12
@B泰勒,我们只是人类,以前曾经发生过更大的错误。考虑<!=那时更具防御性的编程……

2
@ThorbjørnRavn Andersen-我不是说我不同意你的意思,我是的。<更防御,我的同事讨厌我写,!=所以我不喜欢。但是,!=在C ++中有一个案例,这就是我所介绍的。

9
i++当将while循环更改为for循环时,可能会导致意外额外花费的一种情况。但是,不能确定迭代次数的一半是否比(近)无限循环更好;后者可能会使错误更明显。
ak2

12

我认为使用(i <10)是更安全的做法。它捕获了潜在的退出案例的最大数量-大于或等于10的所有案例。将其与其他案例进行对比(i = 10);当我刚好是10岁时,它只会遇到一个可能的退出案例。

这样做的副产品是提高了可读性。另外,如果增量为其他1,则当我们在编写退出案例时出错时,可以帮助最小化问题的可能性。

考虑:

1) for (i = 1; i < 14; i+=2)

2) for (i = 1; i != 14; i+=2)

尽管这两种情况都可能有缺陷/错误,但是第二种错误可能更多,因为它不会退出。第一种情况会退出,即使有14个错误的数字(在15个可能更好),也有可能在正确的位置退出。


第一种情况可能是正确的!如果要迭代所有小于14的自然数,则没有更好的方法来表达它-计算“适当的”上限(13)会很愚蠢。
maaartinus 2011年

9

这是另一个似乎没有人提出的答案。 for当需要迭代序列时,应使用循环。使用!=是陈述循环终止条件的最简洁的方法。但是,使用限制性较小的运算符是非常常见的防御性编程习惯。对于整数没有关系-这只是个人选择,没有更具体的示例。!=出于其他人陈述的原因,使用要使用的迭代器循环遍历集合。如果考虑float或的序列double,那么您将!=不惜一切代价避免。

我想指出的是,for当您需要遍历序列时使用。生成的序列具有起点,间隔和终止条件。这些在for声明中简明地指定。如果您发现自己(1)不包括的步骤部分,for或者(2)指定了类似true保护条件的内容,那么您不应该使用for循环!

while循环用于在特定的条件满足时继续处理。 如果不处理序列,则可能需要while循环。保护条件参数在这里是相似的,但是在a whilefor循环之间的决定应该是一个非常有意识的决定。while在C ++圈子IMO中,该循环未被充分认识。

如果您正在处理项目的集合(非常常见的for-loop用法),那么您确实应该使用更专业的方法。不幸的是,std::for_each由于多种原因,在C ++中非常痛苦。在许多情况下,以for独立功能(虽然有些痛苦)将环路的主体分开会导致更清洁的解决方案。 当使用集合,考虑std::for_eachstd::transformstd::accumulate 以这种方式表达时,许多算法的实现变得简洁明了。更不用说将循环主体隔离为单独的函数/方法会迫使您专注于算法,算法的输入要求和结果。

如果您使用的是Java,Python,Ruby甚至C ++ 0x,则应该使用适当的foreach循环集合。随着C ++编译器实现此功能,for这些类型的讨论将消失许多循环。


2
“但是,使用较少限制的运算符是一种非常常见的防御性编程习惯。” 这或多或少地总结了它。
James P.

1为discussin在意图的差异与比较whileforforeach(或语言equivilant)
凯文PENO

严格地说,while循环用于继续处理,直到一个特定的条件并不满足
参宿

@Alnitak-D'oh ...我将解决这个问题:)
D.Shawley 2012年

我对“较少限制”的两种可能含义感到困惑:它可能表示运算符对传递的值宽容(<)或运算符对失败的值宽容(!=)。
Mateen Ulhaq '16

4

使用“小于”在语义上通常是正确的,实际上,您的意思是直到i不小于10 为止,因此“小于”清楚地传达了您的意图。使用“不等于”显然可以在虚拟调用情况下使用,但传达的含义略有不同。在某些情况下,这可能是您所需要的,但根据我的经验,从来没有这样。如果确实有一个i可能大于或小于10 的情况,但您想一直循环直到等于10,那么该代码确实需要非常清楚地注释,并且可能可以使用其他一些结构更好地编写,例如作为一个while循环。


2

我之所以支持a而less than不是a的原因之一not equals是充当后卫。在某些有限的情况下(不良的编程或消毒),not equals可以跳过,而less than仍然有效。

这是一个缺少消毒检查导致奇怪结果的示例:http : //www.michaeleisen.org/blog/? p=358


0

这是您可能更喜欢使用<而不是的原因之一!=。如果您使用的语言具有全局变量作用域,那么如果修改其他代码会i怎样?如果您使用<而不是!=,则发生的最坏情况是迭代完成得更快:也许其他一些代码i偶然增加了,您在for循环中跳过了几次迭代。但是,如果您从0到10循环,而循环达到9,又i由于某些奇怪的原因,一些写入不良的线程增量会发生什么。然后,循环完成该迭代并递增,i以使该值现在为11。由于循环已跳过退出条件(i从不等于10),因此它将无限循环。

导致这种情况的不一定是特别怪异的线程化和全局变量类型逻辑。可能只是您正在编写需要回溯的循环。如果您要i在循环内部进行变异,并且弄乱了逻辑,那么逻辑就应该具有上限而不是a,这样!=就不太可能使您陷入无限循环。


3
当然,对于每个循环来说,这似乎是一个完美的论据,并且通常来说,它是一种更具功能性的编程风格。但是,当可变变量变得更加有趣时,为什么还要这么做呢?
Tom Morris

3
我认为这不是一个很好的理由。无论哪种方式,都有需要发现并修复的错误,但是无限循环会使错误变得更加明显。
ak2

0

在嵌入式世界中,尤其是在嘈杂的环境中,您不能指望RAM的运行方式必须正确。在使用“ ==”或“!=”进行比较的条件(有条件的情况下)中,您总是冒着变量跳过终止循环的关键值的风险-这可能会带来灾难性后果——Mars Lander水平的后果。在这种情况下使用“ <”或“>”可提供更高的安全性,以捕获“未知未知数”。

在原始示例中,如果i莫名其妙地将其弹射到大于10的值,则'<'比较将立即捕获错误并退出循环,但是'!='将继续向上计数,直到i包裹在0之前并返回至10


2
另一种与代码相关的变体存在,例如for (i=4; i<length; i++) csum+=dat[i];合法数据包始终始终具有length至少四个的a,但其中一个可能接收到损坏的数据包。如果不同的数据包类型具有不同的长度要求,则可能希望作为每种数据包类型不同的处理的一部分来验证长度,但首先将校验和作为公共逻辑的一部分来验证。如果接收到长度不足的数据包,则校验和计算报告的是“好”还是“坏”并不重要,但它不会崩溃。
2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.