循环终止条件

发布于 2024-07-06 06:44:07 字数 732 浏览 3 评论 0 原文

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

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

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

差异在后置条件中变得很明显:

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

  • 第二个仅在循环终止后提供 i >= N 的弱保证,但您会很想假设 i == N。< /p>

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

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

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

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

These for-loops are among the first basic examples of formal correctness proofs of algorithms. They have different but equivalent termination conditions:

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

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

The difference becomes clear in the postconditions:

  • The first one gives the strong guarantee that i == N after the loop terminates.

  • The second one only gives the weak guarantee that i >= N after the loop terminates, but you will be tempted to assume that i == N.

If for any reason the increment ++i is ever changed to something like i += 2, or if i gets modified inside the loop, or if N is negative, the program can fail:

  • The first one may get stuck in an infinite loop. It fails early, in the loop that has the error. Debugging is easy.

  • The second loop will terminate, and at some later time the program may fail because of your incorrect assumption of i == N. It can fail far away from the loop that caused the bug, making it hard to trace back. Or it can silently continue doing something unexpected, which is even worse.

Which termination condition do you prefer, and why? Are there other considerations? Why do many programmers who know this, refuse to apply it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

俏︾媚 2024-07-13 06:44:08

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

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

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

I tend to use the second form, simply because then I can be more sure that the loop will terminate. I.e. it's harder to introduce a non-termination bug by altering i inside the loop.

Of course, it also has the slightly lazy advantage of being one less character to type ;)

I would also argue, that in a language with sensible scope rules, as i is declared inside the loop construct, it shouldn't be available outside the loop. This would mitigate any reliance on i being equal to N at the end of the loop...

染年凉城似染瑾 2024-07-13 06:44:08

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

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

We shouldn't look at the counter in isolation - if for any reason someone changed the way the counter is incremented they would change the termination conditions and the resulting logic if it's required for i==N.

I would prefer the the second condition since it's more standard and will not result in endless loop.

往日 2024-07-13 06:44:08

在 C++ 中,出于通用性考虑,首选使用 != 测试。 C++ 中的迭代器有多种概念,例如 输入迭代器正向迭代器, 双向迭代器, 随机访问迭代器,每个迭代器都用新功能扩展了前一个迭代器。 要使 < 工作,需要随机访问迭代器,而 != 仅需要输入迭代器。

In C++, using the != test is preferred for generality. Iterators in C++ have various concepts, like input iterator, forward iterator, bidirectional iterator, random access iterator, each of which extends the previous one with new capabilities. For < to work, random access iterator is required, whereas != merely requires input iterator.

战皆罪 2024-07-13 06:44:08

如果您信任您的代码,则可以执行任一操作。

如果你希望你的代码可读且易于理解(因此更能容忍那些你认为是笨手笨脚的人的改变),我会使用类似的东西;

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

If you trust your code, you can do either.

If you want your code to be readable and easily understood (and thus more tolerant to change from someone who you've got to assume to be a klutz), I'd use something like;

for ( int i = 0 ; i >= 0 && i < N ; ++i) 
面如桃花 2024-07-13 06:44:08

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

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

I always use #2 as then you can be sure the loop will terminate... Relying on it being equal to N afterwards is relying on a side effect... Wouldn't you just be better using the variable N itself?

[edit] Sorry...I meant #2

一江春梦 2024-07-13 06:44:08

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

第一个变体不具有这种质量。 我可以查看它,我所知道的是 i 将从 0 开始,并且它永远不会等于 N。不太有帮助。

无论如何终止循环,在循环外使用循环控制变量时保持警惕总是好的。 在您的示例中,您(正确地)在循环内声明 i ,因此它不在循环外的范围内,并且其值的问题没有实际意义......

当然,第二个变体也有一个优点,即它是所有 C 的优点我见过使用的参考资料:-)

I think most programmers use the 2nd one, because it helps figure out what goes on inside the loop. I can look at it, and "know" that i will start as 0, and will definitely be less than N.

The 1st variant doesn't have this quality. I can look at it, and all I know is that i will start as 0 and that it won't ever be equal to N. Not quite as helpful.

Irrespective of how you terminate the loop, it is always good to be very wary of using a loop control variable outside the loop. In your examples you (correctly) declare i inside the loop, so it is not in scope outside the loop and the question of its value is moot...

Of course, the 2nd variant also has the advantage that it's what all of the C references I have seen use :-)

不必在意 2024-07-13 06:44:08

一般来说,我更喜欢

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

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

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

In general I would prefer

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

The punishment for a buggy program in production, seems a lot less severe, you will not have a thread stuck forever in a for loop, a situation that can be very risky and very hard to diagnose.

Also, in general I like to avoid these kind of loops in favour of the more readable foreach style loops.

洒一地阳光 2024-07-13 06:44:08

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

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

I prefer to use #2, only because I try not to extend the meaning of i outside of the for loop. If I were tracking a variable like that, I would create an additional test. Some may say this is redundant or inefficient, but it reminds the reader of my intent: At this point, i must equal N

@timyates - I agree one shouldn't rely on side-effects

生活了然无味 2024-07-13 06:44:08

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

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

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

    //版本1 
      { 整数 i = 0; 
        而(我!= N){ 
          ... 
          ++我; 
        } 
      } 
      

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

如果程序员知道以上所有内容,就不会普遍假设 i 的值,并且会明智地选择 i 作为结束条件,以确保退出条件最终会得到满足。

I think you stated very well the difference between the two. I do have the following comments, though:

  • This is not "language-agnostic", I can see your examples are in C++ but there
    are languages where you are not allowed to modify the loop variable inside the
    loop and others that don't guarantee that the value of the index is usable after
    the loop (and some do both).

  • You have declared the i
    index within the for so I would not bet on the value of i after the loop.
    The examples are a little bit misleading as they implictly assume that for is
    a definite loop. In reality it is just a more convenient way of writing:

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

    Note how i is undefined after the block.

If a programmer knew all of the above would not make general assumption of the value of i and would be wise enough to choose i<N as the ending conditions, to ensure that the the exit condition will be eventually met.

原来是傀儡 2024-07-13 06:44:08

如果在循环之外使用 i,则在 C# 中使用上述任一方法都会导致编译器错误

Using either of the above in c# would cause a compiler error if you used i outside the loop

是伱的 2024-07-13 06:44:08

有时我更喜欢这个:

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

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

另一个版本:

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

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

I prefer this sometimes:

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

This version shows directly the range of values that i can have. My take on checking lower and upper bound of the range is that if you really need this, your code has too many side effects and needs to be rewritten.

The other version:

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

helps you determine how often the loop body is called. This also has valid use cases.

撕心裂肺的伤痛 2024-07-13 06:44:08

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

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

因为

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

它不太容易出错,特别是当代码被重构时。 我曾见过这种代码不小心变成了无限循环。

该论点表明“你会很想假设 i == N”,我不认为这是真的。 我从未做过这样的假设,也没有见过其他程序员做过这样的假设。

For general programming work I prefer

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

to

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

Because it is less error prone, especially when code gets refactored. I have seen this kind of code turned into an infinite loop by accident.

That argument made that "you will be tempted to assume that i == N", I don't believe is true. I have never made that assumption or seen another programmer make it.

烟花肆意 2024-07-13 06:44:08

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

如果您考虑一下递增 2 的循环 (i = i + 2),这个总体想法就更容易理解了。 为了保证终止,现在需要知道 i%2 == N%2,而当使用 < 作为条件时,这是无关紧要的。

From my standpoint of formal verification and automatic termination analysis, I strongly prefer #2 (<). It is quite easy to track that some variable is increased (before var = x, after var = x+n for some non-negative number n). However, it is not that easy to see that i==N eventually holds. For this, one needs to infer that i is increased by exactly 1 in each step, which (in more complicated examples) might be lost due to abstraction.

If you think about the loop which increments by two (i = i + 2), this general idea becomes more understandable. To guarantee termination one now needs to know that i%2 == N%2, whereas this is irrelevant when using < as the condition.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文