循环终止条件

Posted

技术标签:

【中文标题】循环终止条件【英文标题】:Loop termination conditions 【发布时间】:2010-09-13 00:40:44 【问题描述】:

这些for-loops 是算法形式正确性证明的第一个基本示例。它们具有不同但等效的终止条件:

1   for ( int i = 0; i != N; ++i )

2   for ( int i = 0; i < N; ++i )

后置条件中的区别变得明显:

第一个给出了循环终止后i == N的有力保证。

第二个仅在循环终止后提供i &gt;= N 的弱保证,但您会很容易假设i == N

如果由于任何原因将增量 ++i 更改为类似 i += 2,或者如果 i 在循环内被修改,或者如果 N 为负数,则程序可能会失败:

第一个可能会陷入无限循环。它在有错误的循环中提前失败。调试很简单。

第二个循环将终止,并且由于您对i == N 的错误假设,稍后程序可能会失败。它可能会在远离导致错误的循环的地方失败,从而难以追溯。或者它可以默默地继续做一些意想不到的事情,这更糟糕。

您更喜欢哪种终止条件,为什么?还有其他考虑吗?为什么很多知道这个的程序员都拒绝应用?

【问题讨论】:

对于那些提到 i 在 for 之外不可用的人,请注意,在推理程序时可以使用终止时的 i 值。例如,如果你想用一个保持不变 P(i) 的循环来建立 P(N),那么 i==N 会给你 P(N) 而 i>=N 不会。 +1 表示写得很好的问题,清楚地表达了每个选项的优点。 【参考方案1】:

在 C++ 中,为了通用性,首选使用 != 测试。 C++ 中的迭代器有多种概念,如input iterator、forward iterator、bidirectional iterator、random access iterator,每一个都扩展了前一个概念,并提供了新的功能。要使&lt; 工作,需要随机访问迭代器,而!= 只需要输入迭代器。

【讨论】:

【参考方案2】:

我们不应该孤立地查看计数器 - 如果出于任何原因有人更改了计数器的递增方式,他们会更改终止条件和结果逻辑(如果 i==N 需要)。

我更喜欢第二个条件,因为它更标准,不会导致无限循环。

【讨论】:

【参考方案3】:

如果您信任您的代码,您可以选择其中任何一种。

如果您希望您的代码可读且易于理解(因此更能容忍从您必须假设为笨拙的人进行更改),我会使用类似的东西;

for ( int i = 0 ; i >= 0 && i < N ; ++i) 

【讨论】:

【参考方案4】:

我总是使用#2,因为这样你就可以确定循环将终止......之后依赖它等于 N 是依赖于副作用......你会不会更好地使用变量 N 本身?

[编辑] 抱歉...我的意思是 #2

【讨论】:

【参考方案5】:

总的来说,我更喜欢

for ( int i = 0; i < N; ++i )

对生产中的错误程序的惩罚似乎不那么严重,您不会让线程永远卡在 for 循环中,这种情况可能非常危险且难以诊断。

另外,一般来说,我喜欢避免使用此类循环,而使用更具可读性的 foreach 样式循环。

【讨论】:

【参考方案6】:

我认为大多数程序员都使用第二个,因为它有助于弄清楚循环内部发生了什么。我可以看它,并且“知道”我将从 0 开始,并且肯定会小于 N。

第一个变体没有这种品质。我可以看一下,我只知道我将从 0 开始,并且永远不会等于 N。没有那么有用。

不管你如何终止循环,在循环外使用循环控制变量时要非常小心。在您的示例中,您(正确地)在循环内声明 i,因此它不在循环外的范围内,并且其值的问题没有实际意义......

当然,第二个变体还有一个优点,那就是我见过的所有 C 引用都使用它:-)

【讨论】:

【参考方案7】:

我更喜欢使用#2,只是因为我尽量不在 for 循环之外扩展 i 的含义。如果我要跟踪这样的变量,我会创建一个额外的测试。有人可能会说这是多余或低效的,但它提醒了读者我的意图:此时,我必须等于 N

@timyates - 我同意不应该依赖副作用

【讨论】:

【参考方案8】:

我倾向于使用第二种形式,只是因为这样我可以更确定循环将终止。 IE。通过在循环内更改 i 来引入非终止错误更难。

当然,它还有一个稍微懒惰的优势,就是少输入一个字符;)

我还认为,在具有合理范围规则的语言中,由于 i 在循环构造内声明,它不应该在循环外可用。这将减轻对 i 在循环结束时等于 N 的任何依赖...

【讨论】:

我也在想同样的事情,当然大多数语言只会在循环范围内使用 i。 +1 在 Perl 中:for( my $i=0; $i&lt;N; ++$i )...【参考方案9】:

我认为您很好地说明了两者之间的区别。不过,我确实有以下 cmets:

这不是“语言不可知论”,我可以看到您的示例是用 C++ 编写的,但是 是不允许修改循环变量的语言 循环和其他不保证索引的值在之后可用的 循环(有些人两者都做)。

您已声明i for 内的索引,所以我不会在循环后押注i 的值。 这些示例有点误导,因为它们隐含地假设 for 是 一个确定的循环。实际上它只是一种更方便的写作方式:

// version 1
 int i = 0;
  while (i != N) 
    ...
    ++i;
  

注意i 在块之后是如何未定义的。

如果程序员知道以上所有内容,就不会对i的值做出一般假设,而是明智地选择i&lt;N作为结束条件,以确保最终满足退出条件。

【讨论】:

【参考方案10】:

如果在循环外使用 i,在 c# 中使用上述任何一种都会导致编译器错误

【讨论】:

【参考方案11】:

我有时更喜欢这个:

for (int i = 0; (i <= (n-1)); i++)  ... 

这个版本直接显示了我可以拥有的值的范围。我对检查范围的下限和上限的看法是,如果你真的需要这个,你的代码有太多的副作用,需要重写。

另一个版本:

for (int i = 1; (i <= n); i++)  ... 

帮助您确定调用循环体的频率。这也有有效的用例。

【讨论】:

【参考方案12】:

对于一般的编程工作我更喜欢

for ( int i = 0; i < N; ++i )

for ( int i = 0; i != N; ++i )

因为它不易出错,尤其是在代码被重构时。我曾看到这种代码不小心变成了无限循环。

那个论点说“你会很想假设 i == N”,我不相信这是真的。我从来没有做过这样的假设,也没有见过其他程序员做出这样的假设。

【讨论】:

【参考方案13】:

从形式验证和自动终止分析的角度来看,我非常喜欢#2 (&lt;)。很容易跟踪某些变量的增加(在var = x 之前,在var = x+n 之后对于一些非负数n)。但是,要看到i==N 最终成立并不容易。为此,需要推断i 在每一步中恰好增加了1,这(在更复杂的示例中)可能由于抽象而丢失。

如果您考虑以 2 为增量的循环 (i = i + 2),这个总体思路就会变得更容易理解。为了保证终止,现在需要知道i%2 == N%2,而当使用&lt; 作为条件时,这无关紧要。

【讨论】:

以上是关于循环终止条件的主要内容,如果未能解决你的问题,请参考以下文章

python条件循环终止

在js中怎样防止死循环

尽管存在终止条件,为什么无限循环仍然存在?

趁着课余时间学点Python终止循环,阻断循环

趁着课余时间学点Python终止循环,阻断循环

php如何循环一个数组,当找到一个对应条件时候,就会终止循环。。