2008-09-25 260 views
5

这些for循环是算法形式正确性证明的第一个基本例子。他们有不同的但等效的终止条件:在后置循环终止条件

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

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

的区别变得清晰:

  • 第一个给出了i == N循环结束后的有力保障。

  • 第二个只给出了在循环终止后i >= N的弱保证,但您会被假设为i == N

如果出于任何原因增量++i是不断变化的东西像i += 2,或者如果i被修改的循环中,或者如果N为负,该程序可能会失败:

  • 的第一个可能陷入无限循环。它在出现错误的循环中提前失败。调试很容易。

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

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

+1

对于那些提到我不在for之外的人,有必要指出,在推理程序时我可以使用终止时的i值。例如,如果你想用一个保持不变P(i)的循环来建立P(N),那么i == N给你P(N),而当i> = N时不会。 – mweerden 2008-09-25 13:03:30

+0

+1表达良好的问题,清楚地表达每个选项的优点。 – 2010-10-06 08:23:29

回答

2

我们不应该孤立地看待计数器 - 如果由于某种原因某人改变了计数器增量的方式,他们会改变终止条件和结果逻辑(如果它是i == N所需的)。

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

1

如果你信任你的代码,你也可以做。如果你想让你的代码可读性和容易理解(并且因此更容忍改变某个你认为是klutz的人),我会使用类似于;

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

我总是用#2,那么你可以肯定的循环将终止...依托它是等于N之后是依靠副作用......难道你只是使用更好变量N本身?

对不起...我的意思#2

0

一般来说,我宁愿

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

在生产的童车程序的惩罚,似乎少了很多严重的,你不会有一个线程在一个永远困for循环,这种情况这可能非常危险并且很难诊断。

此外,通常我喜欢避免这些类型的循环,以支持更易读的foreach样式循环。

1

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

第一个变体没有这个质量。我可以看看它,而我所知道的是,我将从0开始,它不会等于N.不是很有帮助。

无论您如何终止循环,在循环外部使用循环控制变量都非常谨慎。在你的例子中,你(正确地)声明我在循环内部,所以它不在循环的范围之外,并且它的值的问题是没有意义的...

当然,第二个变体也有它的优点我看到的所有C参考都使用:-)

0

我更喜欢使用#2,只是因为我试图不扩展我在for循环之外的含义。如果我正在跟踪这样的变量,我会创建一个额外的测试。也许有人会说这是多余的或无效的,但它让我想起我的意图的读者:在这一点上,我必须等于n,

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

4

我倾向于使用第二种形式,只是因为我可以更确定该循环将终止。即通过改变循环内部的i来引入非终止错误更加困难。

当然,它也有被少一个字符键入略微懒惰优势;)

我还认为的是,在与合理的范围规则语言,作为i为循环结构内声明,它不应该在循环之外提供。这将减轻任何依赖于我在循环结束时等于N ...

0

我想你说得非常好,两者之间的区别。我有如下意见,虽然:

  • 这不是“语言无关”,我可以看到你的例子是在C++中,但有 情况下,你不允许修改里面的循环变量语言 循环以及其他不能保证索引的值在 循环(以及其中一部分都可用)后可用的其他索引。

  • 声明该fori 指数,所以我不会对i循环之后的价值投注。 这些例子有点误导,因为它们暗示for是 确定的循环。在现实中,它仅仅是一个写作的更方便的方法:

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

    注意i是如何块后不确定。

如果程序员知道所有上述不会使i价值的一般假设,将是明智的选择i<N作为结束条件,以确保出口条件将最终实现。

0

在C#中使用上述任何一种会导致编译器错误,如果你用我的外循环

0

我喜欢这有时会:

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

此版本的显示值的直接的范围内,我可以有。如果你真的需要这个,你的代码有太多的副作用,需要重写。

另一个版本:

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

可以帮助您确定循环体是如何经常被称为。这也有有效的用例。

0

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

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

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

因为它是不太容易出错,尤其是当代码被重构。我已经看到这种代码偶然变成了一个无限循环。

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

0

从我的正式验证和自动终止分析的角度来看,我强烈倾向于#2(<)。跟踪一些变量是很容易的(在var = x之前,对于某些非负数n,在var = x+n之后)。然而,要想看到i==N最终成立并不那么容易。为此,需要推断在每一步中i确切地增加了1,这在更复杂的示例中可能由于抽象而丢失。

如果你考虑循环增加2(i = i + 2),这个一般的想法变得更容易理解。为了保证终止,现在需要知道i%2 == N%2,而当使用<作为条件时,这是不相关的。