Thursday 10 September 2009

AS3 101: Branching

Preview

After our gentle yet thorough introduction to the concept, we'll apply the ideas in a little shape drawing application, which can be seen below. Click on one of the little shape buttons, then click elsewhere on the stage to place that shape:

Step 1: What is Branching?

Branching is a generic term given to constructs in our code that allows us to make choices and do one thing, or another, depending upon a given condition. This is something called "flow control" because it can do different things depending on the conditions. There are numerous other terms for this aspect of programming, including conditions or conditional statements, logic, or even the colloquial if statement. The key thing to realize is that branching is basically the brains of the operation, the logic that gives your program some intelligence. Without branching, you won't be able to accomplish very much as a programmer.

Think of branching as those old Choose Your Own Adventure stories. If you don't remember these, they were "game books" where you started reading on page 1, but were soon presented with choice: If you decide to do X, turn to page 10. If you decide to do Y, turn to page 15. Reading the book straight through wouldn't make sense. You had to make your choices and jump around the book like a literary leapfrog. In any given reading of the book, you would only read a fraction of the pages. Every time you reached the end of a section, you'd branch off onto either one path or another. See Wikipedia's page on the Choose Your Own Adventure books for more information.

Branching in programming is very similar. You set up a condition and then either execute a bit of code or not, depending on whether that condition is met.

For example, imagine a form where you're collecting data from the user. Naturally, you're a good web citizen and want to provide some form validation to catch potential errors before submission. When the user clicks the "submit" button, you check to see if the form passes validation. If it does, you go ahead and submit the data. If it doesn't, then you display some error message.

At the point of clicking the submit button, one of two possibilities could occur, depending on conditions at that point in time. It's not unlike Schrodinger's Cat, if you're familiar with that. We could branch off in one direction, following the path where the data gets submitted, or we could branch off in another direction, where errors get displayed. It's entirely possibly to end up back at that branch point and take a different path (for example, you tried to submit invalid form data, got the errors, fixed them, then clicked the submit button again, this time successfully).

The curious may wish to do a little light reading on causality at Wikipedia.

Step 2: Your First Branch

OK, it's technically not the first, if you've been following along with the AS3 101 series. We've thrown them in before, but that's largely because it's next to impossibly to write a meaningful program (even simplified programs used to teach programming concepts) without some kind of logic in it.

We're going to write pretty much the simplest branch we can. We'll use an if statement.

  1. if (true) {
  2. trace("This happened.");
  3. }

Let's break it down. First, we have the if keyword. This is a reserved word in the ActionScript language that marks the start of an if block. Immediately following this is the condition. The condition goes in between parentheses. Leaving out the parentheses will cause an error; I bring that up because I still sometimes forget to put them in! We'll talk about conditions more in the next step, but for now we'll simply use true as our condition. After the condition is an opening curly brace. This marks the beginning of a block of ActionScript statements that will execute if (and only if) the condition is true. After the block of statement(s) is a closing brace, marking the end of the block.

You might like to read the above code in a more English-friendly way, like "if the condition is true, then execute the statements between the curly braces." Or, to be more specific in our example, "if true is true, then trace 'this happened.'" As a hypothetical example, one might read "if the user is logged in, then display the 'View my account' button."

So, if the condition ends up being true, then everything between the curly braces gets executed. On the other hand, if the condition ends up being false, then the entire block of code between the braces is skipped. Go ahead and run this movie. Since the word true is a true condition, then you'll see the trace in your Output panel. Close the movie and modify the code so that it looks like this:

  1. if (false) {
  2. trace("This happened.");
  3. }

Run it again; because false is not a true condition, you won't see anything happen. The block was skipped.

If you want to experiment here, go ahead. If you feel a little shaky on this information, now's a good time to answer those "what if I did this?" questions you might have. You might try putting more statements inside the if block, or putting statements around the block, to see what happens under which circumstance.

Step 3: Boolean Expressions

Remember that condition we used in step 2? It isn't much of a condition. As it stands, the whole if structure is just a formality, because we're using the Boolean literal true, which means the block always runs. Naturally, that first example is an over-simplified version. To make use of conditions, we need to become familiar with Boolean expressions.

You probably know that the Boolean datatype is something native to AcionScript. A variable that is a Boolean can either be true or false. It's worth noting that while we use the words true and false, these are actually keywords in the language. They are not Strings "true" and "false."

So, a Boolean is something that can have one of two values: true or false. Sometimes you might think of these as yes or no, or 0 or 1 (which gets down to the whole binary aspect of it...but that's not important right now). A Boolean expression, then, is an entire expression that evaluates to one of two values, true or false. Wikipedia has a short article on this subject, which can be augmented by the more in depth discussion on expressions in general.

To make the most of Boolean expressions, we need to learn a few Boolean operators...little symbols that work with values and evaluate them into Boolean values. All but one of them work on two values; the value on the right and the value on the left, comparing the two. Here's a short table of the operators you'll most often use:

OperatorNameIs true ifExample
==Equalityleft side equals right side in value42 == 42

This is probably the most used one. It compares the two values on either side and returns true if the two values are the same and false if they are not. This is called the equality operator and is different from the assignment operator that you met in the AS3 101 tutorial on variables. Mixing up those two operators is very easy to do and even after years of programming I still occasionally goof up on that one. Consider yourself warned (we'll talk about this again shortly).

As an example, 5 == 3 would be false. "Hello" == "Hello" would be true.

OperatorNameIs true ifExample
!=Inequalityleft side does not equal right side in value42 != 5

The above is the inequality operator and would probably take second for most used comparison operator. It does the exact opposite of the equality operator. If the two operands are the same value, it evaluates to false. If they are different, you get true.

For example, 5 != 3 would be true. "Hello" != "Hello" would be false.

OperatorNameIs true ifExample(s)
<Less thanleft side is numerically less than right side5 <>
<=Less than or equal toleft side is numerically less than, or equal to, right side5 <= 42 42 <= 42
>Greater thanleft side is numerically greater than right side42 > 5
>Greater than or equal toleft side is numerically greater than, or equal to, right side42 > 5
42 >= 42

The above compares the numerical values and work very much like what you probably already know from basic arithmetic. For example, the expression 5 > 3 is true because, in fact, 5 is greater than 3.

OperatorNameIs true ifExample
&&Logical ANDleft side is true and right side is true42 == 42 && 5 == 5
||Logical ORAt least one side is true5 == 5 || 42 == 5

If you've ever done Boolean math, then these will probably be familiar. They both look at the Boolean values on either side and return a Boolean value depending on the combination of values.

AND will only return true if both operands are true. If one of them is false, then the whole expression is false. It's like saying, "If the ice cream store is open AND they have cheesecake ice cream today, then I'll buy some." Both of those conditions must be true for the result to be positive. The store may be open, but they don't have cheesecake, so we won't buy any. Or maybe they had cheesecake in stock today, but they closed early, so obviously we can't buy anything. Both must be true for the final result to be true.

OR will return true if at least one of the operands is true. It will only return false if both are false. We can imagine a scenario where we might say, "If either the local pizza joint OR the corporate pizza chain are open, then I'll get some pizza for dinner." In this case, we're not as discriminating as with the ice cream place. We're up for any kind of pizza, really and as long as one of them is open, we're set. If both are closed, then we're out of luck. Of course, both might be open, but in that case we still end up with pizza.

Which brings up the concept of short-circuiting a Boolean expression. Notice that with both AND and OR, there are 4 possible combinations of values. Three of those values will the the same, one will be the other value.

ANDtruefalse
truetruefalse
falsefalsefalse
ORtruefalse
truetruetrue
falsetruefalse

The upshot of this is that sometimes ActionScript doesn't actually evaluate the entire expression. For example, take our pizza place analogy. Imagine we call the local pizza joint and find out they're closed, so we call the corporate pizza chain and luckily they're open, so we go get pizza. If, however, the local joint was open when we called them first, then there's no need to call the corporate chain, because we know that there is at least one place open and it's the one we'd prefer anyway.

Similarly, if we call the ice cream store and they're open, then we need to find out if they have cheesecake and only then will we go get ice cream. However, if the store is closed, then there really isn't much point of checking on the cheesecake, because we couldn't get it anyway.

In these cases, ActionScript evaluates "just enough" of the entire expression to determine the outcome. Why would you want to know this? For optimization reasons. If you have an OR expression and one of the operands is a simple Boolean variable value and the other is a complex statement involving lots of calculations and function calls, then you'd be a bit better off by sticking the simple variable first in the expression. If it's true, then there's no need to execute the complex statement. ActionScript knows this and will therefore entirely skip the execution of that statement. In a performance critical application, that's the kind of optimization you can do easily.

OperatorNameIs true ifExample
!Logical NOTreverses the right side!(42 == 5)

The exclamation point simply reverses the state of the Boolean expression to its right. For example:

  1. var thisIsTrue:Boolean = true;
  2. trace(!thisIsTrue);

Will result in false being sent to the Output panel.

Like so many things in programming, Boolean logic is actually rooted in mathematics. You may remember Venn diagrams, truth tables and logic gates from some math class long ago. This is the mathematical Boolean logic. Wikipedia once again provides further reading if you wish to take a tangent.

Step 4: Using a Boolean Expression in the Condition

Let's put all that theory we just learned to use. Let's change that if (true) to something a little more realistic:

  1. var state:String = "foo";
  2. if (state == "foo") {
  3. trace("The current state is 'foo'");
  4. }

OK, so it's not completely realistic, but now we have an comparison for equality happening in the condition. Run this and you should see the trace. Try changing either the value of the state variable or the String used in the comparison. For example:

  1. var state:String = "bar";
  2. if (state == "foo") {
  3. trace("The current state is 'foo'");
  4. }

So, what's happening? Well, pretty much the same thing as before, except we have a more interesting condition. We start by setting the value of a state variable (presumably to keep track of the current state of our application). Then we hit the if block and evaluate the expression inside the parentheses. If the whole expression evaluates to true, then we execute the stuff between the braces. The only real difference is that our expression is more complex.

But it's not bad; it's just the equality operator, used to compare the value of state against the literal String "foo". In the first example, that's exactly what state is, so the condition is true and we see the trace. In the second example, state is not "foo," so we don't see the trace.

Let's try a slightly more complex example:

  1. var state:String = "bar";
  2. var loggedIn:Boolean = true;
  3. if (state == "foo" && loggedIn) {
  4. trace("Welcome, logged in user! The current state is 'foo'");
  5. }

Again, we have our state variable. We also have another variable loggedIn, hypothetically tracking whether or not we've logged in. Our condition is actually checking for two things: first, whether the current state is "foo," and also if we're logged in. If we come short on either of those, then the whole thing is false (the AND operator requires both to be true) and nothing else happens.

You may be wondering how we knew to compare state to "foo" first and not check to see if "foo" AND loggedIn is true. There's something called operator precedence, which works a lot like the same thing in mathematics (they way I keep bringing that up, you'd think I'd have stock in mathematics to sell you). For example, in the mathematical expression 2 + 3 * 4, you multiply 3 by 4 first, then add 2, because multiplication takes precedence (happens first) over addition. All of those logical operators we discussed last step have precedence as well. I won't get into a formal discussion on it, but you can find information about ActionScript's operator precedence here and here and a rather geeky article on precedence in general on Wikipedia here.

But for now, we can get a lot of mileage knowing that the && and || have a lower precedence that all other operators listed in the previous step. So, we know that the equality comparison will evaluate first and the result of that evaluation will be used in the AND expression. If this is still confusing, let me put it this way: The following:

  1. if (state == "foo" && score > 100) {...

Will evaluate to exactly the same thing as:

  1. if (score > 100 && state == "foo") {...

Aside from the previously mentioned short circuiting that might happen, these statements are exactly the same.

It's useful to think of AND and OR as separators of sorts for multiple conditions that all need to be evaluated. They kind of work as commas in a list to separate elements.

Step 5: Getting Booleans Out of Non-Boolean Values

Something else to understand is how pretty much any value, whether or not it's a Boolean value, can be used in places where a Boolean is expected, such as in the condition of an if statement (but not assigned to variables datatyped as Boolean; that's not what I mean). For example, consider this:

  1. if (42) {
  2. trace("It was true.");
  3. }

Run that snippet and you'll see the trace. But 42 isn't a Boolean! How'd that happen? Well, when we add a numeric value to the condition, it's evaluated like this: if the number is 0, then treat the condition as false. Other wise, treat it as true. You can see the effects of this by swapping out 42 with 0.

Strings follow similar rules. Any String except for an empty String will be true; an empty String will be false.

  1. if ("a") {
  2. trace("'a' is true");
  3. }
  4. if ("") {
  5. trace("You'll never get here.");
  6. }

Similarly, any complex object, such as Arrays, Sprites and MovieClips, XML and URLRequests can evaluate to a Boolean value. Try this one out:

  1. var sprite:Sprite = new Sprite();
  2. if (sprite) {
  3. trace("We have a Sprite.");
  4. }

Run it and again you'll see the trace. However, try the following:

  1. var sprite:Sprite;
  2. if (sprite) {
  3. trace("We have a Sprite.");
  4. }

and you won't. The only difference here is that we never put a value into sprite. In the first example, sprite had a value and therefore the condition was treated as true. In the second example, we declared the variable but never put a value into it, so the value of the variable was undefined or null. When that gets put into the condition, it evaluates as false.

This can be useful to ensure that some object has been initialized before attempting to use it. For example, the following code would cause a runtime error (go ahead and try it to see what I mean):

  1. var sprite:Sprite;
  2. sprite.x = 100;

Whereas the following provides a safety net:

  1. var sprite:Sprite;
  2. if (sprite) {
  3. sprite.x = 100;
  4. }

One more tidbit then we'll move on. As mentioned earlier, a common mistake is to confuse the assignment operator (a single equals sign) with the equality operator (two equals signs). Our condition that looks like this:

  1. if (state == "foo") {...

Could easily accidentally be written:

  1. if (state = "foo") {...

It's a subtle difference and easy to make. But let's look at what happens when the assignment operator is used here. This isn't a Boolean expression per se, but as mentioned earlier in this step, when a Boolean is expected, whatever value is fed to it will be translated to a Boolean. The result of an assignment operation is the value being assigned. That is, the expression state = "foo" actually evaluates to "foo". So, according to what we learned earlier, a non-empty String will evaluate to true, so in this case the condition is met and we run the code. However, we aren't checking to see if state is "foo" – it could be any other value – we're always assigning it to the value "foo" and always running the code. This error does actually cause a non-fatal warning when compiling: "Warning: 1100: Assignment within conditional. Did you mean == instead of =?", but the SWF will still run and simply have faulty logic in it. If it weren't for the compiler warning, you'd probably end up spending several minutes, if not hours, trying to track down the bug.

Step 6: The If/Else

OK, let's move on! There's so much more to cover. The if statement by itself is extremely useful, but sometimes we'll want to not only execute some code if something is true, but also execute a different bit of code if that same something is not true. For example, back in step 1 I used the example of submitting a form and either displaying errors or sending the data, depending on whether the condition of the input being valid or not. In this case, we'd want to turn to the if/else statement. It looks like this:

  1. if (state == "foo") {
  2. trace("Current state: 'foo'");
  3. } else {
  4. trace("It is not 'foo'");
  5. }

Here, we introduce a new keyword, else, along with another set of curly braces. This will probably be somewhat intuitive at this point, but the gist is that if the condition is true, then we execute the first block (between the first set of braces), but if that condition is false, then we execute the second block. It's either/or, we will never run both blocks in any given execution of the entire block.

Step 7: The If/Else If

Sometimes there are more than two possibilities. For this, we turn to the else if. These go after the initial if, but before the closing else (if present). For example:

  1. if (state == "foo") {
  2. } else if (state == "bar") {
  3. } else {
  4. }
  1. if (state == "foo") {
  2. } else if (state == "bar") {
  3. } else if (state == "fud") {
  4. } else if (state == "splat") {
  5. } else {
  6. }

And keep in mind that the else is still optional. We might have several states, but only want to do some stuff if we're in one of two states. Otherwise, nothing needs to happen. So we can easily do this:

  1. if (state == "foo") {
  2. } else if (state == "bar") {
  3. }

One more detail worth noting: if we had a chain of else ifs, the ActionScript considers it a job well done as soon as it finds the first true condition. So if the second else if succeeds, then it doesn't matter if the fourth else if would also succeed under the current condition; once the second else if block's code executes, the entire block is done. For example:

  1. var state:String = "bar";
  2. if (state == "foo") {
  3. trace("Foo state.");
  4. } else if (state == "bar") {
  5. trace("You'll see this one for sure.");
  6. } else if (state == "bar") {
  7. trace("Pigs are flying.");
  8. }

Step 8: A Note on Braces

I cuddle my braces, if only because I like the way that sounds. That's a silly word meaning that I place my opening brace at the end of the same line as the thing needing the brace. You'll see two main variations, though:

  1. if (state == "foo")
  2. {
  3. trace
  4. }
  5. else
  6. {
  7. trace
  8. }

Which seems to be the Adobe-approved method, sometimes called ANSI style. If you're a cuddler, though, you may still find it easier to read long branch block if you drop the else if or else word down on the next line, like so:

  1. if (state == "foo") {
  2. trace
  3. }
  4. else {
  5. trace
  6. }

No one way is right, but people are usually be very passionate about their choice. All sides claim that their way is the easiest to read. But the important thing to know is that ActionScript ignores whitespace around the braces. You could do this:

  1. if (state == "foo") {trace("foo")}else{trace("bar");}

or this

  1. if (state == "foo")
  2. {
  3. trace("foo")
  4. }
  5. else {
  6. trace("bar");
  7. }

There are actually quite a few variations of styles and they all have names and rationales. Head over to Wikipedia for a thorough discussion on them.

And this discussion does lead into the no-brace variation, which I advocate in certain cases.

  1. if (state == "foo") trace("You're on foo");

If the whole block to be executed in response to a true condition is a single line, then you can omit the curly braces. While the previous snippet makes the most sense to me (it practically reads like an English sentence: "If state is equal to foo, trace you're on foo"), the whitespace involved is again inconsequential. You'll sometimes see this:

  1. if (state == "foo")
  2. trace("You're on foo.");

While perfectly valid and can make for nice compact branches, like

  1. if (state == "foo")
  2. trace("You're on foo.");
  3. else if (state == "bar")
  4. trace("You're on bar.");
  5. else if (state == "fud")
  6. trace("You're on fud.");
  7. else
  8. trace("What are you on?");

The problem comes when you invariably need to add a second line. ActionScript isn't smart enough to just consider the code between the if and the else if to be the block...it's either the code between the braces, or the first statement after the if, if there are no braces. At some point well after originally writing the above code, you will probably have to change it to:

  1. if (state == "foo") {
  2. trace("You're on foo.");
  3. trace("Which is a lovely place to be.");
  4. } ... etc.

which means adding braces. You will either not enjoy retrofitting your code with braces, or you will forget, producing some spectacular errors and then be forced to go and not enjoy retrofitting your code with braces, anyway.

One last note on this, then we'll move on: keep in mind that the no-brace rule is for the first statement after the if (or else), not the first line. If you tried to cram two statements into one line, like so:

  1. if (state == "foo") trace("You're on foo.");trace("Which is a lovely place to be.");

The second trace is considered a whole separate statement and will not be counted as part of the if clause. That snippet by itself will always trace "Which is a lovely place to be," regardless of the value of state.

Step 9: Switch Blocks

Kinda sounds like a 19th century rural society discipline implement. But they're actually an alternate way to write the if blocks involving what value a certain variable may hold. It looks like this:

  1. switch (state) {
  2. case "foo":
  3. trace("You're on foo.");
  4. break;
  5. case "bar":
  6. trace("You're on bar.");
  7. break;
  8. case "fud":
  9. trace("You're on fud.");
  10. break;
  11. default:
  12. trace("What are you on?");
  13. }

You will probably get the idea without the intricate breakdown coming up next and you'll probably see this as a much easier-to-read version of the same thing.

So, let's break it down. First, is the switch keyword. Much like the if keyword (or var or function), it kicks off the whole thing.

Then, also like the if block, there's a set of parentheses. However, instead of evaluating the contents of the parentheses into true or false, a switch simply evaluates the parentheses to its value. In this case, the state variable holds a String and so the switch will "remember" this value as it executes.

Then some curly braces. So far, nothing too worrisome.

Then we get to the case keyword. This is a special entity in ActionScript that is of use only within a switch block, kind of like how you can only use the else keyword if it's following an if block. Immediately after the case word we have a value (a literal String in this case) and the whole case is terminated by a colon. What happens here is that the value of the case ("foo" for the first one) is compared to the value in the switch (whatever state currently holds). If there is a match, then the code underneath the case statement executes. If not, then it skips that code and moves on to the next case.

The default will run if there was no match in any of the cases; it's sort of like an else block.

Step 10: Take a Break

Those break statements in the switch block are really important. Without them, code executing as a result of a matching case will continue to execute. For example, the following code is a variation on the last example, except without break statements. Setting the state variable to "foo" will give you unexpected results:

  1. var state:String = "foo";
  2. switch (state) {
  3. case "foo":
  4. trace("You're on foo.");
  5. case "bar":
  6. trace("You're on bar.");
  7. case "fud":
  8. trace("You're on fud.");
  9. default:
  10. trace("What are you on?");
  11. }

If you run that, you'll see you got all four traces. Why? Because without breaks in there, the code starts executing after the first match. It doesn't matter that the second case doesn't match; the switch statement basically says, "OK, I found match. I'll start executing all code that is not a case or default line, until I see a break.

You may be thinking that that's awfully inconvenient and in truth it can be. 9 times out of 10, you'll write switch statements with a break for every case. It equates to a series of if/else-if/else conditions.

However, this behavior can actually be quite useful once you grasp the flexibility it can lead to. Imagine you wanted to tell if a number was part of the Fibonacci sequence. There are certainly better ways to do this, but the following example illustrates a use of a case without a break:

  1. var value:Number = 5;
  2. switch (value) {
  3. case 1:
  4. case 2:
  5. case 3:
  6. case 5:
  7. case 8:
  8. case 13:
  9. case 21:
  10. case 34:
  11. trace("Part of the sequence.");
  12. break;
  13. default:
  14. trace("Just a regular number.");
  15. }

Yes, I know that this is limited to a few hard-coded values and that we could write a function that calculates the sequence in just a few lines, but this is just a simple example of when cases without breaks might be useful. The point here is that the equivalent if statement would look something like this:

  1. if (value == 1 || value == 2 || value == 3 || value == 5 || value == 8 || value = 13 || value == 21 || value == 34) {
  2. trace("Part of the sequence.");
  3. } else {
  4. trace("Just a regular number.");
  5. }

You can see that the switch version is easier to read and even easier to change.

  1. switch (state) {
  2. case "member":
  3. show
  4. case "logged in":
  5. showLogOutButton();
  6. case "logged out":
  7. showLogInButton();
  8. }

Step 11: Comparing Ifs and Switches

Which is better, an if statement or a switch statement? Well, really, in the end, it comes down to what you prefer. Sometimes, however, there are scenarios when one might be more convenient than the other.

Switch statements shine when all you're doing is checking what the current value of some variable is, especially if it's a situation where you do one thing for one value, another thing for another value and a third thing for a third value, etc.

If statements provide more flexibility. You can test for the value of one variable in the first if, then for the value of a completely different variable in the else if. Also, it's easier to string together a more complex condition, such as

  1. if (value > 10 && value <>

0 nhận xét:

Post a Comment

 

Blogroll

Site Info

Text

Tut - Designer Copyright © 2009 WoodMag is Designed by Ipietoon for Free Blogger Template