1

I'm trying to understand the while loop more deeply. I understad the basics which is that a while repeats the statement as long as the test condition is true.

In the book Accelerated C++ the authors state the following:

//the number of blanks surrounds the greeting
const int pad = 1;

//total number of rows to write
const int rows = pad * 2 + 3;

//we have written r rows so far
int r = 0;
//setting r to 0 makes the invariant true

while (r != rows){
     //we can assume that the invariant is true here

     //writing a row of output makes the invariant false
     std::cout << std::endl;

     //incrementing r makes the invariant true again
     ++r;
}

//we can conclude that the invariant is true here

I don't get it, why does writing a row of output the invariant false, and then true again at the increment. Did the authors of the book made an mistake?

Edit - The variant is just an intellectual tool to understand a while loop easier. In this example the variant is "r = number of rows in output".

-----------------------what's an invariant---------------

What is an invariant?

Julian
  • 4,396
  • 5
  • 39
  • 51
  • 2
    What is the invariant? It seems like it could be "`r` lines have been written", which would make the excerpt entirely sound. The invariant should be stated explicitly in your book. The loop invariant is not the same as the loop condition (which is `r != rows`). – Magnus Hoff Oct 15 '13 at 09:59
  • Yes it should, but they didn't. They only said "Writing a row of output causes the incariant to become false, because r is no longer the number of rows we have written. However, incrementing r to account for the row that was written will make the invariant true again." – Julian Oct 15 '13 at 10:01
  • 1
    Then I suggest you read it as if the book had said "For this loop, we will assume the loop invariant '`r` lines have been written' and analyse the loop from this perspective" – Magnus Hoff Oct 15 '13 at 10:02
  • I gues the authors made an mistake. – Julian Oct 15 '13 at 10:15
  • The statement depends on what `rows` is, and you don't provide any code that initializes it. – reinierpost Oct 15 '13 at 10:35
  • @reinierpost i updated it, now var rows is declared. – Julian Oct 15 '13 at 10:38
  • Thanks! The invariant should be inserted as well (as a comment, I suppose). – reinierpost Oct 15 '13 at 10:45

3 Answers3

2

The author didn't make a mistake

He just might have forgotten to mention what the invariant is.

Invariant != Loop condition.

An invariant (in your case most probably "the program has printed r lines") is a condition considered when formally talking about correctness of code (it's usually only given as a comment somewhere); it's not the same as the loop condition (which would be "r != rows" in your case!

codeling
  • 11,056
  • 4
  • 42
  • 71
2

Essentially, you answered your own question in your comment:

They only said "Writing a row of output causes the invariant to become false, because r is no longer the number of rows we have written. However, incrementing r to account for the row that was written will make the invariant true again."

Summary:

  • the invariant is "r = number of rows written to output"
  • printing an empty line invalidates this invariant (since r is unchanged, but the number of rows written to the output has incremented by 1)
  • after incrementing r by one, the invariant is true again
Frank Schmitt
  • 30,195
  • 12
  • 73
  • 107
  • I still don't really understand why this code `std::cout << std::endl;` makes the invariant false. – Julian Oct 15 '13 at 12:07
  • 1
    The invariant is "r == number of rows in output". If you increase "number of rows in output" by printing a line, the invariant becomes false. It doesn't matter whether the line is empty or not - you're still increasing the number of lines in the output by one. – Frank Schmitt Oct 15 '13 at 13:19
0

What is the invariant the author is talking about? (I'm sure it is stated somewhere.) From the comments, I'd guess that it is something along the lines: r == number of rows output. So when you output a row (line), this becomes false until you update r to reflect the row you just output.

There are, of course, other invariants: that r != rows, for example. In this case, the output will not invalidate the invariant, but the ++r might. Of course, there's no code after the ++r in the loop, so it doesn't matter, and many people would write this loop:

for ( int r = 0; r != rows; ++ r ) {
    std::cout << std::endl;
}

so that this second invariant is never violated "within the loop".

James Kanze
  • 150,581
  • 18
  • 184
  • 329