MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 See this if you're having trouble printing code examples


Loop the Loop

by Seth Roby
09/09/2003

Our programs have come a long way. Would you believe that a few lessons ago we were examining the semicolon after a line; now our programs can determine what actions to take based on criteria that we've only begun to explore? We've made a lot of progress, marching straight from our humble beginnings to where we are now.

It's that straight part that we're going to rectify today. No longer will our programs be forced to merely plow through line after line of code, skipping comments and blocks in conditionals, never looking back. Now we'll teach our programs how to jump around a bit, to take their time and enjoy the code we've written, to evaluate those lines a few times over. In this lesson, we'll examine the loop.

Loops are one of the most important structures in programming. You use loops to build lists and to examine lists, to read input, and Cocoa actually uses a loop to process all of the things the user is doing. When you're programming in Cocoa, you are almost always running in a loop.

The basic structure of a loop is very simple and is reminiscent of the conditionals we were looking at in our last lesson. You have a token that identifies the type of loop you're using, a condition that tells the loop when to stop, and a block of code that get executed each time through the loop. These three attributes of a loop define all the behavior that goes into the structure, even though some of the loops treat the different parts a little differently and some of the loop types order the parts differently.

While you Were Looping

while loops are the simplest type of loop and the one you will probably use most often. When you start dealing with the Cocoa classes that represent lists and groups, you'll use while loops coupled with NSEnumerators to process all the elements of the list. Let's take a look at a while loop, which is wrapped up in a new function that we can add to our program.


void countTo(int loopsToPerform) {
    int loopsPerformed = 1;
    printf("Let's count up to my favorite number!\n");

    while(loopsPerformed <= loopsToPerform) {    //loop while we're counting
        printf("%d!\n", loopsPerformed);
        loopsPerformed++;    //keep counting
    }

    printf("That's it! My favorite number is %d!!!\n", loopsToPerform);
}

Let's that a look at the code before we graft this into our program. First note that the loop code we're looking at is in a function definition, which we recognize from lesson 2. The return type for the function is a new type, void. This type is special; it means that the function returns nothing of value, so the return should be ignored (If you try to define a variable's value to be the return value of a void function, you'll incur a warning ).

Learning Cocoa with Objective-C

Related Reading

Learning Cocoa with Objective-C
By James Duncan Davidson, Apple Computer, Inc.

Now that we understand the window dressing, let's look inside the panes. We start with a normal variable definition, and then a printf that announces our intent to the user. We've got a while loop, and then a printf that announces what we've done. So what does the while loop do, and how does it work?

It begins with a while token, then something in parentheses, and then a block of code that defines what the computer should execute while in the loop. The magic lies in this something in parentheses, which is the familiar condition. The code will continue to loop while this condition is TRUE. As long as the condition evaluates to something besides FALSE (a.k.a. zero), the computer will execute the loop.

The easiest way to think of this is that the computer looks at the whole while structure as a complex line of code. When it gets to the line, it evaluates the condition. If the condition is FALSE, the computer moves on to the next bit of code after the while loop. But if the condition is TRUE, it executes the code in the block. This execution is called an iteration, which is one pass through the loop. After the computer finishes the block, it checks the condition again, and if it's still TRUE, it does another iteration, over and over like this in a loop until the condition is FALSE. So very literally, the loop is executed while the condition is true.

The code we have in our block prints out a line of text, containing the number it has counted to. Then it increments the variable loopsPerformed. So each iteration through the loop, it prints what it's counted to and counts higher. This counting higher is called the step, because it defines where the loop should move next as it goes along its path from start to finish. Without a step, your loop would never complete itself. And each iteration through the loop, the condition checks that the number of loops we've performed is less than or equal to (that's the '<=') the total number of loops to perform. Our condition is TRUE as long as we're still counting, and false as soon as we've counted higher than that.

So let's pop our new function into our project at the end of our main.c file. Remember to put a function declaration at the top of the file, like this:

void countTo(int loopsToPerform);

Now, let's take out the line of code in our main function that used to say:

printf("My favorite number is %d!", favoriteNumber);

And replace it with a line that says:

countTo(favoriteNumber);

That should put our program into a state that can run, so let it loose. You'll see how quickly the computer can count.

Do it While You Can

There is one little thing that while does that can sometimes be annoying: it checks the condition every time through the loop, from first to last. This seems like it's just the way you want it, but sometimes you want to have some code that always works once, but then continues to work while the condition is true. For this case (which you won't use very often), C has the do...while loop, which looks like this:


void countTo(int loopsToPerform) {
    int loopsPerformed = 0;
    printf("What's my favorite number?\n");

    do {	//we want to always rule out one number
        printf("It sure isn't %d...\n", loopsPerformed);
        loopsPerformed++;
    } while (loopsToPerform >= loopsPerformed);

    printf("%d is my favorite number!\n", loopsToPerform);
}

We've changed things around a bit, but the basic structure here is similar to the one in our last code snippet. The important changes happen where the while loop turned into a do...while loop. Let's look at that in more detail.

We begin our new loop with a do token. This tells the computer that the immediately-following block is to be executed no matter what. After the block we find a while token, then a condition in the normal parentheses. Note that we've switched our condition around so that we're now checking with a greater-than or equal-to operator (the '>='), but since we also changed the order of the variables in the condition, it's the same test.

Since there really isn't anything special about this type of loop, we're going to explore a little more about loops and programming in general. Let's start by doing something we're not supposed to (which means you shouldn't run the following):


void countTo(int loopsToPerform) {
    int loopsPerformed = 0;
    printf("What's my favorite number?\n");

    do {
        printf("It sure isn't %d...\n", loopsPerformed);
        loopsPerformed++;
    } while (1);

    printf("%d is my favorite number!\n", loopsToPerform);
}

We've replaced our continue condition with the constant 1. This means our loop will continue until 1 evaluates to 0, which will never happen. This is an example of an infinite loop. You've told the computer to continue iterating through the loop forever, printing all the while.

You will note, as you program more and more, that the computer always does what you tell it to very literally because it has no idea how to take your instructions any way other than literally: it can't catch your mistakes and won't pick up the inflection in your voice. Programming (in imperative languages like C and it's derivatives, at least) is the process whereby you spell out exactly what you want the computer to do and in what order you want the computer to do that. You tell the computer how to do everything, from writing on the screen to writing to a file. We're using functions like printf, a C function, to avoid having to do some of that low-level work, by relying on the work others have done before us because we don't want to do what most programmers call "reinvent the wheel." If the work is done, use it, and don't do it again. The code you write contains loops; your development should not. Avoiding the trap that is reinventing the wheel is called "Code Reuse," and it is very common and a demonstration of good code design.

The power of Cocoa is that almost all of the basic tasks--and some of the more advanced ones--are already programmed and waiting for you, and you only have to tell the computer what to do when you're dealing with things specific to your program. Right now we can see a glimmer of that by noticing how much we've used printf, or how much every Mac application uses a common Cocoa feature, a little thing called a window.

But before we start dreaming of things to come, let's see how we can fix our little snafu we made:


void countTo(int loopsToPerform) {
    int loopsPerformed = 0;
    printf("What's my favorite number?\n");

    do {
        printf("It sure isn't %d...\n", loopsPerformed);
        loopsPerformed++;
        if (loopsPerformed == loopsToPerform) {
            break;
        }
    } while (1);

    printf("%d is my favorite number!\n", loopsToPerform);
}

We've added in a little conditional that checks to see if we've counted high enough, and if we have it will use the break statement we used in lesson 3 to break out of the switch, only now we're using it before to break out of the loop. Whenever the computer finds a break, it will stop iterating through the loop it's currently in. If it's in a loop inside another loop (a nested loop), it will break out of the deepest one. Here's another way to stop our broken code from becoming a real problem (again, you can run this):


void countTo(int loopsToPerform) {
    int loopsPerformed = 0;
    printf("What's my favorite number?\n");

    do {
        printf("It sure isn't %d...\n", loopsPerformed);
        loopsPerformed++;
        if (loopsPerformed < loopsToPerform) {
            //more counting to be done
            continue;
        }
        break;
    } while (1);

    printf("%d is my favorite number!\n", loopsToPerform);
}

Here, we added a break at the end of our loop that will pop us out, but we also added a conditional that will fire whenever we have more counting to do. In that conditional we use the continue command, which puts us into the next iteration of the loop without completing this iteration. So whenever we have more counting to do, we skip back to the top of the block, without hitting the break statement.

You're probably thinking that these are stupid ways to fix the problem, and we should just use the loop's condition like we started with, and you're right. Keep thinking of things like that and your code will be nice and readable.

For Every Loop There is a Condition

The final loop we'll look at is the for loop. When you want to deal with a range of numbers, for loops can be a great timesaver. And when you're dealing with integers, you'll usually want to use a for loop. Lets' take a look at the code we used to demonstrate the while loop:


void countTo(int loopsToPerform) {
    int loopsPerformed = 1;
    printf("Let's count up to my favorite number!\n");
	
    while(loopsPerformed <= loopsToPerform) {    //loop while we're counting
        printf("%d!\n", loopsPerformed);
        loopsPerformed++;    //keep counting
    }

    printf("That's it! My favorite number is %d!!!\n", loopsToPerform);
}

And the slightly simpler version of the same code using a for loop:


void countTo(int loopsToPerform) {
    int loopsPerformed;
    printf("Let's count up to my favorite number!\n");
	
    for(loopsPerformed = 1; loopsPerformed <= loopsToPerform; loopsPerformed++) {
        printf("%d!\n", loopsPerformed);
    }

    printf("That's it! My favorite number is %d!!!\n", loopsToPerform);
}

The basic idea of the for loop is to take code logically related to the loop and move it into places so that it is more visually related. The condition of a for loop thus takes on three different duties, which we will examine in order. Each duty has it's own section within the parentheses, which is separated from the others by a semicolon.

The first section is for initialization. This is evaluated when the program flow first gets to the for loop and never again (unless the program comes upon the loop again). Here, you can give a value to a variable that you'll use as a counter, usually using the normal assignment operator. If you don't want to initialize any values, just leave this section blank.

The middle section is the condition. This acts just like a while loop's condition: the loop will continue to be executed as long as this condition is true. Just like in a while loop, this is evaluated every time at the beginning of the iteration to see if the loop should be executed at all.

The last section is the step, just like the step we saw in the while and do...while loops. This section is evaluated every time the loop completes an iteration, before the condition is checked.

These three sections--initialization, condition, and step--make up the environment that the for loop operates in. Copy this code snippet and replace the countTo function you had, and run the program to see that even though the code changed, the functionality of the program didn't change at all from when we had the while loop.

Note, however, that this isn't always the case. There are things you can do with a while loop that you can't do with a for loop, like take multiple actions to step to the next iteration, or initialize multiple variables. In general, a for loop is a less extensible solution that solves the same problem as a while loop, but for now it's hard to see the differences because our knowledge of variable types limits us pretty closely to counting.

More Types That You Can Count On

We now understand the different types of loops and have a basic idea of what their differences are. In our next lesson we'll take a closer look at variables and introduce a few more types that will allow us to make a better distinction between for and while loops, as well as express a variety of new concepts that just can't be done with integers. Although we've touched on variables before, and used them in literally every line of code we've written so far (well, not the lines with just brackets on them), we really don't have much of an understanding of what these little bits of data are or how we can interact with them. We'll look at how we make them, where they live, and lots more in our next lesson.

Seth Roby graduated in May of 2003 with a double major in English and Computer Science, the Macintosh part of a three-person Macintosh, Linux, and Windows graduating triumvirate.


Return to the Mac DevCenter

Copyright © 2009 O'Reilly Media, Inc.