C++ 循环不变量(特别是“加速C&“x2B”和“x2B”的第3章)

C++ 循环不变量(特别是“加速C&“x2B”和“x2B”的第3章),c++,invariants,C++,Invariants,我目前正在学习“加速C++”,刚刚在第3章中遇到了这个问题: // invariant: // we have read count grades so far, and // sum is the sum of the first count grades while (cin >> x) { ++count; sum += x; } 作者随后解释说,不变量需要特别注意,因为当输入被读入x时,我们将读取count+1等级,因此不变量将不真实。类似地,当我们增

我目前正在学习“加速C++”,刚刚在第3章中遇到了这个问题:

// invariant:
//  we have read count grades so far, and
//  sum is the sum of the first count grades
while (cin >> x) {
    ++count;
   sum  +=  x;
}
作者随后解释说,不变量需要特别注意,因为当输入被读入
x
时,我们将读取
count+1
等级,因此不变量将不真实。类似地,当我们增加计数器时,
sum
将不再是最后一次计数分数的总和(如果您没有猜到,它是计算学生分数的传统程序)

我不明白为什么这很重要。当然,对于任何其他循环,类似的说法都是正确的?例如,这是本书的第一个
while
循环(稍后填写输出):

//不变量:到目前为止,我们已经编写了r行
while(r!=行){
//写入一行输出

std::cout从您的描述中,听起来作者在胡说八道。是的,在指令之间,不变量暂时变得不真实,但每当您执行类似的非原子操作时,就会发生这种情况。只要没有任何明显的断点,可能导致不变量不正确,并且程序处于错误状态状态一致,你很好


在这种情况下,唯一可能发生的方法是,如果std::cout在不变量不真实的情况下抛出异常,那么您会在某个地方捕获该异常,但会在错误的状态下继续执行。在我看来,作者过于迂腐。因此,只要您没有任何break/continue语句出现在错误的位置,或者没有异常出现你没问题。我怀疑很多人会费心关注你的示例代码,因为它太简单了。

我相信这本书是指while循环停止的方式。在第二种情况下,很容易看到循环将在“r”增加到等于“rows”时停止因为大多数C++中的计数都是零,所以这很可能为每行输出一行。 另一方面,第一个示例是对cin对象上的“>>”使用运算符重载。只要此函数不返回零,while循环将继续。在输入关闭之前,该运算符重载不会返回此值

您可以按什么键使“cin>>”返回0?否则循环将永远不会结束。您需要确保不会创建这样的循环


需要在条件之外添加一行来停止循环。请查找“break”和“continue”语句。

一般来说,不变量只适用于循环的迭代之间。(至少我是这样理解的!)

一般情况如下:

[invariant true];
while (keep going) {
    [state transformation];
    [invariant true];
}
但是在状态转换期间,不变量不一定成立

作为一个单独的风格说明:如果你想成为一名超级程序员,不要把不变量留在注释中,让它们成为断言

// Loop invariant: x+y = -4
for (int x = 0; x < 10; x++) {
    [do something];
    assert(x+y == -4);  // Loop invariant here!
}
//循环不变量:x+y=-4
对于(int x=0;x<10;x++){
[做点什么];
断言(x+y==-4);//此处循环不变!
}

这样您就有了自检代码。

这在异常安全环境中非常有趣/重要

考虑以下场景:

  • “count”是一个用户定义的类,具有重载的
    运算符+++
  • 重载的
    运算符+++
    引发异常
  • 稍后在循环外部捕获异常(即,循环看起来像现在一样,没有try/catch)
在这种情况下,循环不变量不再成立,循环中发生的所有事情的状态都有问题。行是否已写入?计数是否已更新?总和是否仍然正确


一些额外的保护(以临时变量的形式保存中间值和一些try/catch)需要确保即使在抛出异常时,所有内容都保持一致。

这本书似乎比它应该做的要复杂得多。我真的不认为用不变量解释循环是一件好事。这有点像用量子物理解释加法

作者随后解释说,不变量需要特别注意,因为当输入被读入变量x时,我们将读取计数+1级,因此不变量将不真实。同样,当我们增加计数器时,变量和将不再是最后计数级的和(如果你没有猜到,这是计算学生分数的传统程序)

首先,不变量是不清楚的。如果不变量是“在
的迭代结束时,
循环,我们已经阅读了
计数
等级和
求和
”,那么在我看来这是正确的。不变量没有明确指定,所以谈论它何时被尊重和不被尊重都没有意义

如果不变量是“在<代码>循环的任何迭代点,而<代码>循环,我们已经读过…`,那么严格地说,该不变量将不是真的。就循环而言,我认为不变量应该是指在循环的开始、结束或固定点处存在的状态

我没有那本书,我也不知道事情是否得到澄清,但似乎使用不变量是错误的。如果它们的不变量不是真的,为什么还要麻烦使用一个呢

我认为你不应该太担心这个问题。只要你了解这些循环是如何工作的,你就可以了。如果你想通过不变量来理解它们,你可以,但是你必须注意你选择的不变量。不要选择一个不好的,否则它就达不到目的。你应该选择一个易于编写代码的不变量尊重它,而不是选择一个随机的然后努力编写尊重它的代码,并且绝对不要选择一个模糊的然后编写具有
// Loop invariant: x+y = -4
for (int x = 0; x < 10; x++) {
    [do something];
    assert(x+y == -4);  // Loop invariant here!
}
// invariant: we have written r rows so far
int r = 0; // this is also important!
while (r != rows) {
    // write a row of output 
    std::cout << std::endl;
    ++r;
}
while (cin >> x) {
    ++count;
}


rows = NROWS;
r = 0;

while (r != rows) {
    // write a row of output 
    std::cout << std::endl;
    ++r;
}