macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Go with the Flow

by Seth Roby
08/19/2003

In the first installment of C is for Cocoa, we moved from one line of code to two lines of code, pointing out that the second one would be executed directly after the first, like items being crossed off of a to-do list. Our program knows to do the things we tell it to do in the order that we tell it. In lesson 2, we learned how to tweak this simple list approach by making single lines of code call functions where many more lines of code might be found. Our program knows that some of the things we tell it require more than one step.

Although we didn't make note of it at the time, both of these demonstrate the same concept, which is the idea that we'll be tackling today, usually called the flow of control. In our first program, as each line of code was found in order, that line was where the code program "was", and it got executed before the program would move on to the next line. Once we added functions to the mix, the control could move "into" those, and again would take the code on one line at a time, making sure to execute every line of code before it was done.

Related Reading

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

But sometimes we don't want it to execute every line of code. Sometimes we want it to execute this line of code when the computer is connected to a network, and this line if it's not. Sometimes we want the computer to choose from a whole range of things to do, depending on one circumstance. Sometimes we want the program to do something a whole lot of times, like while the user is idle, or as many times as there were key presses.

In all of these cases, we want to change the flow of control in our program. We want the computer to do something that's not just a simple list of directions, each to be done once and checked off, never to be looked at again. We want our program to do what makes sense given its circumstances, and not bother with the rest. This is what we'll be looking at in this lesson.

Learning How to Say No

But before we do that, we need to learn how to say no. When you ask a simple question, you expect a simple answer. But as we learned in lesson 1, you can't ask the computer a question, and your code certainly won't ask you any. Your program might ask the user questions, but the programmer is left in the position of uttering only imperatives forever and ever. The solution to all of this is borrowed from an old Irish schoolteacher named George Boole, who invented a way to express "yes" and "no" called boolean logic. It is traditional in C to refer to these as TRUE and FALSE (note the case: you must use all caps or it won't work), but Cocoa traditionally uses YES and NO. In this lesson, we'll use the C tradition because they stand out more in prose.

Now you might think that the best way to do this would be to make a boolean type that handles TRUE/FALSE distinctions. And indeed in a perfect world that's exactly how this would be handled. In newer languages, that's what's done. In C, however, we cheat a little. Instead of actually making a type that handles this, we use a type we already have and pretend. So we take our familiar int and whenever it's a zero, we read that as FALSE; when it's not, we read that as TRUE. This might seem a little odd, but it actually leads to a number of simplifications later on, which we'll see in later lessons. But now let's head into this lesson and learn to control our flow.

If You Code It, It Will Run

The simplest type of flow control structure is the if block. It's so simple that you probably don't need much help dissecting the following code block:

int integerForSeedValue(int seedNumber) {
	int retval = 0;

	if (1) {
		printf("This is always true.\n");
		retval = seedNumber - 3;
	}
	
	return retval;
}

You should use this and later examples in your code to modify the integerForSeedValue function we wrote in the last lesson. Each of the following code blocks will work and do something slightly different. As we discuss each, run your program with the newest code.

I've added a declaration for the variable retval, which is the value that the function will return (this is a common name for such a variable). Also note that the variable is returned at the end of the function. The middle is where the new conditional is.

Here we've added if, and then something in parentheses, and then a block. The key to it all is that something in the middle, in parentheses. That is called our condition, and if it's TRUE, the code in the block gets executed, and the function returns seedNumber -3. If it's not, then the computer skips the block entirely, and the function returns the 0 that retval is set to in its declaration. Since our condition here is the constant 1, the block is always executed. Let's make the code a little more interesting:

int integerForSeedValue(int seedNumber) {
	int retval = 0;
	int cond = 17 < 23;

	if (cond) {
		printf("What do you know: 17 *is* less than 23!\n");
		retval = seedNumber - 3;
	}
	
	return retval;
}

We added a line of code that seems familiar to us: it's a variable declaration that creates a variable named cond. But how it does that is a little new. Instead of the familiar arithmetic operators, we have the comparison operator "<", the less-than operator, which you remember from grade school when you were learning which numbers were bigger than others. Like all operators, it operates on the things around it. If it was an addition sign, it would add the two things around it, but since it's the less-than operator, it compares them. If the thing on the left is less than the thing on the right, it evaluates to a boolean value for TRUE. Since 17 is always less than 23, the first line sets cond to TRUE. Then, when we get to our if condition, since cond is true, the block is executed. If you switched the operator to be the greater-than operator ">", cond would be FALSE and the if block would not be executed.

What would be executed in that case is the optional else block, which we can see in this code example:

int integerForSeedValue(int seedNumber) {
	int retval = 0;
	int cond = 17 > 23;	//note we're now using the greater-than operator

	if (cond) {
		printf("What do you know: 17 *is* greater than 23!\n");
		retval = seedNumber - 3;
	} else {
		printf("Well lookie here: 17 is *less than* 23!\n");
		retval = seedNumber * 3;
	}
	
	return retval;
}

The else block will be executed whenever the if block is not executed--and vice versa. But note that neither of these need to be a block at all. You can just put a single line of code after the if's condition or the else, and that line of code will be executed in lieu of a block:

int integerForSeedValue(int seedNumber) {
	int retval = 0;
	int cond = 17 > 23;
	
	//no printf's here; only one line statements
	if (cond) retval = seedNumber - 3;
	else retval = seedNumber * 3;
	
	return retval;
}

It's a good practice to always include the block, because leaving it out can lead to headaches later on if you want to add code to either side of the conditional and you forget to add the brackets then. You're usually better off making a habit of always putting the brackets in whether you need them right away or not.

But it is often the case that the logic you need is more complex than a simple this or that choice. It's often this, or that, or this other thing. In that case, we chain our if/else statements together to allow more and more decisions, like this:

int integerForSeedValue(int seedNumber) {
	int retval = 0;
	int cond = 17 > 23;

	if (cond) {
		printf("Amazing! 17 is *greater than* 23! My life has changed\n");
		retval = seedNumber - 3;
	} else if ((seedNumber < 5) && (seedNumber > -5)) {
		printf("That there seed number is real close to zero!\n");
		retval = seedNumber * 2;
	} else {
		printf("No special cases here; do what we normally do!\n");
		return = seedNumber * 3;
	}
	
	return retval;
}

The second if statement is only tested if the first fails, which it always will since 17 is never greater than 23. But here we also do two important new things. First, our condition includes the function's input. This, as you can imagine, is much more useful than defining variables that will always be TRUE or FALSE as we have done before. Secondly, we did some craziness with some ampersands that's totally new.

What we're doing here is making a compound condition. Now, instead of just checking one condition, we're checking two: we want to make sure seedNumber is less than 5 and greater than -5. That double-ampersand means boolean and. Why not a single ampersand? That's used for something else, which you won't need to care about for some time.

Go back over the examples of if/else blocks and try them in your program; each one works a little differently, so try each one if you haven't yet. In each example, watch what code does and does not get executed to see the flow of control. When you're done, we'll look at a different way to do the same type of things.

Pages: 1, 2

Next Pagearrow