Chapter 5. Programming Structures

CONTENTS
  •  Statements, Expressions, and Operators
  •  Simple Objects in Statements
  •  Conditional and Loop Statements
  •  Applied Expression Writing
  •  Summary

I suspect that the overview of programming from the previous chapter has made you eager to start scripting. (I hope so anyway.) The last chapter briefly touched on practically every concept from ActionScript. In this chapter, we'll start by exploring the structural elements necessary to write any script. Just as a house is built from the ground up by first laying down the foundation and then the framework, Flash scripts require a design and a framework. We're about to explore the structural elements of ActionScript that hold your scripts together.

This chapter covers:

  • How to write expressions and statements.

  • How to use operators in expressions.

  • How to use conditions (such as if) and loop structures.

  • How to practice using this knowledge.

Statements, Expressions, and Operators

To quickly review, statements are complete "sentences of code" that usually do something. Expressions are more like "phrases" because they don't do anything by themselves, but rather are used within statements. Expressions also result in a value when they're evaluated. For example, if you were to evaluate the expression "slow as molasses," it would have an actual value (perhaps 1 inch per hour).

It works the same in Flash the expression price/2 results in a value. Finally, operators, as part of an expression, perform an operation (often math) on one or more operands. For example, the "plus" operator (+) performs the addition operation on two numbers (operands). The expression 2+2 results in a value (4). Finally, the statement quad=2+2 actually does something (namely, assigning the value of 4 to the variable quad). Now that we know the terms, we can explore each concept in detail.

Writing Expressions

The key to writing expressions is to always remember that you're only writing part of a larger statement. By themselves, expressions don't do anything; rather, expressions result in a value because they are evaluated. That is, expressions are evaluated and become their result. An expression from real life might be, "The shirt's price minus the discount." If you said, "My credit card's balance is now increased by the cost of the shirt (minus its discount rate)," it becomes a statement that does something. After you can write expressions (segments), you'll have no trouble writing statements.

Let's use the discounted shirt price for practice. Imagine that you previously assigned the variable price to the cost of the shirt. It doesn't matter what the price was but let's just say $25 (that is, price=25). Also, consider that the discount rate is 10 percent. Interestingly, I'll bet everyone who's ever gone shopping already knows the shirt will cost $22.50, which just goes to show that you can write expressions! You just have to take one step at a time. Say the variable containing the discount rate is called "discount" (or discount=0.1). The final expression looks like this:

price-(price*discount)

You can think of this as a mathematical formula. No matter what the values of price and discount are, the formula works. It always results in the discounted price.

Precedence

We'll get to statements later in this chapter (in the section, "Types of Statements"). For now, however, there's more to learn about expressions. Notice in the earlier expression about price, I placed parentheses around price*discount. In the version with no parentheses price-price*discount you might think that Flash will execute the first two elements (price-price) first the result of that portion would be zero. Then zero multiplied by discount would always equal zero. (What a sale "All Shirts $0.") So, putting parentheses around the expression price*discount tells Flash to execute this expression first. It turns out this wasn't necessary. The expression price-price*discount results in the same value as price-(price*discount). That's because the precedence for multiplication is greater than for subtraction; multiplication is executed first, and then subtraction.

Of course, if you want to force Flash to execute the subtraction operation first, you could rewrite the expression as (price-price)*discount. Personally, instead of memorizing the precedence for each operator (also called order of operation), any time there's a question as to how Flash will interpret my expression, I simply place parentheses to make the expression not only clear when reading, but crystal clear to Flash despite the fact the extra parentheses are unnecessary. Flash always executes the expressions in the most-nested parentheses first. Look up "Operator Precedence and Associativity" in Flash's ActionScript Reference (from the Help menu).

Interestingly, operators each have an associativity of either "right-to-left" or "left-to-right," which determines the order of execution when two of the same operators appear and therefore have the same precedence. For example, because addition has left-to-right associativity, 2+3+4 is the same as (2+3)+4. Although this example doesn't demonstrate a different result, remember that parentheses can override associativity (as in 2+(3+4)). Associativity is not usually a critical issue, but it's covered in Flash's ActionScript Reference along with all the operators and their precedence.

Balancing Parentheses

Parentheses in an expression must balance. That is, for every open parenthesis, you must have a closing parenthesis. This holds true for the entire statement but that doesn't mean you can't create errors within an expression. While you are in Expert Mode, you need to ensure that the parentheses balance. One way you can do that is to read your scripts (from left to right) and count up for every opening parenthesis and count down for every closing parenthesis. After reading the entire statement, your count should be at zero, proving everything balances. While you are in Normal Mode, any errors in balance will be highlighted in red, and Flash will provide limited information about the error in the parameters area (see Figure 5.1).

Figure 5.1. The Actions panel will draw your attention to missing parentheses while you are in Normal Mode.

graphics/05fig01.gif

It is important to understand that just because you balance your parentheses, there's no guarantee that your code will work as expected. For example, the expression (price-price)*discount balances just as well as price-(price*discount), but with entirely different results. So, balancing parentheses is just a technical requirement (like spell-checking a document) making your code logical or work for your purpose is still necessary. Here's a great tip that I personally guarantee will help you: Any time you type an opening parenthesis, immediately type a closing parenthesis and then backspace to complete the parenthetical portion of the expression. This way you're sure to balance all parentheses. Finally, everything just discussed about parentheses also applies to quotation marks (" or '), brackets ([ and ]), and curly braces ({ and } also called curly brackets).

Using Operators in Expressions

Instead of listing every operator here, we'll first look at how operators work, and then explore the ones that operate within expressions (operators categorized as arithmetic, comparison, and logical). Finally, after the upcoming section on statements, we can look at operators that perform assignments.

I've said several times that operators operate on one or more operands. To be technical, when an operator operates on a single operand, it is called unary (like "uni-cycle"). When operating on two operands, it's called a binary operator. Finally, one operator (?:) is considered ternary because it operates on three operands. (Because there's just one such operator, "ternary" may only come up on a quiz show for geeks.) What makes this important is that some operators can act as either a unary operator or a binary operator. One example is -, which is both a "unary minus" and a "subtract" operator. When used on a single operand (as with -direction or within a statement such as oppositeDirection = -direction), it simply results in an inverse (or minus version) of its operand. But as a binary operator (as in price-couponValue), the entire expression is converted into the result of subtracting the second operand from the first.

The fact is that operators operate differently depending on the context. Even though it's pretty easy to see and understand how operators act differently based on the number of operands because you can quickly see how many operands are present, some operators also perform differently based on the data type of their operands. That is, the same operator can perform a different operation on different data types. Recall from Chapter 4, "Basic Programming in Flash," that the value of a variable can be one of several data types. Let's just consider the Number and String data types (probably the most common and familiar data types). The + operator is either an addition operator or a concatenate (meaning to connect) operator, depending on its operands. If one or both operands are strings, + is a concatenate operator, as in:

first="Phillip";  last="Kerman";  wholeName=first+last;

The expression first+last results in "PhillipKerman".

If both the operands are numbers, the + operator performs the addition operation, as in:

previousScore=10  currentScore=2  totalScore=previousScore+currentScore;

The expression previousScore+currentScore results in 12.

This issue can become quite frustrating if you think a variable (say previousScore) contains a number, but it actually contains the string ("say 10"). The expression previousScore+2 will result in the string "102" because + acts as the concatenate operator when one or more of the operands are strings. This is likely to happen in Flash when you use a Dynamic (or Input) Text field to display the value of a variable. Even though the field might read 10,it's actually the string "10" because the data type of fields is string. By the way, in Chapter 8, "Functions," you'll learn how to treat a string like a number by using the Number() function. Also, later in this chapter, you'll see how certain operators will actually change the data type of their operands in the section, "Using Assignment Operators to Create Statements."

There's no need to get freaked out about operators. Just remember that operators often behave differently depending on their position in an expression and on their operands' data types. In practice, you'll usually select the correct operator without fail. Just learn to recognize the symptoms of such problems. For example, if the numbers you were expecting to grow end up getting longer (such as 10 turning into 101) or if your strings are appearing as NaN (meaning "not a number"), you're likely mixing data types or using the operators incorrectly.

Finally, even though an operator operates only on one or two operands, the operand could actually be an expression (really, the result of an expression). This might have been particularly obvious when we discussed parentheses earlier. The example price-(price*discount) has the minus operator operating on the result of an expression (the multiplication part in the parentheses). Figure 5.2 shows how an operand can actually be the result of an expression.

Figure 5.2. An operand can be the result of an expression.

graphics/05fig02.gif

Arithmetic Operators

Let's look at the operators used to perform simple arithmetic. These won't change their operands and (when used on number operands) will have expected results.

  • Add numbers (+) results in the sum of two number operands.

  • Multiply numbers (*) results in the product of two number operands.

  • Subtract numbers (-). As a binary operator (that is, with two operands), it results in the difference by subtracting the second number from the first. It can also be used as a unary operator (on one operand) by placing it before the operand (as in -myNum), in which case it will result in the inverse of the operand. If it's positive, the result is negative; if it's negative, the result is positive.

  • Divide numbers (/) results in the quotient of two numbers. That is, it divides the first number by the second.

  • Modulo (%) results in the remainder when you divide the first number by the second. For example, 20%7 results in 6 because after you divide 7 into 20 (two times), you're left with a remainder of 6.

In addition to these, there are two operators (++ and --) that also perform simple arithmetic. Because they both change their operands, I've decided to discuss these in the "Assignment Operators" section later in this chapter.

Flash enables you to perform many additional math operations through the Math object (discussed later this chapter), but don't discount how such simple operators can be used in expressions. When you look at the following examples, keep two things in mind: All the variables' values are assumed to have been previously set to numbers and these are just expressions so, by themselves, they don't do anything.

Average (mean):

sum/total

Half:

full/2

Average (median that is, the midpoint):

lowest+((highest-lowest)/2)

Price when discounted:

price-(price*discount)

Compounded interest:

principal+(principal*interestRate)

Seconds (with milliseconds known):

milliseconds/1000

These examples all use simple arithmetic operators on homemade variables. You can certainly combine built-in properties in expressions (for example, use _currentFrame+1 to express the frame number of the next frame). You'll see more of this in Chapter 7, "The Movie Clip Object."

Finally, I didn't provide any examples of the modulo (%) operator but it is one of the most powerful operators available. It seems so innocuous, the remainder. But consider how you determine whether a number is even it has to be evenly divisible by two. Or, when divided by two, the remainder is zero. Similarly, to determine whether something is evenly divisible by five, there just has to be no remainder when dividing by five. This is where the modulo operator can help. If you just use anyNumber%2 and find the expression results in 0, you know the number is even. Later in this chapter (in the "Applied Expression Writing" section), you'll see an example that uses the modulo operator to make a loop execute every other time (that is, when loopCounter%2 equals 0).

Comparison Operators

Comparison operators are used to write expressions that evaluate to either true or false. That's it. You might understand the need for such expressions if you remember that they'll usually reside within a larger statement. For example, by itself the word "true" doesn't mean anything. However, an entire statement that makes sense might be "If your age is greater than 21, you can purchase alcohol." The expression "is greater than 21" always evaluates as either true or false. To make the statement even more explicit, you could say "If the expression 'your age is greater than 21' is true, you can purchase alcohol." This example is a conditional statement. Such statements are covered in detail later this chapter in the section "Conditional and Loop Statements."

The comparison operators by themselves are pretty easy to understand. All these operators require two operands in the form first operand, operator, second operand (such as 12>4, where 12 is first operand, > is the operator, and 4 is second operand). Let's look at them all.

  • Greater than (>) results in true when the first number is greater than the second.

  • Less than (<) results in true when the first number is less than the second.

  • Greater than or equal to (>=) results in true when the first number is greater than or equal to the second.

  • Less than or equal to (<=) results in true when the first number is less than or equal to the second.

  • Not equal to (!=) results in true when the first number is not the same value as the second.

  • Equality(==) results in true when the value of the first and second numbers are equal.

  • Strict equality (===) results in true when both the value and data type of the first and second numbers are equal. (See Flash MX's Reference entry for a full description of how the different data types vary.)

You'll see the most practical examples of the comparison operators later in this chapter, but there are several interesting points to make now. Remember that the result of any expression you write with these operators is always either true or false. That is, they can result in nothing except true or false, and false is a perfectly fine possibility. An expression such as 12<25 is perfectly legitimate it just happens to evaluate to false.

It's interesting that true and false are the two variations of the Boolean data type. However, you can use them within expressions as though they were numbers. True is 1 and false is 0. For example, the expression score*(timesCheated<1) will automatically reduce the value of score (no matter what it is) to 0 if the timesCheated variable is greater than 0. That is, the portion timesCheated<1 evaluates to either true or false (1 or 0). If timesCheated is 0, that portion is true and score is multiplied by 1 and thus is unaffected. If timesCheated is not less than 1, that portion is false and multiplies score by 0 (bringing it down to 0). This is a form of a conditional statement but much simpler.

The regular equality operator is formed by two equals signs (==) and the strict equality operator with three equals signs (===). A single character (=) is a different operator entirely. The single equals sign performs an assignment (as you'll see later in this chapter). That is, the variable to the left of = is assigned the value of the expression on the right. It actually creates a complete statement (because it does something), rather than an expression as the == or === operators do. Not only does this mean that the variable on the left side changes, but if you intended to create an expression that resulted in true or false, you'd find it always results in true. That is, age=21 assigns 21 as the value of age, and this statement will be evaluated as true. On the other hand, age==21 will be either true or false (depending on what the age variable's value happens to be). In addition, age will not change value when you use ==. The first case said, "age now equals 21;" the second said, "Does age happen to equal 21?" You'll see more about assignments, but just don't forget this operator is a "double equal."

Finally, string manipulation is covered in much more detail in Chapter 9, "Manipulating Strings," but it's worth mentioning that the comparison operators work perfectly well on strings. To work intuitively, both operands must be strings. But the expression "a"<"b" evaluates as true because "a" is earlier in the alphabet. (Uppercase letters are considered less than lowercase, which might be counterintuitive.) The truth is, you'll see such amazing ways to manipulate strings in Chapter 11, "Arrays," that it's not worth discussing much here. Just don't expect all the comparison operators to act differently depending on the data type of their operands (the way that many of the arithmetic operators do).

Logical Operators

Logical operators are used to compare one or two Boolean values expressions that result in either true or false. (Operators "and," and "or," use two operands, whereas "not" uses one.) Commonly you will extend the comparison operators with logical operators to make compound expressions such as "age is greater than 12 and age is less than 20" ("and" being the logical operator in this case). You're actually comparing two expressions (not two single values), but the result of the entire expression must be either true or false. If you use these logical operators on non-Boolean values (such as numbers), any number except 0 will be considered true. If you use them on strings, each string will be true as well.

  • The "and" operator (&&) results in true if both operands are true.

  • The "or" operator (||) results in true if either (or both) operands are true.

  • The "not" operator (!) results in true when the operand (following !) is not true (that is, it's false).

Here are a few common examples:

True if age is a "teen":

(age>12) && (age<20)

True if either age is greater than 15 or "accompaniedByAdult" is true:

(age>15) ||(accompaniedByAdult==true)

True if age is anything except 21:

!(age==21)

True if age is not a "teen":

!((age>12) && (age<20))

True if age is not equal to 21:

age != 21

Even though these expressions should be easy to figure out, there are some interesting elements to note. I included additional parentheses to make these expressions clear. But because the logical operators have very low precedence, the expressions on each side will be evaluated first. That is, age>12&&age<20 works just as well as (age>12) && (age<20), although it might not be as easy to read. Also notice that both operands of the "and" and "or" operators must be a complete expression. For example, age>12 && <20 won't work. It sounds okay in speech (as in "age is greater than 12 and less than 20,"), but in ActionScript, you want to say "age is greater than 12 and age is less than 20."

Finally, there's one trick that is commonly used to abbreviate scripts but it might not be intuitive to you. The expression accompaniedByAdult==true is the same as accompaniedByAdult (by itself). So, in this example, I could have said age>15||accompaniedByAdult. If you don't say "==true," it's implied. Of course, you can still get messed up (in either case) if the value of accompaniedByAdult is a number or string but that's another issue (discussed earlier).

You'll get plenty of practice writing expressions. Remember that they'll always be contained within bigger statements. These concepts should begin to make more sense as you write statements. The best way to learn to write statements and expressions is to first write your objectives and then start to program in pseudo-code (as discussed in Chapter 3, "The Programmer's Approach"). You might notice that in many of my examples, I actually include the pseudo-code version as well.

Types of Statements

As I've mentioned countless times, statements do things. Often statements do one of two things: either assign values or compare values. A statement could assign a value to the variable score. Another statement might compare the user's score to a set of values to determine a grade. Realize that when comparing values, the end result of a statement could be that no action is taken. For example, a statement could compare the user's score to a minimum and then, if the score is not high enough, do nothing. Only when the score is high enough would this comparison (or "conditional") statement do something perhaps display a message. We'll look at such conditional statements (that is, the kind that compare values) in the "Conditional and Loop Statements" section later in this chapter. There's a lot of material to discuss related to assigning values.

Using Assignment Operators to Create Statements

The granddaddy of the assignment operators is the equals sign (=). When this operator appears in a statement, you can read = as " is assigned the value of ." That is, username="Phillip" can be translated and read aloud as "username is assigned the value of 'Phillip.'"The truth is that you don't need any assignment operator other than =. The others just make certain tasks easier. For example, the increment assignment operator (++) can be used to increment its operand, as in age++. That's the same as saying age=age+1. So, if you just understand how the plain old = assignment operator works, the others are variations on the same theme.

Here are the basic assignment operators:

  • Assignment (=) places the value of the expression on the right into the variable on the left.

  • Increment (++) increments the variable on the left by 1 (that is, it's increased by 1).

  • Decrement (--) decrements the variable on the left by 1 (that is, it's reduced by 1).

  • Addition and assignment (+=) increases the variable on the left by an amount equal to the expression on the right. (counter+=10 will increase counter by 10.)

  • Subtraction and assignment (-=) decreases the variable on the left by an amount equal to the expression on the right.

  • Multiplication and assignment (*=) multiplies the variable on the left by an amount equal to the expression on the right.

  • Divide and assignment (/=) divides the variable on the left by an amount equal to the expression on the right.

  • Modulo and assignment (%=) assigns the variable on the left a value equal to the remainder of dividing the value of the expression on the right into the value of the variable on the left. It sounds worse than it is; it just tries dividing the second number into the first and assigns the variable on the left what's leftover. For example, if your variable counter happens to equal 10, counter%=3 will assign counter the value 1 because 3 goes into 10 three times (3*3=9) with 1 leftover.

If these are starting to seem a bit complicated, remember that you only need =. All the other operations can be achieved with =. For example, counter+=10 will increase counter by 10 but so will counter=counter+10. It's not important that you memorize these now. Ultimately, the only thing that matters is getting your movie to do what you want. After you sort that out, you can reach for whichever operators you want.

Possibly the best thing about the all the assignment operators (except +=) is that they actually change the operand being assigned (usually on the left side) to a Number data type. Realize that these operators don't serve double duty (that is, they perform only mathematical assignments and don't work with strings). As such, they try to convert their operand into a number. For example, if you had a variable count that contained a string "12" and you wrote the script count++, the value of count would become 13 (the number, not the string). So, unlike some operators that operate differently based on the data type of the operands, these attempt to convert operands to numbers.

The two assignment operators = and += can work with strings as well. For example, if name="phillip" and then you executed name+="kerman", the current value for name would be "phillipkerman". So, here, the += operator performs like the concatenate version of +. (That is, when either operand is a string, + is "concatenate," not "add."). Obviously, the simple statement name="phillip" assigns the name variable the value of a string; therefore, = is not just for numbers.

Although the data type of operands won't make some of the assignment operators perform different operations, the operator's placement (before or after) the operand can make a difference. You can actually place the ++ or -- in front of or behind the operand, as in ++counter or counter++. The difference is subtle but important. Placing the operator after the operand (counter++, called post-increment) increments counter "returns" the value of the operand before the increment happened. When your entire statement is only counter++, this issue doesn't matter. However, when you say otherVariable=counter++, otherVariable will turn into the value of counter before it gets incremented. On the other hand, if you say otherVariable=++counter, otherVariable turns into the incremented (higher) value of counter. In either case, the variable counter increments (sooner or later). It's not that one variation is better than the other; it just depends on your intent.

Although the difference between pre- and post-decrement and increment might seem very subtle, there's actually a particular situation in which you might have to use the pre-decrement or pre-increment option. Specifically, when the operand is a value that you're not allowed to change, only pre-decrement or pre-increment makes sense. For example, the _totalFrames property of a Movie Clip cannot be changed with ActionScript. (You can "get" it, just not "set" it.) If you want to use a value that's one less than _totalFrames, you must use --_totalFrames. Although Flash "wants" to decrement _totalFrames, it can't, but at least the expression results in a number that's one less than _totalFrames. Realize that both pre and post options do two things: change the value of the operand and return the value (either before or after the change). Even if the operand is an unchangeable value (such as _totalFrames), the pre-increment or pre-decrement options will still return the value as if it could change. Consider these two statements:

oneMore=_totalFrames++;  oneMore=++_totalFrames;

In either case, _totalFrames can't change. But only the second statement will assign the oneMore variable a value that's one greater than _totalFrames.

Here are a few examples of typical statements that assign values. (Notice that I've thrown in plenty of expressions within the statements.)

  • Reduce price to half: price=price/2; (You could also use price/=2;)

  • Calculate a percent correct based on a number of questions: percentCorrect=(numberCorrect/totalQuestions)*100;

  • Apply a discount to price if age is greater than 64: price=price(discount*(age>64));

Notice that I ended each statement with a semicolon. Flash understands the end of your statement is reached when the semicolon is used. You can't just type a return at the end of the line because both blank spaces and returns are ignored when Flash reads your script. You could actually run all your code along one long (difficult-to-read) line if you separated distinct statements with semicolons.

The Thought Process

Writing statements takes the same skill as writing expressions. Similarly, the skill will come with practice. Most people write any such code in segments. That is, it's difficult to write a statement in the same way that you might compose a sentence in speech (in which you almost talk without thinking let alone thinking about the composition of your sentence). Rather, to write a script statement, first think of your general objective, write it out in pseudo-code, and then break down your pseudo-code into discrete elements that can be expressed in script. For example, it's unrealistic to simply think "double score when they get the bonus question" and then immediately type score*=(1+(bonusQuestion==true)). Even though this example is intentionally complex, you're unlikely to create such a statement without breaking down the task into smaller parts. Your thought process might follow this order:

"Okay, I want to double their score if they got the bonus question right. Well, what if they got it wrong? In that case, just leave score alone. What can I do to score that will leave it untouched? Either add 0 or multiply by 1. Since doubling involves multiplying by 2, I've got an idea: I'll either multiply by 2 or multiply by 1 (depending on the outcome of the bonus question). So far, I have the statement score*= in mind. That is, score is assigned the value of score multiplied by blank. I want "blank" to be either 1 or 2. That is, either 1+0 or 1+1. For the second number (+0 or +1), I can simply write an expression that evaluates as either false or true. The expression bonusQuestion==true will always evaluate as 0 (when bonusQuestion is not true) or 1 (when bonusQuestion is true). The part on the right ((1+0) or (1+1)) can be replaced with (1+(bonusQuestion==true)). So, the entire statement score*=(1+(bonusQuestion==true)) makes complete sense now."

Perhaps your brain works differently than mine, but this was just a sample of my thought process. By the way, the same exact task could be expressed with countless variations of statements this is just one solution. Generally, when you're starting out, your expressions will tend to be wordy that's fine. You'll get more practice writing expressions and statements at the end of this chapter.

Built-In Statements

Although I've probably said that "statements do things" enough times so that you'll never forget, what I failed to mention is that there are built-in statements that also do things. Consider that ActionScript and JavaScript are practically identical, but there are a few Flash-centric actions that only ActionScript contains, such as gotoAndPlay(). The ambiguous term actions is left over from older versions of Flash. Realize that everything listed in the Actions section of the toolbox list (some of which are shown in Figure 5.3) is either a Flash-centric feature (a command or method) or a true built-in statement. Think of them all as statements if you want. (By the way, another way in which ActionScript and JavaScript vary is in the events to which they respond Flash has on(release), and JavaScript has onClick, for instance.)

Figure 5.3. Flash actions are either Flash-specific features or built-in statements.

graphics/05fig03.gif

We don't need to step through each statement now. You'll pick them up as you write blocks of code. For example, you'll see both return and break in the upcoming section about conditional and loop statements. The point is that all built-in statements are listed under Actions and that some of them are unique to Flash.

Simple Objects in Statements

It's only fair to introduce you to objects now. Even though there are seven other chapters dedicated to the finer points of objects (Chapter 7, "The Movie Clip Object;" Chapter 9, "Manipulating Strings;" Chapter 10, "Keyboard Access and Modifying Onscreen Text;" Chapter 11, "Arrays;" Chapter 12, "Objects;" Chapter 13, "Homemade Objects;" and Chapter 14, "Extending ActionScript"), there are a couple of simple objects that will help you immensely as you write statements namely, the Math object and the Number object. Instead of providing detailed information about objects here, I'll simply show you how to use the Math and Number objects. They're so easy that you really can use them without fully understanding objects. When you get to the workshop chapters, you'll find the Math Object useful in almost every exercise. You'll use it in Workshop Chapter 3, "Creating a Horizontal Slider," (to help determine the percentages) and in Workshop Chapter 11, "Using Math to Create a Circular Slider," (to help determine the angles). In fact, you'll find the Math Object invaluable if you ever want to go beyond simple addition, subtraction, multiplication, and division.

Using the Math Object

The Math object will give you access to both common mathematical functions as well as a few constants (such as pi). The functions in the Math object (called methods) are almost like the buttons on a scientific calculator; actually, they're practically identical. For example, my calculator has a square root button (that looks like this: ). If I first type a 9 and then press the square root button, my calculator "returns" (into the display field) the square root of 9 that is, 3. Within an expression in Flash, you can also type a 9 (or a variable whose value happens to equal 9) and use the Math object's square root method to return the square root into the expression where you used it. The expression looks like this: Math.sqrt(9). (Remember that as an expression, this evaluates to 3, but it doesn't do anything unless you use it in a statement, such as answer=Math.sqrt(9);.)

The form for all the Math object methods is Math.methodName() where methodName is the function that you want to use. The parentheses are required for all methods because they often accept parameters. For example, you can't just say Math.sqrt(). Flash needs to know, "Square root of what?" You put the number (or expression that evaluates out as a number) into the parentheses for example, Math.sqrt(9); Math.sqrt(6+3); Math.sqrt(oneVar+((someExpression*2) +whatever)). Flash will take the result of the expression in the parentheses, calculate the square root, and finally put the answer in place of the entire expression. (Remember that it's an expression.)

In addition to methods that perform mathematical operations, the Math object has constants. You can tell them apart from the methods because they're listed in all uppercase letters, such as E (Euler's constant for use in natural logarithms) and PI (the ratio of a circle's circumference to its diameter used in trigonometry and geometry). They're used like methods except that because they don't accept parameters, the parentheses aren't used. That is, Math.PI (not Math.PI()) turns into 3.14159 and Math.E into 2.718 . You could probably live your entire life without ever really needing constants. For example, you could just hard-wire 3.14159 every time you needed to use pi in a formula (for example, if you wanted to calculate the circumference of a circle, PI times radius). It's just that the constants are built into ActionScript and they're very accurate so you might as well use them when you need them. Just remember the constants are all uppercase and don't need or accept parameters; therefore, they don't use parentheses.

Instead of going through all the Math object features (see Figure 5.4) effectively providing a recap of the last trigonometry course you took (and possibly awakening the long repressed anxieties associated) we'll just start using them in statements.

Figure 5.4. You'll find all the components of the Math object under Objects>Core in the Actions panel's toolbox.

graphics/05fig04.gif

Here are a few fun examples of the Math object:

  • Math.abs(number) Absolute value. Returns a non-negative version of number. For example, Math.abs(startPoint-endPoint) returns the distance between startPoint and endPoint, and it will always be a positive number even if the endPoint is a greater number (which would otherwise cause startPoint-endPoint to be a negative number).

  • Math.max(x,y) returns the value of either x or y (whichever is greater). For example, the statement bestScore=Math.max(writtenScrore, verbalScore); will assign the value of bestScore to equal the value of either writtenScore or verbalScore (whichever is greater).

  • Math.floor(number) returns the integer portion of a number. (That is, it rounds it down.) For example, given the number of minutes I've worked on this chapter, I can calculate how many full hours that is by using hours=Math.floor(minutesWorked/60);. I can combine this with the modulo operator (%) we looked at earlier to express a string that shows the total time in hours and minutes (instead of just minutes): Math.floor(minutesWorked/60) + " hour(s) and " + (minutesWorked%60) + " minute(s)".

  • Math.round(number) rounds off number to the closest integer. For example, Math.round(1.9) returns 2; Math.round(1.1) returns 1; and Math.round(1.5) returns 2. Note that computers have an inherent "rounding error" that crops up with many floating-point numbers (that is, decimal numbers). Consider the fact that 1/3 + 1/3 + 1/3 equals 1. When a computer tries to represent 1/3 in a decimal form, however, it can't just use 0.3333 (with threes repeating forever). Instead, it rounds off and eventually this type of thing will cause errors. Don't lose any sleep over it, but do be sure to check out Workshop Chapter 8, "Creating a Currency-Exchange Calculator," for an application of the issue.

  • Math.random()returns a random decimal number between 0 and 1 (but not including 0 or 1 that is, literally between 0 and 1, not inclusive). For example, if you want to return a number between 1 and 100 (inclusive), first set min=1 and max=100 and then use this expression: Math.floor(Math.random()*((max-min)+1))+min. This might not be easy to read, but the idea is that you multiply the random decimal number by 100 (that is, the difference between the max and min plus 1 that's the part that reads Math.random()*((max-min)+1)). Assume that Math.random() returns 0.56899. It will turn into 56.899 when multiplied by 100. You take that whole expression and strip off the excess decimals (using the floor method), resulting in 56. Finally, you add the min (at the end of the expression) because it's quite possible that the random number could be 0.00000000001, which turns into 0 even after you multiply by 100 and use the Floor method (and that's lower than our min). Also consider that if you multiply the random decimal by 100 (the max) you'll never quite get to 100 because the random number is always lower than 1. That last +min eliminates the possibility of going lower than the min (1) and makes sure that it's possible to reach the max (100).

graphics/icon01.gif

To return a random integer between min and max, use

Math.floor(Math.random()*((max-min)+1))+min

Just to make things fun, the Math object's trigonometry functions (Math.sin (angle), Math.cos(angle), and Math.tan(angle)) expect the angle provided to be expressed in radians (not in degrees with which you might be more familiar). Radians and degrees are simply different measurement units like miles and kilometers. A half-circle has 180 degrees but only pi radians, whereas a whole circle is 360 degrees, or 2 pi (see Figure 5.5.) Therefore, any time you need to convert degrees to radians, you must multiply by Math.PI/180. That is, 90 degrees is (Math.PI/180)*90 radians (or 1.57 .) (Just multiply your degrees by Math.PI/180 to get radians.)

Figure 5.5. A full circle contains 360 degrees or 2 pi radians two units of measure that can lead to confusion.

graphics/05fig05.gif

Whereas the trig functions accept angles (in radians, not degrees), the inverse functions (Math.asin(), Math.acos(), and Math.atan()) return angles (naturally, in radians as well). If you want to convert a value represented in radians to degrees, just divide by Math.PI/180. That is, 1.57 radians is 1.57/(Math.PI/180) degrees (or 90). Even though all this might seem like a sick joke from a sadistic mathematician, the truth is that you can usually do all your calculations in radians; you might never need to convert between radians to degrees. An exception would be if you want to display for the user a found angle in degrees, or you want to set the _rotation property of a clip to an angle you calculated. Generally, do all the calculations in radians and then (if you need to) convert the values to degrees at the last minute. If nothing else, just be aware of the difference.

graphics/icon01.gif

To calculate radians from a given value in degrees, use

radians=degrees*(Math.PI/180)

To calculate degrees from a given value in radians, use

degrees=radians/(Math.PI/180)

Flash provides one trig function that you might never have seen in math class: Math.atan2(). As you might know, the plain Math.atan() function (or "arc tangent") will help you determine the angle of a corner in a right triangle. Just provide the length of the triangle's opposite side divided by the length of the adjacent side, and Math.atan() will then return the angle. For example, if one side is 70 pixels and the other side is 200 pixels, the angle is 30 degrees (well, .528 radians, because Math.atan(70/120) returns .528). Figure 5.6 has a couple of examples.

Figure 5.6. The regular arc-tangent function (Math.atan()) enables you to calculate angles given two sides of a right triangle.

graphics/05fig06.gif

The plain Math.atan() function is fine and dandy when you're in the real world (where moving up increases values). But consider the coordinate system in Flash. The y values decrease when you go up! Consider the second example in Figure 5.6; both the x and y values are really negative numbers. In addition, you have to remember in that case to subtract the value you find from 180. It becomes even more of a hassle when you move into the other quadrants (from 180 degrees to 270 degrees and from 270 degrees to 360 degrees). Luckily, Math.atan2() resolves the entire mess! You just provide Math.atan2() with two parameters: one for the y value and one for the x value. That's it. You'll notice in Figure 5.7 that all the issues with positive and negative values are handled automatically by Math.atan2(). In Workshop Chapter 11, you'll learn how Math.atan2() can make calculating angles a snap.

Figure 5.7. When you consider Flash's coordinate system, Math.atan2() is a life-saver because it handles all issues with positive and negative numbers.

graphics/05fig07.gif

By now you see how the Math object's methods can be used within expressions. Basically, you're given a suite of mathematical functions through this object. It's pretty easy to use the Math object, but that doesn't mean all your expressions will be easy to write. It also doesn't mean all the other objects are as easy either. For now, just realize that the Math object provides you with many useful functions.

Using the Number Object

The Number object and Math object are similar in that you can use both of them in expressions without having an intimate understanding of objects. You simply say Number.methodName() or Number.CONSTANT_NAME. As you can see in Figure 5.8, the Number object consists mainly of constants (uppercase items). I suspect that you'll most often use the Number object for its constants only, so at this point we'll focus on them. (When we discuss objects in Chapter 11, the Number object's methods toString() and valueOf() will be covered.)

Figure 5.8. The Number object is primarily used for the constants (all listed in uppercase).

graphics/05fig08.gif

I've included this brief discussion now, however, because in addition to being a simple object there's another part of ActionScript with the same name. In addition to the Number object, there's a function called Number() that we touched on earlier this chapter. Even though we won't discuss functions fully until Chapter 8, using the Number() function is easy: Number(expressionOrVariable) attempts to convert the value of expressionOrVariable into a number and return the result. So, myNum=Number("100")+1; will convert the string "100" to a number, add 1 to it, and place 101 into myNum. Remember that the addition operator will act differently if one operand is a string (and in our case, turn myNum into "1001"). Because the word Number is used in both the case of a Number object and the Number function, it can be confusing. The weird part is that if you use the new constructor in front of Number(),you'll be creating a new instance of the Number object a different thing entirely, and something that you probably don't want to do. (At least wait until you learn all about objects in upcoming chapters.)

The Number object's constants are easy to use, albeit not particularly exciting. Here they are:

  • Number.MAX_VALUE is the "largest representable number," meaning a very high number, but one from which you can subtract. For example, you might initialize a variable to equal MAX_VALUE (as in bigNumber= Number.MAX_VALUE). Then you could subtract from your variable (for a very long time but not forever).

  • Number.MIN_VALUE is the smallest number (actually, a negative number).

  • Number.NEGATIVE_INFINITY is so far into the negative that you can't even add numbers to ever get out. You can use this constant in comparison expressions, but you can't perform operations on "infinity" the way you can with Number.MAX_VALUE and Number.MIN_VALUE.

  • Number.POSITIVE_INFINITY is like Number.NEGATIVE_INFINITY but positive. Because you can't perform calculations on variables that contain Number.POSITIVE_INFINITY, the most likely usage would be in a comparison expression (such as an if statement discussed later this chapter). For example, you could check whether a variable or expression is equal to Number.POSITIVE_INFINITY. For example, 1/0 is Number.POSITIVE_INFINITY.

  • Number.NaN means "Not a Number." You'll probably see "NaN" by accident more often then you'll need to select it by choice. When you try performing a math operation on a string, you'll get "NaN" for an answer meaning the operation failed. Number.NaN can be used in comparison expressions such as (aVariable==Number.NaN). Often, however, there are alternatives that might work better (for example, undefined, which all variables have for a value before you ever use them). Probably a better way to determine whether you've got NaN is to use the function isNaN(expression), which return false if expression is a number (or true if it's not a number). Because variables all equal undefined before they are assigned, you can use a conditional (for example, an if statement) to check whether a variable is undefined in which case you could take corrective action, such as assigning it a legitimate value.

We've looked at a lot so far in this chapter: expressions, statements, and the operators that hold them together. Also, the Math object was included because it gives you a different set of operators. There's more. Despite the fact that we've seen some perfectly complete statements, we haven't looked at conditional statements (that execute only when certain conditions exist) or loop structures (that repeatedly execute scripts). Loops and conditionals will round out your knowledge, and then you can start practicing everything at the end of the chapter.

Conditional and Loop Statements

Flash executes every line of script that it encounters. If a script is never encountered, it's never executed. And, if a script is encountered repeatedly, it is executed over and over. Using conditional statements, you can control what part of your script is executed or skipped. Obviously, if you place a script on a button and the user never clicks the button, the script won't execute. However, after a button is clicked, a conditional statement can control what portion of the contained script executes. This way, you can write scripts that behave differently depending on outside conditions.

Looping is a way to make a particular script execute repeatedly either a certain number of times or until a condition is met. This is helpful when you have a lot of code to process, but also when you're not sure how many times the code needs to execute. In the upcoming section on loops, you'll find that loops can save you a lot of typing. Compare the following two pseudo-code descriptions in which a loop would help:

The long way:

Attention Dasher; Attention Dancer; Attention Prancer; Attention  Vixen; Attention Comet; Attention Cupid; Attention Donder; Attention  Blitzen.

Using a loop:

All reindeer... Attention.

Although this might not explain the ActionScript syntax (it's just pseudo-code after all), the second choice (the loop) is obviously easier to use.

Conditional Statements: if, if else, if else if, switch

These four structural conditional statements are really variations on the same concept: If a condition is true, Flash should execute a block of code. At the core of all four variations is a kind of "if" and it's the easiest. Consider my wife instructing me, "If they have jellybeans, get me some." The condition is that "they have jellybeans," and the result if they do is that I'm supposed to "get some" for her. More technically, if the condition "they have jellybeans" is true, I execute the second part of my wife's instructions.

A plain if statement will execute the consequences only when the statement is encountered and the condition is true nothing more. Realize that unless my wife specifies what to do in the event that they're out of jellybeans, I don't need to do anything (of course, I know better). Had she said, "If they have jellybeans, get some; otherwise, get some chocolate," it's clear that I'll be purchasing one item or the other. If the condition is false (that is, if they don't have jellybeans), I am to automatically follow the second part (the "otherwise" or the else statement, if you will). In reality, I can't get chocolate unless they have some, but that's not at issue. In this case, when the initial condition is false, the second part (the else) is executed every time.

You can make the else statement conditional, too. That is, "If they have jellybeans, get some; otherwise, if chocolate is on sale, get some of that." Realize that there's a distinct possibility that I will come home empty-handed. A plain if statement can skip part of the code; an if else statement will do one or the other instructions; and an if else if statement could easily skip all the code. Let me show the three real-life statements in actual ActionScript using a few homemade variables (that are assumed to have previous values).

A plain if:

if (jellybeans>0) {     // buy some  }

A regular if else:

if (jellybeans>0) {     // buy some  } else {     // get chocolate  }

An if else if:

if (jellybeans>0) {     // buy some  } else if (chocolateOnSale == true) {     // get chocolate  }

Even though you can write an if statement with the Actions panel in Normal Mode, I'd recommend (even if just temporarily) going into Expert Mode to do so. I always start by typing the structure of the if statement no conditions, no results just a skeleton as follows:

if (condition){ }

This way I won't forget to satisfy any parentheses or curly braces I start. Then I go back through and replace condition with an expression that results in true or false (1 or 0). Then, finally, after the opening curly brace, I make a new line and type the script I want to be executed (when the condition is true).

If I decide I need to add an else catch-all statement when the condition is not true, I'll add else {} at the end. That is, the code

if (jellybeans>0){     //buy some  }

can have an else provision added, like this:

if (jellybeans>0){     //buy some  } else { }

Then I'll go back through and make a new line after the opening curly brace that follows the else statement. Adding an else if statement is not much different. Simply add else if (condition){} instead of else {}.

Finally, else if statements can be nested. That is, when one condition is not met (because it's false), another condition is not met, and you can keep adding else if ad infinitum. As such, nested else if statements can become unwieldy. Although it's not as eloquent (or efficient for the computer), I recommend avoiding deeply nested else if statements and instead using a series of independent if statements. For example, you could use all three if statement in sequence, as follows:

if (jellybeans>0){     //buy some  }  if (!(jellybeans>0)){     //buy chocolate  }  if (chocolateOnSale==true){     //buy chocolate  }

There's a logical problem with this code as each if statement is encountered (and potentially entered), meaning that it's possible that I come home with both jellybeans and chocolate. (Not necessarily a problem, but remember that this is supposed to serve as an alternative to a nested if else if statement.) If the various conditions were exclusive that is, when one is true, the others are necessarily false then there's no problem with this method. Notice that's the situation with the first two conditions. That is, there's no possible way that (jellybeans>0) and !(jellybeans>0) could both be true they're mutually exclusive. However, the first and third conditions could both be true, and thus both conditional scripts would be executed. In the case of nested if statements, if one condition is met, the others aren't even considered they're simply skipped.

Even if multiple if statements aren't all exclusive (which is the problem here), you can still use this more readable format with a little extra work. The return statement will cause Flash to bypass the rest of the script in a block of code. Realize that the code could be enclosed within a larger block of code perhaps within an on (press){ } mouse event of a button. As soon as Flash encounters return, it will skip the rest of the code. So, if you type return right before the closing curly brace in each if statement, you can be assured that once an if statement's condition is met, the rest will be ignored.

My suggestion for a series of if statements is beginning to look like a real pain, considering that even after one condition is met, you want the rest of the if statements to be skipped but you might have more code (at the end) that you want to execute; therefore, you can't simply use return within each if statement. Just to carry this through, one solution would be to set a "flag" variable. That is, above all the if statements, you can type notDone=1;. Then each condition can be expanded from if(mainCondition){} to read if((mainCondition)&&notDone)){}. This script says that if both the original condition (such as jellybeans>0) and the condition notDone==1, then execute the code. Finally, instead of using return within (but at the end of each) if statement, use notDone=0;, which will effectively cause Flash to skip the rest of the conditions. The result would look like this:

 1 notDone=1;   2 if (jellybeans>0){  3     //buy some   4     notDone=false;   5 }   6 if (!(jellybeans>0)&&notDone){  7     //buy chocolate   8     notDone=false;   9 }  10 if (chocolate=="on sale"&&notDone){ 11     //buy chocolate  12     notDone=false;  13 }  14 //the rest of the code

(Recall that the expression including notDone on lines 6 and 10 is the same as notDone==true.) I'll be the first to admit that this solution is not eloquent, nor is it particularly efficient for the computer. I would argue, however, that compared to a deeply nested if else if statement, it's probably easier to read. The sacrifice of making slightly less concise code is worth it if it means the code is more readable. Feel free to use else if when you're able to keep track of everything. Keep in mind that this discussion hasn't been wasted because we got to talk about the return statement as well as a little bit about solving a problem.

graphics/icon02.gif

There's another statement (called switch) that can be used like the variations of the if statement. The switch statement enables you to avoid deeply nested if else if statements but only when you're trying to match one of many specific results of a single expression. Consider that my wife gave me these instructions: "If jellybeans cost $1 per pound, get 2 pounds plus a chocolate bar; if jellybeans cost $2 per pound, just get one pound of jellybeans; finally, if jellybeans cost $3 per pound, only get one-half pound." Although you could express this by using a nested if else if statement, notice that all the conditions are based on the same expression: "The cost of one pound of jellybeans."

The switch statement uses the following form:

switch (expression){     case value1:          //do this          break; //to skip the rest      case value2:          //do that          break; //to skip the rest  }

Where expression is a single expression that could have a variety of different values. For each possible value you expect, replace value1 or value2. The great thing about switch is that you can account for as many different unique values as you want. Consider the following finished version from my practical example of jellybeans:

switch (pricePerPound){     case 1:          //buy 4 lbs. and buy a chocolate bar          break;      case 2:          //buy one pound          break;      case 3:          //buy one-half pound          break;  }

Notice that cases 1, 2, and 3 don't indicate a sequence but rather specific values for the pricePerPound variable. Also, break in the last case is unnecessary. (You'll learn more about break later this chapter.) Finally, this analogy might not be the most appropriate for the switch statement because it's possible the cost is $2.95 per pound (although I suppose you could include Math.round() in one of the cases). Consider the following example for something a little more useful:

switch (username){     case "admin":          //do admin stuff          break;      case "customer":          //do customer stuff          break;      case "supplier":          //do supplier stuff          break;  }

Ignore, for the time being, the fact that the value for username could be "ADMIN" (all uppercase) and this example would fail to recognize that value. (In Chapter 10, you'll see how to control strings to handle that kind of thing.) However, do notice that if username is not one of the three explicit cases shown, nothing happens. That could be what you want, but there's a feature of switch that enables you to include a "catch-all" at the end. It looks just like any case, but it reads case default: (no quotes around default). Here's a modified version of the preceding example:

switch (username){     case "admin":          //do admin stuff          break;      case "customer":          //do customer stuff          break;      case "supplier":          //do supplier stuff          break;      case default:          //do catch all stuff  }

You'll get more practice at the end of the chapter in the "Applied Expression Writing" section. For now, however, I recommend that you study the skeleton form of each version of the if statement as well as of the switch statement.

Loop Statements: for, for in

Any loop will execute the same script as many times as you specify, or until a condition you specify is met. Often you don't know exactly how many times you need the code to execute, but you can still refer to the number of loops by reference, such as, "Keep rinsing the spinach until there's no more dirt in the water" and, "For every envelope in that stack, please address, seal, and stamp each one." If there are 100 envelopes, you'll repeat the process 100 times but you don't have to explicitly specify 100 iterations. You'll see how to specify the number of iterations this way. In addition, realize that you won't always execute the same exact code repeatedly. For example, when you address, seal, and stamp several envelopes, you're following the same instructions (the same "script," if you will), but with slight variations. You don't put the same address on each envelope, but you do put some address on each envelope. When structuring the loop you're writing, consider two issues: How many times should the script execute and how do I write the script (that executes) so that it behaves differently for each iteration?

If you only learn one loop statement, it should be the for loop. Here's the skeleton:

for (init; condition; next) {     //do statement(s) here  }

Let's use the following example for our discussion:

for (n=1; n<11; n++) {     trace("Current iteration is "+n);  }

You can read the skeleton form of this for loop as: Starting with init, repeat the following while condition is true, and on each iteration, do next. Therefore, the example says, "Starting with n=1, keep doing the trace action while n<11 , each time incrementing n." The trace action is executed 10 consecutive times, but the string that is formed looks slightly different on each loop. In the Output window, you'll see "Current Iteration is 1," and then "Current iteration is 2," and so on. Imagine walking through this loop the init says "n starts at 1," so the first time the trace action is encountered, n is 1. Look at the next expression to see what will happen the next time through, and you'll see n++ (meaning that n is incremented by 1) and n becomes 2. The trace action is executed again. It keeps doing this while the condition n<11 is true. When n reaches 10, the condition is still true. The next time through, n is 11, so the condition is false and Flash jumps past the closing curly brace to continue any scripts that follow. (Go ahead and type this for loop in the first keyframe of a new Movie Clip instance, and then select Control, Test Movie.)

One tip (just like writing if statements) is that you should start by typing the skeleton and then replace init; condition; next. Another tip is to remember that the condition is the condition that keeps them in the loop not the condition to get out of the loop. That is, repeat while the condition is true, not repeat until the condition is true. In the earlier example, we wanted to repeat until n reached 11, but the following (updated) script has a problem:

for (n=1; n==11; n++) {     trace("Current iteration is "+n);  }

The problem is that n starts at 1, but the condition is looking for 11, so it doesn't have a chance to ever get to 11 and the entire loop is skipped.

Probably the biggest warning I can give you is to make sure that your loops eventually finish. It's easy to accidentally produce a script that, in theory, loops forever. While testing a movie, if the Flash Player ever remains stuck in a loop for more than 15 seconds, it displays a warning to the user (see Figure 5.9).

Figure 5.9. This dialog box appears when the Flash Player remains stuck in a script for more than 15 seconds.

graphics/05fig09.gif

Because each iteration of a script takes a tiny fraction of a second, 15 seconds is a very long time! Generally, being stuck in a script is likely a symptom of an infinite loop (also called an indefinite loop). Here's a perfect example of just such a loop:

for (n=1; n>0; n++) {     //anything because this is an infinite loop!  }

The condition (n>0) is true from the beginning and remains so forever! If the "next" expression decremented n instead of incrementing it, this would not be a problem.

Another way to inadvertently create an infinite loop is to forget how = differs from ==. Only init and next are assignments (and, therefore, they're statements), but "condition" is just an expression. Therefore, the following example probably doesn't do what the programmer intended (unless she was trying to make an infinite loop):

for (n=1; n=11; n++) {     //Help, I'm stuck in an infinite loop  }

The problem is that "condition" is n=11 (an assignment). The entire n=11 statement, if evaluated, is 1 (or true). This code loops forever because the condition (n=11) is true and remains so forever. (This mistake messes me up all the time!)

As I mentioned, learning the plain for loop is all you really need. However, the variation for in is also useful. Unlike the if, if else, and if else if statements, the for in loop is not an extension of the plain for loop. The two are entirely different statements. To fully utilize for in loop, it's best to understand arrays and objects two subjects covered in later chapters. The idea is that the loop will continue to loop "for all the items in the object." Here's an example: "For all the envelopes in that box, do whatever." I'll just show you the skeleton form and one example without fully explaining arrays or objects.

Skeleton form:

for(iterant in object){     //do statement(s) here  }

Example:

allNames=["Dasher", "Dancer", "Prancer", "Vixen", "Comet", "Cupid", "Donder", "Blitzen"]  for(n in allNames){     trace("On "+allNames[n]);  }

The skeleton form has two elements for you to replace: iterant and object. Give iterant any name you want and it becomes a variable that automatically increments from the highest down to the lowest. If there are eight things in your object (as in my example), it starts at 7, returns the value of the 7th item in the array (in my example: Blitzen), and then decreases in every loop until it reaches 0. (Because arrays start counting from 0, the range is 7 down to 0 rather than 8 to 1, as you might expect.) object is the name of your object or array (in this case, the variable allNames, which happens to contain an array of eight items). You don't have to understand arrays fully to imagine how this example works. For now, notice that one way to populate a variable with an array is shown in the first line of the example (use brackets and separate individual values with commas). If you ever need to grab individual elements from an array, you can use the form: arrayName[index], where index evaluates as 0 through the total number of items in the array (minus 1, because arrays count 0,1,2,3, and so on).

Unlike the plain for loop, the for in loop doesn't require that you specify what is supposed to happen on every loop (that is, the "next"). That's because the variable name you use for the iterant automatically goes through all the items in the object (starting at the highest and incrementing down). The two advantages of the for in loop are that you don't have to define the "next" and you don't have to specify a condition to control the number of times the loop repeats. Here's an example of using a plain for loop to achieve nearly the same result as the for in example I showed. Notice that it's not quite as clean, but it works basically the same way.

allNames=["Dasher", "Dancer", "Prancer", "Vixen", "Comet", "Cupid", "Donder", "Blitzen"]  for(n=0; n<8; n++){     trace("On "+allNames[n]);  }

(By the way, instead of hard-wiring 8 in the condition, I could have used allNames.length, which returns the length of the array but that's something you'll learn in Chapter 11.) Notice that this example is equivalent to, but not the same as, the for in loop. The for in version effectively goes in reverse order (starting with "Blitzen" rather than "Dasher," as is the case here).

Let's look at the statements that execute every time the loop repeats. You can put any statement (or statements) within the loop. You don't have to use the iterant at all. If you want the statement that executes to vary every time the loop repeats, however, the trick is to use the iterant within the statement. All my examples use a plain trace command. However, the string that appears is different in each iteration because the iterant variable is used either explicitly, as in trace ("Current iteration is "+n); or within an expression, as in trace("On "+allNames[n]);. (This won't reveal the value of n; rather, it uses n to extract an element in the allNames variable.)

Also realize that you can include as many statements as you want within a loop. You could even make your own iterant or counter that you maintain. You can nest other statements, such as if statements. See whether you can figure out the result of the following nested statement:

for(n=1; n<11; n++){     if(n%2==0){         trace(n+" is even");      } else {         trace(n+" is odd");      }  }

The variable n starts at 1 and iterates through 10. If the expression n%2 happens to equal 0 (that is, zero is the remainder when dividing n by 2), the trace command displays "n is even" (but n is replaced by the value of n). Otherwise, the trace command "n is odd" appears. By the way, I wrote out this example by first typing the skeleton of the for loop, and then inserting a skeleton of the if else statement. Finally, I came back through and filled in the data. (And naturally, I had to test it a few times to weed out the bugs; one of the semicolons in the for statement was a comma, and my if condition was mistyped as an assignment: n%2=0.)

while

If nothing else, while loops are the easiest to accidentally turn into infinite loops. Basically, you say, "Repeat while this condition is true." If the condition never becomes false, you'll repeat forever. You have to make sure that the condition eventually turns out to be false. The while loop is not suitable for repeating statements while you wait for the user to do something (or to stop doing something). For example, "While the user has the mouse pressed" is a perfectly legitimate concept, but you have to use a different solution for that objective while loops are just not made for that. A while loop gives you a way to write a script that will repeatedly execute a statement, but because you might not know exactly how many times it is supposed to execute, you can use a while loop while a condition is true. Remember, both for and for in statements require that you specify the number of loops even if that specification is in reference to a variable, length of an array, or number of items in an object. If, for example, the task is that you keep looping while a variable called found is 0 (or while(found==0)), the while loop is perfect. (Just make sure that found will eventually become something other than 0, or you'll be stuck in the loop forever.)

Here's the form:

while (condition) {     //do this statement  }

The following example shows that you must take precautions to ensure that the condition eventually becomes false (so that you can get out of the loop):

allNames=["Dasher", "Dancer", "Prancer", "Vixen", "Comet", "Cupid", "Donder", "Blitzen"]  found=0;  n=0;  while (found==0 && n<allNames.length){     if (allNames[n]=="Rudolf"){         found=1;      }      n++;  }  if (found){     trace("Rudolf found in spot " + n);  }

The form is simple: Statements will repeatedly execute while the condition is true. The preceding example is a bit complex, but instead of contriving an unrealistically simple example, it accurately shows the types of provisions necessary when you use the while loop.

Basically, I want the loop to repeat until I find the string "Rudolf" so the initial condition is while found==0 (or false). Before the loop, both found and n are initialized. Because my basic condition is found==0, I want to make sure that it's 0 from the start. The variable n serves as an iterant. (You'll almost always need your own iterant in while loops.) This iterant must be initialized before the loop, so that I can increment n at the end of each statement in the loop. The if statement just checks to see whether the nth index of allNames contains the string "Rudolf." If so, found is set to 1 and that gets us out of the loop. Notice that even if found were set to 1, the last statement in the loop (n++) would still execute once before getting kicked out of the loop on the next iteration. So, if n were 4, when Rudolf was found, n would be 5 by the time the loop was exited. I'm taking advantage of the fact that n is always greater than the index where Rudolf was found. My trace command doesn't refer to the array index where Rudolf was found but rather to the made-up term "spot," which is more intuitive than array's indexes (that count 0,1,2 ). Finally, it's quite possible that "Rudolf" is never found! To ensure that the loop would exit after each index was checked, I added && n<allNames.length to the condition (found==0). Translated, the entire condition reads: "While both found is zero and n is less than the total number of items in allNames." (Notice that I didn't have to say n<=allNames.length because the length is 8 and I only needed to check through the last item at index 7.)

The previous example shows how while loops require extra maintenance in the form of your own iterating variable that you first initialize and then increment. This is almost always necessary, although occasionally you don't need to do it. For example, if you're trying to create two random numbers, the following statement should work fine:

oneNum=0;  otherNum=0;  while (oneNum==otherNum){     oneNum=Math.random();      otherNum=Math.random();  }

There's a theoretical possibility that on every iteration, both oneNum and otherNum will be assigned the same random number. The two variables contain the same value at the start (0) and will then repeat while they remain the same. Likely, this loop will execute only once, but even if the two random numbers came out the same, it would take only a fraction of a second to loop 1,000 more times. On my old 450MHz Pentium III, for example, a loop like this will iterate more than 10,000 times per second! The point is that there's a high likelihood that two different random numbers will be found before Flash reaches its 15-second timeout. Unless you include a "way out" in the condition, however, a while loop could try looping forever. (This just means that Flash will reach the timeout dialog box refer to Figure 5.9 but that is a bad thing because the user sees the dialog.)

Let's review a few final points about loops. The user won't see any visual change on the Stage during the loop. For example, consider the following statement:

for (n=1; n<6; n++){     someClip._x= n*5;  }

You might expect to see someClip's x position appear at 5, 10, 15, 20, and then 25. It actually does move to those five locations, but you won't see it move. When the loop is finished, you'll see it in the final resting position of 25 but the Stage does not refresh after each loop. (By the way, you can certainly animate a clip by using scripts, as we'll do in the Workshop Chapter 14, "Offline Production," and Workshop Chapter 15, "Creating a Dynamic Slide Presentation" you just can't do it by using loops.)

Finally, there are two interesting statements that change the flow of a loop. Similar to the way return will skip out of any function or enclosed event (such as on(press)), both break and continue can make a loop change course. Specifically, the break statement will jump out of any loop (actually, it jumps to the end of the loop), and the continue statement will immediately jump to the top of the loop (that is, iterate) by skipping any further statements within the loop.

Here's an example that shows how the continue statement jumps to the top of a loop:

for (n=1; n<6; n++){     if (n==3){         continue;      }      trace ("A number that's not three is " + n);  }

If the condition (n==3) is true, the continue statement jumps to the top of the loop (to continue) while bypassing the trace statement below. The continue statement effectively says, "Skip the rest of the statements, but continue looping."

The following example shows how the break statement will jump out of a loop:

n=1;  while(true){   //do something    if (n>10 && n%2==0){     break;    }    n++;  }  trace ("The first even number past ten is "+n);

Without the break statement, this loop would repeat forever. When break is executed, Flash jumps to the end of the loop where it is currently enclosed (that is, to where the trace statement appears in this example). This is just a quick way to get out of a loop. Notice, too, that break will jump past the n++ statement (that is, right outside the enclosing curly brace). You also use break in the switch statement. There, it will cause only the remaining cases to be skipped but any code following the closing brace of the switch statement itself will be encountered. Compared to return, the break statement only jumps past the end of an enclosed loop, whereas return jumps out of the function or the event that started it all (as with on (press)) most likely skipping a lot more code than a break statement would. For example, if return were used in this example instead of break, the trace statement would be skipped as well.

Applied Expression Writing

This chapter is packed with concepts that relate to composing scripts. You've seen a couple of previews of concepts that will come up later (such as arrays and functions), but for the most part, everything in this chapter has to do with writing expressions and statements. The remaining pages provide a chance to practice writing more complex expressions. I'll provide the objective and you should at least try to write the pseudo-code and then (if you can) the actual script. I'll provide my solution with a translation. You should remember two things while you work through these challenges. First, my solution is just one of countless possible solutions. You might very well find a better way to solve the problem. Second, I include many made-up variables. In a real project, these variables would necessarily have their values set earlier or within a different part of the movie. For example, I might use the expression score>=80. The idea is that the variable score is one that I've been tracking. Perhaps, attached to the button for each correct answer, I used the script: score=score+10;. I'm just assuming that such variables have had their values set properly already. You can make up variables to use in expressions just realize that you'll have to take care of them by changing their values, as necessary.

Objective:

Given a minimum (x location, for example) and a maximum x, write an expression that uses a given percent to return an integer midpoint based on percent. That is, if the west coast is at 100 miles longitude and the east coast is at 3100 miles longitude, a location 50 percent across the country would be at longitude position 1600 miles. Even though most people can figure that out in their head, it's more difficult to write the expression (using variables named high, low, and percent).

Solution:

low+ Math.floor((high-low)*(percent/100));

Translation:

Calculating 50 percent of 3000 is easy. Just use 3000*(50/100). To determine the width of the United States, for example, you can subtract the longitude of the east coast (high) from the west coast (low). Our formula so far: (high-low)*(percent/100). That will usually result in a decimal answer, and using Math.floor() returns the integer portion. Finally, the entire expression is added to low (or the west coast) because you'll want to start counting from there. For example, if the longitude of west coast is 5000 and the east coast is 8000, 50 percent of the difference is still 1500. If you want to end up in the middle of the country, however, you need to add 1500 to 5000 that is, 6500 (by counting from the west coast). Figure 5.10 should help you visualize this problem.

Figure 5.10. A visual representation of the problem might help you solve the expression for percent.

graphics/05fig10.gif

graphics/icon01.gif

To determine a proportional x position between two points, use:

xLoc = low + Math.floor( (high-low) * (percent/100) )

Objective:

Build an array that contains the days of the week, as follows:

daysOfWeek=["Sunday","Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];

Then write a loop that displays all the days of the week in the Output window (using the trace statement).

Solution:

daysOfWeek=["Sunday","Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];  for (n=0; n<7; n++){     trace(daysOfWeek[n]);  }

Translation:

After populating the array, our for loop initializes n to 0 (because, remember, the first item in an array is in index 0). Then, while n is less than 7 (the last item in the array is at index 6), our trace statement displays the nth item in the array. Notice that the following also works, but displays the days in reverse order:

for (n in daysOfWeek){     trace(daysOfWeek[n]);  }

Objective:

Write a statement (using if, if else, or if else if) that sets a variable called grade to A, B, C, D, or F, based on a variable called score, using a scale in which 90 100 is an A, 80 89 is a B, 70 79 is a C, 60 69 is a D, and 0 59 is an F.

Solution:

grade="F";  if (score>=60){     grade="D";  }  if (score>=70){     grade="C";  }  if (score>=80){     grade="B";  }  if (score>=90){     grade="A";  }

Translation:

Granted, this isn't the eloquent solution; there are many other possibilities. The way I solved it was to first award an F (sort of discouraging, I suppose), and then if score was high enough (score>=60) I give a D, and so on. Even though this isn't a super-efficient solution after all, if a score were 95, the grade would be assigned F, D, C, B, and then finally A I used it because it's easy to read, not because it shows off every component of ActionScript.

Summary

What a chapter! Believe it or not, there's more. However, the topics covered in this chapter cover almost all the typical programming tasks that you'll be doing day in and day out. Although many upcoming chapters have interesting and valuable information, this chapter summarizes the core skills.

CONTENTS


ActionScripting in MacromediaR FlashT MX
ActionScripting in MacromediaR FlashT MX
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 41

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net