Loops 3: getting loopy

This lecture is essentially all about some shortcuts that make it easier to write certain kinds of loops. As with our other shortcuts, these aren't technically needed, but they make our code much easier to write and understand, and that's pretty important!

Shortcut #1: for-loops

One of the most common looping situtations you'll come across is this: You've got a number n, and you need to go through a loop n times. Consider, for example, the very simple problem of writing a program that reads in double x and int n from the user and prints out x to the nth power. (Forget, for the moment, that cmath contains a function to do this.) Here's how we'd do it with while-loops:


power = 1.0;
i = 0;
while( i < n ) {
  power = power * x;
  i = i + 1;
}

If you think the test "i < n" looks strange, remember this: the variable i counts the number of times we've gone through the loop body. We start having gone through 0 times, and we want to stop when we've gone through n times. In other words, while we've looped fewer than n times, keep looping!

Any time we count our way through a loop like this, we'll have three basic pieces of code:

  1. the initialization of our loop counter before the loop begins
  2. the test-condition that determines whether we keep looping
  3. the update (i.e. increment) of our loop counter at the end of each loop iteration
This set-up is so common that C++ has a special syntax allowing us to express it more succinctly, the for loop. The previous code would be written as follows with a for loop:

power = 1.0;
i = 0;
while( i < n ) {
  power = power * x;
  i = i + 1;
}
BECOMES
 
power = 1.0;
for( i = 0; i < n; i = i + 1 ) {
  power = power * x;
}

The for-loop is just a compact way of writing a while loop which has the three steps outlined above: initialization, test-condition, and update. A for-loop can always be written as a while loop in the following way:

for( A; B; C ) {
  statement1
  statement2
        .
        .
  statementk
}
is the same as
{
  A;
  while( B ) {
    statement1
    statement2
          .
          .
    statementk
    C;
  }
}

As I said before, we usually use for-loops to loop n times, where n is some value we know from earlier on in the program. This for-statment will usually look like this:


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

Making sense of this brings up 2 more C++ shortcuts that we've ignored 'til now.

Shortcut #2: Initializing variables in the declaration

Up 'til now, we've usually made delcaring a variable and initializing that variable (i.e. giving the variable it's initial value in the program) separate steps. However, in C++ you may give a variable it's initial value at the same time you declare it by simply putting "= value" after the variable's name in the declaration. For example:

double x = 1.0;
 
... both declares the variable x and gives it an inital value of 1.0. When you declare several variables in one statement, you can initialize some and leave others, e.g.

string A, B = "doosey", C;
In the context of a for loop this is nice, because something like the counter i in

for( int i = 0; i < n; i++ ) {	// int i = 0; => i is declared and initialized with 0
  ...
}
really only gets introduced for the for-loop. Thus, it's nice to be able to declare it and initialize it all in one statment, so it fits in the first slot of the for-statement.

Shortcut #3: ++ and --

Incrementing (adding 1) and decrementing (subtracting 1) variables is something that goes on all the time in programming. Therefore, C++ has a shortcut: x++ sets x to x + 1, and x-- sets x to x - 1.
Sometimes you want to increment or decrement by values other than one. C/C++ has a nice way of expressing that. The += and -= operators, which are defined like this:
a += b is equivalent to a = a + b
a -= b is equivalent to a = a - b
In fact, for any binary operator we have the operator ℗= defined by
a ℗= b is equivalent to a = a ℗ b
So, for example, if you want to print out all the numbers from 0 to 100 in increments of 5, you might write

for( int i = 0; i <= 100; i += 5 )
  cout << i << endl;
The trick here is that while x++ is an expression (its value is the value of x before the increment), it also has a side effect, namely that it changes the value of the variable x. Thus we have:
code fragmentoutput

int k = 5;
cout << k++ << endl;   
cout << k << endl;
5
6
... which may be a little counter-intuitive at first.

For the moment, let's just say this: Don't use ++ and -- in expressions, just use them as standalone statements that provide a shortcut to something like x = x + 1. This is because you have to be careful about which value you get in the expression - is it the value before or after the increment. It's confusing to read such things, and let's just avoid the confusion. Remember, it's not a contest to see who can write a program with the fewest keystrokes! (Though admittedly, that's kind of fun.)

Note: The name C++ is a pun on the ++ operator: It's the C language, incremented!
Note: ++x and --x are also allowed. The difference is that the value of the expression is the value after rather than before the increment/decrement operation.

A Subtle Point of Scope

If you look back at the correspondence between for-loops and while-lops, you'll see that the corresponding while loop is encased in a block of it's own. The point is that any variable you declare in the first slot of the for-statement goes out of scope after you leave the for-loop. Thus, something like:

for( int i = 0; i < 12; i++ ) {
  cout << i << endl;
}
cout << i << endl;

shouldn't compile - after all, the i doesn't exist as far as that second cout is concerned. This is usually a nice feature. After all, you only introdced the new variable i to iterative through the numbers 0 up to 11. After you're done with that iteration, there's no point to having i around.

Here's a code that illustrates how that can be nice.

#include <iostream>
using namespace std;

int main() {
  for( int i = 0; i < 25; i++ )
    cout << '*';
  cout << endl << " H E L L O   W O R L D !" << endl;  

  for( int i = 0; i < 25; i++ )
    cout << '*';
  cout << endl;

  return 0;
}
See how we didn't need to use a different variable for the two for-loops? That's because their scope does not extend beyond the for-loop itself.

Problems

  1. Write n *'s - this is very easy!
  2. Integration of f(x) by simple end-point approximation - Simple end-point integration approximates the area under a curve between x = 1 and x = b by the sum of n evenly spaced rectangles whose hieghts are the function values at x-values given by the left endpoints of the bases. To remind your self of how this works I've drawn this picture.
  3. A simple example with file io - you'll want to test using the file in4.txt.