Conditional statements are one of the most powerful tools you have as a programmer. They allow you to make decisions about what script is executed based on some condition, usually the value of a variable. The first and most important conditional keyword is if .
The if keyword is followed by an expression in parentheses, which is in turn followed by a code block. If the expression in parentheses evaluates to true , the code block is executed. If it's not, the code block is skipped . Let's get right into some examples, shall we?
myNumber = 10; if(myNumber < 20){ trace("myNumber is less than 20"); }
In this code, a variable called myNumber is assigned a value of 10. Then we see our new conditional statement, which says that if the value of myNumber is less than 20, execute all script within the curly braces; if it's more than 20, skip the script in the curly braces. In our case, 10 is, in fact, less than 20, so the trace function will execute. If you change the value of myNumber to say 50, the trace will no longer execute.
Note | There is no semicolon associated with the conditional. The code block serves to terminate the conditional statement, so no semicolon is needed. This is one of the few times you will omit it. |
You can see the flow chart for the if conditional in Figure 2.15.
The else keyword can only be used immediately following the code block of an if conditional. It allows you to define a pair of code blocks such that if some statement is true , the first block executes. If the statement is not true , the second code block executes. In other words, one and only one of the code blocks will execute when the script is run. It's time for an example:
myNumber = 10; if(myNumber > 20){ trace("myNumber is greater than 20); } else{ trace("myNumber is less than or equal to 20); }
In this example, myNumber is assigned a value of 10, and the if conditional tests myNumber to be greater than 20. Because this is not true , the if block is skipped and the else block is run. If you change the value of myNumber to be, say, 50, the if block will execute and the else block will be skipped.
A flow chart for the if else conditional can be seen in Figure 2.16.
It is possible to string a series of if else statements together to form a sort of compound conditional. The else statement can be followed by another if statement that must appear before the else 's code block. This technique allows a new if statement to be executed in the event that the first if is false , but the new if will be skipped if the first if is true . An example should make this clear:
myNumber = 10; if(myNumber < 20){ trace("myNumber is less than 20"); } else if(myNumber < 50){ trace("myNumber is less than 50 but greater than or equal to 20"); }
By doing things this way, you get what is called a bi-conditional statement in the second half of that example. The second if statement will only be evaluated if the first if statement was false . You can see a flowchart of this type of conditional in Figure 2.17.
It is also possible to place an else conditional on the end of the else if conditional we just created. This allows you to give an else block that will execute only if both the first and second if statements evaluate as false . Consider the following example:
myNumber = 10; if(myNumber < 20){ trace("myNumber is less than 20"); } else if(myNumber < 50){ trace("myNumber is less than 50 but greater than or equal to 20"); } else{ trace("myNumber is greater than or equal to 50"); }
Take a look at Figure 2.18 to see a flowchart for this type of conditional structure.
Following each conditional is a set of curly braces that indicate a code block associated with that conditional. Inside one of these code blocks, you can put basically any script you want, including other conditionals. If you put an if statement inside a code block associated with another if statement, it's called nesting a conditional. As your programs become more complex, you'll find this an invaluable asset. Let's look at an example of a nested if conditional:
myNumber = 10; if(myNumber < 20){ trace("myNumber is less than 20"); if(myNumber < 10){ trace("myNumber is also less than 10"); } }
Figure 2.19 shows a flowchart for a nested conditional.
Tip | Did you notice the way I indent every line of script that occurs within a code block? Each time I open a new block, I indent everything in it. When blocks are nested, the indention continues and you end up with a staircase effect. This is extremely important to you as a programmer. Properly indented script will make your life much easier because you'll be more easily able to read and interpret the logic in your code. The Actions panel will help you indent properly. |
This last conditional type is called switch . It's basically an elegant way to string together a series of if and else if statements. The general form of switch is as follows :
switch ( expression ){ case caseClause1 : code block case caseClause2 : code block ... [default] }
As you can see, this form of conditional starts off with the switch keyword, followed by an expression in parentheses, followed by a block of code in curly braces. The code block is broken into several sections called case clauses . Each case clause begins with the keyword case , which is followed by an expression and then a colon . The main switch block concludes with an optional default case clause. Let's take a look at each piece individually.
The expression in parentheses that follows switch is the first important thing to look at. It's the piece that is unknown at author time. It's similar to the expression following an if or else if . The first thing that happens when Flash encounters a switch statement is the evaluation of this expression. It evaluates to some value that can be a boolean, a number, or even a string; it doesn't matter. After the evaluation of the expression, Flash moves into the set of case clauses.
These are just expressions, followed by a colon, followed by a block of code. During execution, the first case clause expression is evaluated and compared to the original expression in parentheses. If the two are equal ( assuming equivalence comparison [ == ]), the body of the case clause is executed. If the expressions are not equivalent, Flash skips the body and moves to the next case clause.
The expression in a case clause is followed by a colon. All statements following the colon are executed when the case clause expression matches the original expression. But notice that there are no curly braces around this code block. Instead, the code block begins with the colon, but it ends with a break statement.
When Flash sees a break statement in your code, it immediately leaves the current code block and begins executing script one scope above. For example, look at the following script:
if( true ){ trace("this line will print"); break; trace("this line will NOT print"); } trace("this line will print");
The if statement will execute because all we put in its expression was true . But inside its code block are two trace calls ”one before a break and one after. The one after will not print because the break sends the execution point of the program up one level of scope, which in this case causes it to break out of the if statement's code block.
In the same way, a break is used to leave the currently executing switch statement. Therefore, a set of case clauses often looks something like the following:
case expression1: code code break; case expression2: code code break;
As you can see, each expression is followed by the colon and then a code block. Each block has a break statement as its final line.
In nearly all switch statements, there is more than one case clause. That's because a single case clause would be the same as an if statement, in which case if is preferred. In a switch statement, however, one and only one case statement is executed. The first one to match the original expression gets executed, and no others. This is due to the break statement stopping execution of the entire switch statement. Consider the following script:
x = 10; switch( x ){ case 10: trace("case 1"); break; case 10: trace("case 2"); break; }
In this case, x is 10 and there are two cases, each using the literal value 10 as their expression. The output of this script would be "case 1" only. The second trace , "case 2," would never be printed.
However, the break statements that usually end a code block are not required. When a break is absent, any other case blocks that follow the current one are executed as well, regardless of the case expressions they contain. After one case has been successfully compared to the original expression, the rest of the code in the switch block is executed. Let's look at an example:
exp = "hello"; switch( exp ){ case "hello": trace("case 1"); case "hi": trace("case 2"); break; }
Caution | The blocks of code following each case statement are not actually code blocks, which is why they don't have curly braces. They do not represent a deeper level of scope. (Scope is described in Chapter 3 in a section titled "Scope.") This is also the reason that only one case will execute in a switch . When a break is encountered , it leaves the current scope, which is actually the entire switch statement. Therefore, break will leave the entire switch , omitting any other case statements in the block. |
In this version, both trace calls execute. That's because the first case has no break in its code block. The second case , although its expression does not match the original one, is executed because there has already been a true case, and there was no break statement in it.
This ability to leave off breaks is important because it allows us to stack code blocks in such a way that you can enter into the block at various points. Consider the following script:
switch( exp ){ case 1: //do task a case 2: //do task b case 3: //do task c }
Because there are no break statements in any of the cases, the evaluation of this switch works as follows. If exp evaluates to 1, tasks a, b, and c are all executed. If exp evaluates to 2, tasks b and c are executed. And if exp evaluates to 3, only task c is executed.
Creating this kind of logical structure with if and else if statements is crude. The switch statement provides a much more elegant solution to problems of this sort. The same solution, expressed without switch , might look like this:
if( x == 3 ){ //do task c } else if( x == 2 ){ //do task b //do task c } else if( x == 1 ){ //do task a //do task b //do task c }
As you can see, this solution is much less desirable, particularly if you're dealing with a few dozen tasks. There is another similar solution using nested if statements, but it is no less crude.
At the end of a switch block, you have the option of adding a default case. This is done with the keyword default , which is executed if no other case expressions have been executed. I think of it as meaning, "If none of our cases were equivalent to the original expression, execute this default code block." There is, of course, no expression following the default keyword. And like I said, the default case is optional.
Note | The default case in a switch statement is similar to an else statement at the end of a block of if and else if s. The default case catches anything the others didn't, just like else . |
As you've seen, the syntax for switch is somewhat different from the other conditionals. As such, it's a bit harder to learn and remember. But as you've already seen, it can be used to create some elegant solutions to certain kinds of problems. Let's take a look at another example where switch gives us just such a crafty solution.
Imagine that it's your job to write the code for a text adventure game. The user can type in commands like "move north " or "climb tree" to interact with the game. To make things easier on the user, you've decided that your user commands can be given in several forms. The user can type in the command to move north in many different ways including "move north" , "go north" , "north" , and "n" . The same will be true of all four directions: north, south, east, and west. Therefore, consider the following switch statement as a solution to this problem:
switch( user_command_string ){ case "move north": case "go north": case "north": case "n": trace("you have moved north"); break; case "move south": case "go south": case "south": case "s": trace("you have moved south"); break; default: trace("I'm sorry, I don't understand "+ user_command_string); }
Notice the way I'm stacking the case statements. If any of the "move north" commands are given, the trace about moving north will be executed. The same works for the set of "move south" commands. Solving this problem with if and else if statements would be ugly, to say the least.
Also notice the default case. After all, the commands are broken down into sets of cases; the default case handles anything left over. Because we would code the entire set of possible commands into this switch statement, the only thing left over to handle would be commands that are not in our list of possible commands ”or invalid commands. Our default case does this by printing a message to the user that contains the invalid command string. Pretty slick, eh?