Conditional Execution

You can have Director execute different Lingo code in response to various conditions. Any non-zero expression (including negative numbers) is considered TRUE, and only an expression that evaluates to zero is considered FALSE. Refer to Chapter 5 for details on evaluating comparisons, including compound expressions. (These example multi-line code fragments must be placed in a handler for testing.)

If...Then...Else...End If Decisions

The if statement will execute different Lingo code based on the value of the specified expression. This allows you to, say, use a single Director movie on both Macintosh and Windows but branch to different code depending on the playback platform, as determined by the platform property, such as:

if (the platform starts "Windows") then
  -- Do Windows-specific stuff here
else
  -- Do Macintosh-specific stuff here
end if

The if statement has many possible forms, but I recommend only these. Items shown in curly braces are optional. Note the correct indentation:

Single-Clause If...Then...End If

if expression then
  statement1
  statement2
end if

Multiple-Clause If...Then...Else If...Else...End If

The else if and else clauses are optional:

if expression then
   statement1
{else if expression then
   statement2}
{else
   defaultAction}
end if

For example:

if x = 1 then
  go frame "Slide1"
else if x = 2 then
  go frame "Slide2"
else if x = 3 then
  go frame "Slide3"
else
  go frame "End"
end if

One-Line and Two-Line If Statements

The following forms are theoretically valid, but they often misbehave in Projectors and within nested if statements, so I don’t recommend them:

if expression then statement1

or

if expression then statement1
else statement2

The Classic Three-Line If Statement

The one-line and two-line forms of the if statement make it hard to tell if the indenting is correct even after Director auto-formats your Lingo.

Warning

Where I use the one-line form of if...then in the examples throughout the book, I do so only for brevity. The Lingo compiler occasionally makes errors when evaluating one-line and two-line if statements.

Use the following three-line form even for simple if statements, especially when nesting if statements.

if (expression) then
  statement
end if

By always using an end if statement, and by always including a carriage return after the then keyword, Director won’t get confused and neither will you. The auto-indenting will work reliably and it also makes the code easier to read and debug. See Chapter 3.

Nested Ifs

You can nest if statements to create the desired logic. It is crucial that you use the three-line if...end if construct, rather than the one-line or two-line form. An end if always pairs up with the most recent if from the inside out. Let Lingo’s auto-indentation be your guide.

if expression1 then
  if expression2 then
    action1
  else
    action2
  end if
else
  defaultAction
end if

Example 1-20 is a typical usage of a nested if statement:

Example 1-20. Nested If Statements

Nested If Statements

Common Errors When Using If Statements

Although conceptually simple, the if statement consistently confuses new Lingo programmers. Avoid the common pitfalls shown in the following examples.

Omitting the End If or the Then

There must be one end if for each if statement. Watch for this, especially with nested ifs, such as is shown in Example 1-21.

Example 1-21. Common If Statement Errors

if x > 5 then
  if x < 7
    statements
  end if
-- You are is missing an "end if" here

If you omit the then keyword, you’ll also have problems:

if x > 5   -- the keyword"then" is missing
  statements
end if

Failure to Use Else If

Use one if...else if...end if statement instead of a series of if statements.

The following is inefficient because if x equals 5, it will never equal 6, and vice versa:

if x = 5 then
  -- Do something
end if
if x = 6 then
   -- Do something different
end if

This is more efficient (see also "Case Statements" later in this chapter):

if x = 5 then
  -- Do something
else if x = 6 then
   -- Do something different
end if

Improper Comparison Expressions

The incorrect order of comparison can lead to the wrong code being executed. Some code may never be executed, or code may be executed unintentionally.

In this example, both if statements will be executed if x equals 5. This may be what you want, but it is more likely a logic error:

if x > 0 then
  statements
end if
if x > 4 then
   statements
end if

In this erroneous example, the second branch will never be executed because the first branch is taken in every case where the second condition would be TRUE:

if x > 0 then
  statements
else if x > 4 then
   alternative statements (never reached)
end if

In this corrected example, the second branch will be executed only if x is less than 4 but greater than 0:

if x > 4 then
  statements
else if x > 0 then
   alternative statements
end if

Nesting If Statement Unnecessarily or Incorrectly

Nested if statements seem to give people fits. Don’t use a nested if statement when an if...then...else if...end if statement will suffice.

The innermost conditional clauses in this example are never executed.

Example 1-22. Nesting If Statements Properly

                if x = 3 then
                alert "Executed option 3"
                -- This is never executed; if x is 3, it's not 4
                if x = 4 then
    alert "Executed option 4"
                -- This is never executed; if x is 4, it's not 5
                if x = 5 then
      alert "Executed option 5"
    end if
  end if
end if

The correct construct is:

              if x = 3 then
  alert "Executed option 3"
else if x = 4 then
  alert "Executed option 4"
else if x = 5 then
  alert "Executed option 5"
end if

Excess End If Statements

Use an end if only for each if, not for each else if (sometimes Director won’t complain, but the results won’t be what you wanted). Example 1-23 is wrong.

Example 1-23. Using End If Improperly

if x = 3 then
  alert "Executed option 3"
else if x = 4 then
  alert "Executed option 4"
else if x = 5 then
  alert "Executed option 5"
end if
end if  -- This is not needed
end if  -- This is not needed

Extraneous end ifs can change your logic unintentionally. Contrast the following with the earlier nested if example. It will always execute the last alert command because it follows the last end if:

if field "name" <> EMPTY then
  -- These five lines of code are a nested "if".
  if length(field "name") > 10 then
    alert "Enter only the first ten letters"
  else
    alert welcome & field "name"
  end if  -- This terminate the inner "if" statement
end if -- This terminate the outer "if" statement
-- This will always be executed
alert "Please enter your name"

Missing End If Statements

Each if statement must have a matching end if. Erroneous structures at the end of a preceding handler will trickle into the next handler and corrupt the indentation. Note the incorrect indentation in the mouseDown handler caused by the missing end if in the preceding mouseUp handler:

on mouseUp
  if the clickOn = 3 then
    if rollover(7) then
      put "Yahoo!"
    end if
    put "whatever"
    -- This is missing an "end if"
end

on mouseDown
put "Hello"  -- Note incorrect indentation
end

Note the corrected indentation of put "Hello" in the mouseDown handler:

on mouseUp
  if the clickOn = 3 then
    if rollover(7) then
      put "Yahoo!"
    end if
    put "whatever"
  end if  -- That's better!
end

on mouseDown
  put "Hello"
end

Inefficient Use of If Clauses:

Look for ways to reduce the number of if clauses. Here we’ve modified the example shown under "Multiple-Clause If...Then...Else If...Else...End If.” We’ve reduced the number of clauses by using the value of x to construct the name of the destination marker (“Slide1,” “Slide2,” or “Slide3”).

if x >= 1 and x <= 3 then
  go frame ("Slide" & x)
else
  go frame "End"
end if

Case Statements

The case statement conditionally executes Lingo statements based on the value of an item. It is often easier to implement than multiple if...then statements, but long case statements can be slower than the corresponding if...then constructs. Director executes the statement(s) following the first value that matches the case clause. The colon after otherwise is optional, and multiple statements can be included after each value to be matched. Once a match is found and its statements executed, subsequent values and their statements are ignored, and execution continues after the end case statement.

case (item)of
  value1:
    statement
  value2:
    statement
    statement
  value3, value4:
    statement
  otherwise:
    statement
end case

Example 1-24 shows a case statement and the equivalent if statement.

Example 1-24. Case Statements

on keyDown
  case (the key) of
    RETURN: go frame "done"
    TAB, BACKSPACE: beep
    otherwise: pass
  end case
end keyDown

The case statement is equivalent to the following if ... then statement:

on keyDown
  if (the key = RETURN) then
    go frame "done"
  else if (the key = TAB) or (the key = BACKSPACE) then
    beep
  else
    pass
  end if
end keyDown

To use a comparative expression to branch within a case statement, use TRUE in the case clause, and enclose the comparative expression in parentheses, such as:

on keyDown
  case (TRUE) of
    (the keyCode >= 123 and the keyCode <= 126):
      -- The user pressed an arrow key
    statements
        (the keyCode = 122):
      -- The user pressed F1
    statements
    (the keyCode = 118):
      -- The user pressed F4
    statements
    otherwise:
      alert "Please press an arrow key, F1 or F4"
  end case
end keyDown

Repeat Loops

Repeat loops repeat any statements within the body of the loop. They are used to cycle through a series of items, such as elements in a list, or to repeat an operation a specific number of times (they are equivalent to so-called for...next loops used in some languages). When the end repeat command is reached, execution begins again at the top of the repeat loop until some condition causes the loop to terminate. Execution continues at the statement following the end repeat command. Use the Debugging window, described in Chapter 3, to examine the exact flow of Lingo as Director executes the steps in the repeat loop.

Most repeat loops are very fast, even for hundreds or thousands of iterations, but Director can’t do anything else while you are executing a repeat loop, especially in Shockwave.

Warning

Don’t use repeat loops that monopolize Director’s attention for more than a few seconds.

In Shockwave, you can’t check whether an operation completed with netDone() from within a repeat loop because Director doesn’t perform network operations during a repeat loop. Likewise, Director doesn’t update all system properties or update the Stage automatically during a repeat loop.

The repeat loop has four forms (3 forms of repeat with plus repeat while), as shown earlier in Table 1-3. The example multi-line code fragments must be placed in a handler for testing.

The repeat while command repeats as long as an expression is TRUE. If the expression never becomes FALSE, it will be an infinite loop.

Example 1-25. Repeat Loops

repeat while (the stillDown = TRUE)
  put "The mouse is still down"
end repeat

The repeat with commands loop through a range of values. Although not necessarily apparent, the three forms of the repeat with loop all use an index variable (counter) that changes automatically each time the loop is executed.

In this example, the index variable (i) starts at the initial value (in this case 1) and increments each time through the loop until it hits the upper bound (in this case 100). If the initial value is greater than the upper limit, the statement(s) within the loop are never executed.

repeat with i = 1 to 100
   put "The next number is" && i
end repeat

The following is the equivalent repeat while loop to the above repeat with loop. You can see that repeat with loops are more convenient.

set i = 1
repeat while i <= 100
  put "The next number is" && i
  set i = i + 1
end repeat

The repeat with...down to command repeats for a decreasing range of values. In this case, the index variable (i) is decremented (not incremented) each time through the loop.

repeat with i = 100 down to 1
 put string(i) && "Bottles of beer on the wall..."
end repeat

The repeat with...in command cycles once through all the items in a list. (Refer to Chapter 6 or ignore this example for now.) Each time through the loop, i is automatically set to the next item in the list. Use the following to loop through an irregular set of numbers:

set myList = [12, 17, 52, 43]
repeat with i in myList
  put "The next item in the list is" && i
end repeat

The variable i is not an integer in the previous example; it is actually the contents of the next item in the list. The previous example can be simulated with a standard repeat with loop, as follows:

set myList = [12, 17, 52, 43]
repeat with j = 1 to count (myList)
  set i = getAt (myList , j)
  put "Item number:" && j
  put "The next item in the list is" && i
end repeat

In the last example, note that the index variable j is an integer and can be used to print a list element’s position within the list. You must manually intialize and increment an index variable to obtain a similar counter if using a repeat with...in loop, such as:

set myList = [12, 17, 52, 43]
set j = 1
repeat with i in myList
  put "Item number:" && j
  put "The next item in the list is" && i
  set j = j + 1
end repeat

Altering Loop Execution

Use next repeat to skip the current iteration of a repeat loop and to continue with the next iteration.

repeat with x = 1 to the number of members
  if the memberType of member x = 0 then
    next repeat
  end if
  put "Item" && x && "is type" && the memberType of member x
end repeat

Use exit repeat to exit the current repeat loop immediately. Program execution continues with the statement following the end repeat statement. Exit repeat exits only the current (innermost) repeat loop, and it will not exit nested repeat loops. Use exit, or abort, or a flag to exit multiple loops, as shown in Example 1-26. (Again, use the Debugging window to examine the exact flow of Lingo as Director executes the following code.)

Example 1-26. Nested Repeat Loops

on findIt
  global gFoundIt
  set gFoundIt = FALSE
   -- Search for a #shape cost member
  repeat with y = 1 to the number of castLibs
    repeat with x = 1 to the number of members of castLib y
      set thisItem = the memberType of member x of castLib y
      if thisItem = #shape then
        set gFoundIt = member x of castLib y
        exit repeat -- exit the innermost loop
      end if
    end repeat
    -- exit the outermost loop too
    if gFoundIt then
      exit repeat
    end if
  end repeat

  -- Execution continues here after loop
  if gFoundIt then
    put "Found shape cast member at" && gFoundIt
  else
    put "Shape cast member not found."
  end if
end

Manually Controlling the Loop’s Counter

Lingo does not precalculate the number of iterations it will perform for a repeat...with loop. Rather, it reevaluates the expression each time through the loop. In the following example, we manually increment the index variable (i) to step by two rather than one. Note that we add only 1 to i, not 2, because Lingo will automatically increment i once each time the loop is executed.

Example 1-27. Customized Repeat Loops

on printEvenNumbers
  repeat with i = 0 to 100
    put i
    set i = i + 1
  end repeat
end printEvenNumbers

Tip

Avoid manually setting the index variable within a repeat loop unless you need to change the number of loop iterations. Setting it incorrectly can lead to an infinite loop.

After a loop completes, the index normally is one greater than the ending value:

on testRepeat
  repeat with i = 1 to 100
    put i
  end repeat
  put "The ending value for i is" && i
end testRepeat

testRepeat
-- 1
-- 2
-- <etc.>
-- 99
-- 100
-- "The ending value for i is 101"

Infinite Loops

An infinite loop is a repeat loop that will never be exited because the conditional expression never turns FALSE. (Apple’s street address in Cupertino, California, is One Infinite Loop). An infinite loop will appear to hang the computer, and you must abort the Projector or halt the movie to stop it.

The most simple infinite loop is shown in Example 1-28.

Example 1-28. Infinite Loops

repeat while TRUE
  -- do something
end repeat

Unless you add an exit repeat, Lingo will never exit such a repeat loop:

startTimer
repeat while TRUE
  if the timer > 60 then
    exit repeat
  else
    -- do something
  end if
end repeat

But this would be better written as:

startTimer
repeat while the timer <= 60
  -- do something
end repeat

It is easy enough to create an infinite loop accidentally. The following will loop forever, assuming the loop takes less than 60 ticks to execute, because startTimer is within the repeat loop and keeps resetting the timer.

startTimer
repeat while the timer <= 60
  startTimer
  -- do something
end repeat

The following would lead to an infinite loop as well. The variable x is unintentionally set to 5; the programmer did not realize that x is also being used as the loop’s index variable.

repeat with x = 1 to 10
  put "x" && x
  set x = 5
end repeat

A loop will also be infinite if some condition you expect to become FALSE remains TRUE forever. Suppose you are waiting for a sound to start:

puppetSound "mySound"
repeat while not soundBusy(1)
  nothing
end repeat

If the sound never starts (in this case, you need to add updateStage after the puppetSound command), Director will loop forever because soundBusy(1) will never become TRUE, so not soundBusy(1) will always remain FALSE.

Tip

Use the Debugger to diagnose infinite loops and similar problems. See Chapter 3.

Get Lingo in a Nutshell now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.