PencilCoder

Teacher's Guide: While Loops

Overview

This lesson introduces while loops and illustrates purposes for which they are better suited than for loops.

More about the lesson

while loops and for loops are similar in that both provide a means to iterate, but the two structures are based on fundamentally different logic. for loops establish a maximum number of iterations when the loop is constructed; formally, this approach to iteration is termed definite iteration. while loops, in contrast, continue iterating so long as a specified condition is met. The structure of looping provided by while loops is called indefinite iteration.

For coding challenges involving definite iteration, while loops and for loops can be used interchangeably. However, while loops are the better choice when the coder doesn't know up front how many iterations are needed to complete a task. For example, if the goal is to move a turtle randomly until it exits the screen, a while loop would be more appropriate than a for loop. Such a loop might look like this:

while inside(window)
  fd 5
  rt random([-25..25])
  await done defer()

An attempt to replicate this snippet using a for loop would not only necessitate embedding a break statement, but it also requires specifying an arbitrarily large upper bound for the number of iterations. For example, consider the following approximate equivalent of the preceding while loop:

for [1..10000]
  fd 5
  rt random([-25..25])
  await done defer()
  if inside(window)==false
    break

Note the need to specify an arbitrarily large number of iterations to help ensure that the loop doesn't end before the sprite exits the screen. However, regardless of how large a value is chosen, there is still a chance that number might not be high enough. The while loop is therefore the preferred approach.

Because they can be used for both definite and indefinite iteration, while loops are more flexible than for loops. As the lesson notes, while loops can be written to do anything that a for loop can do, but the reverse is not true. However, each coding structure has its own relative advantages, and as a result in many situations the choice of using a for or a while loop simply comes down to a question of style and ease of use. Although while loops provide greater control over iteration than for loops, that often comes at the expense of more verbose code. For example, for loops are generally preferable for iterating over collections. Consider, for example, the following snippet, which iterates over a collection of turtles and assigns each one a new color:

for t in turtles
  t.wear random(color)

Accomplishing comparable results with a while loop is more challenging. In fact, using while loops for this purpose necessitates the use of additional features not yet introduced in this curriculum (subscripts), as this snippet illustrates:

i = 0
while i<turtles.length
  turtles[i].wear random(color)
  i++

The use of for loops with a break statement is appropriate in certain situations. When indefinite iteration is required, however, a while loop should always be chosen. Simply specifying a large number of iterations in a for loop is expedient, but it is poor style and a bad habit, as it could ultimately lead to the code failing.

Notes to activities

The WhileLucky, RollDoubles, and EstimateSqrt activities all provide opportunities to work with indefinite iteration. Arguably, one does not need while loops for the ConcentricRings activity, as the number of iterations can be computed ahead of time. However, it can be easier to use a while loop than to figure that out—especially if the changes between ring sizes are geometric rather than linear (i.e., each radius is a ratio of the prior one, rather than smaller by a fixed amount each time.)

Additional activities

  • TurtleRace: Make a new variant on the old classic, but this time using a while loop to handle the indefinite iteration.

  • ZenosBox: Make a "squiral" that has sides that decrease in size by a fixed percentage each time, stopping when the side length reaches 1.

  • Cone: Start with a copy of your ConcentricRings code, but this time move and turn the turtle a little bit between drawing each ring, to generate a cone.

  • InfiniteReflections: Mimic what happens when you have a mirror reflecting a scene with a mirror in it reflecting a scene with a mirror...

  • BinaryConverter: Write a script that picks a large random number and converts it to its binary (base-2) equivalent. For an extra challenge, compute the hexidecimal (base-16) value as well.
  • NewtonsMethod: The lesson suggests a graphical method to estimate the square root of 2. But how can you compute the square root of 5? or of 29? A famous mathematician named Isaac Newton figured out the following algorithm for finding the square root of n. It's tedious to carry out the computations by hand, but easy to do with a computer:

    1. Start by setting your estimate equal to a guess (any postive number will do, such as n/2, or perhaps even just 1).
    2. Compute a new estimate by averaging your guess with n/estimate.
    3. Keep repeating, and you will get closer and closer to the true square root.

    In coding this problem, the hardest part is knowing when to stop searching. The trick is to stop when the difference between the new guess and the previous guess gets tiny. Note, however, that the difference can be negative, so be sure to compute the absolute value of the difference.

  • Mount a LaserCannon on one side of the screen, point its barrel in a random direction, and fire! Make its shell bounce around the screen until it hits the cannon.
  • Blank space Blank space Blank space
  • 440YardDash: Code a race that has the turtles compete on a circular track, each in their own lane. To make the race far, you will need to stagger the starting point of each turtle (just like you often see in the Olympics or other track and field events). You can derive the offset for each turtle by computing the circumference of the circle that it will follow, and comparing that to the circumference of the circle followed by the innermost turtle. Recall that the circumference of a circle is related to its radius by the formula \(\small{C = \pi r^2}\).
  • DartsForPi: There are many algorithms to compute an approximation of \(\pi\). In this activity, write a program that estimates \(\pi\) using a statistical approach that involves throwing darts at "a dartboard", i.e., a round target inscribed in a square background. Simulate this random dart throwing, keeping track of the number of darts that land in the circle and those that don't. Because the diameter of the circle is the same as the side length of the square, we can deduce that the ratio of darts that land in the circle to all darts thrown at the board is \(\frac{\pi}{4}\). (Can you work out this theoretical result on your own? Hint: it has to do with area.) Keep iterating until your estimate is accurate to 5 or 6 decimal places of the known true value of \(\pi\), i.e., 3.14159. (By the way: this is a horribly inefficient way to compute an estimate of \(\pi\)!)

Beyond the lesson

CoffeeScript alternatives for indefinite iteration

CoffeeScript adds two variants on the while loop: loop and until.

The CoffeeScript loop keyword is equivalent to while true, which sets up an infinite loop. Use of while true is discussed below in the Technicalities section.

until loops in CoffeeScript are comparable to while loops, except they continue so long as the boolean expression evaluates to false. Thus, the following two loops are equivalent:

until touches(blue)
  fd 5
  rt -20 + random(41)
      
while not(touches(blue))
  fd 5
  rt -20 + random(41)

What can go wrong

The computer "got stuck in calculations" or "got confused"

When working with iteration, students will invariably write programs that cause the browser to seemingly freeze and ultimately crash. In industry parlance, the browser is said to get "hung up" or to "hang". In such situations, the hung computer is not actually frozen. Rather, it is busy carrying out computations according to the script, though while doing so it does not return execution control to the user or the rest of the system. It thereby gives the appearance of being frozen, since the computer gives no sign of making progress and it also doesn't respond to user input.

A hung state continues until either allocated system memory is exhausted, at which point the program will crash; or a preestablished maximum time limit is reached, at which point the user is typically prompted by the browser to determine if he or she wishes to terminate the script. The qualifier "typically" is added here because Pencil Code overrides the default browser behavior in the latter case. In the Pencil Code environment, the script is always terminated after being hung for 4 seconds. This feature is beneficial because it helps prevent the loss of unsaved data.

Examples of code generating the different messages can be found here. When a program crashes because allocated memory is exceeded, Pencil Code will provide the following warning:

Allocated memory is often exceeded owing to scripts that carry out a very large number of calculations or large number of animations. Frequently, such scripts involve iteration. In such cases, particularly when animation is involved, an expedient fix is to embed periodic calls to await done defer(). This statement effectvely breaks the program into smaller execution blocks that are run sequentially and (importantly) with a break between execution so that animations can appear on screen, emptying animation queues and thereby freeing up system resources.

When a program crashes because the script timed out (i.e., was hung for 4 seconds), Pencil Code will provide the following warning:

A common reason for scripts to time out are infinite loop. An infinite loop arises from iteration instructions that, intentionally or not, do not include a terminating condition—i.e., an infinite loop is a loop that never ends.

Coders will typically create infinite loops by mistake. For example, in the lesson's coding snippet, omission of the statement distance = distance + x will yield an infinite loop, because distance can never become a value that matches the condition checked at the beginning of each iteration:

goal = 500
  distance = 0
  while distance < goal  # always evaluates to true
    x = random(50)
    rt 25
    fd x
    #distance = distance + x  # erroneously omitted

Technicalities

while true

Passing the boolean expression true to while creates a (potentially) infinite loop. This is a legitimate technique that must nonetheless be used with great care in JavaScript owing to its potential to cause the browser to freeze up, or hang. For example, even the following simple program will crash:

start = Date.now()
while true
  see (Date.now()-start)/1000

The source of the problem is that a while true loop, once started, runs using 100% of the CPU resources allocated to JavaScript in the current browser window. Because the loop never completes, it will never return control to the browser, and thus the browser seems to freeze. Of course, it only seems frozen, as it is actually continuing calculations, processing lines of code, but it never reports those results back to the user.

Modern browsers will ultimately sense this situation and will prompt the user to exit the page, though this can take a minute or more. However, the browser will not provide any additional information about the source of the problem, and it will force the user to exit the page (which could result in the loss of unsaved work).

Recognizing the challenges posed by infinite loops, Pencil Code has a built-in failsafe designed to catch a hung browser and terminate the student's script without allowing the browser itself to time out. It provides the following message onscreen:

Additionally, when it detects a hung browser, Pencil Code will display the progress of the program up to the point of being interrupted. In programs involving animation, all queues are flushed, meaning they are run at speed Infinity and the final result at the point of interrupt is displayed to the screen. The simple script introduced at the beginning of this section has no animation; it simply logs timestamps to the Pencil Code test panel. The last value reported from this program each time is about 4 secconds, reflecting the time elapsed before Pencil Code aborts a hung program.

In a web browser, a true infinite loop created with while will result in a hung browser. However, embedding a call to await done defer() in the loop allows us to seemingly circumvent this limitation and make a program that uses use while true to effectively run for an unlimited length of time, as the following example illustrates:

while true
  await done defer()
  if inside(window) 
    rt random(90)
  else
    turnto(0,0)
  fd 25

The reason this works is because the call to await effectively breaks up the program into many smaller programs. However, this structure should be avoided. It is achieving the result of "unbounded iteration" but at a high computational cost, from the perspective of system resources (i.e., CPU usage). A far superior solution is to use timers and callbacks, as discussed in the Timers (forever)! lesson.

Loops created with while true can also be convenient for tasks that don't require unbounded iteration. In this setup, a conditional break statement is embedded in the body of the loop, rather than placing the condition in the while statement. For example, consider the modificaiton of the previous unbounded iteration, which stops when the sprite reached the edge of the screen:

while true
  await done defer()
  if inside(window) 
    rt random(90)
    fd 25
  else
    break

The preceding example is trivial; arguably, placing the conditional logic within in the while statement would be better style. However, this should not imply that while true cannot be advantageus. For example, it can greatly benefit readability of code when the conditional logic is more complicated, such as requiring evaluation of different boolean expressions at separate points of the while block.

The following example programs/activities make use of "unbounded iteration" using while true. Many students enjoy exploring this technique. Note, however, each of these programs would be more appropriately coded using JavaScript timers, discussed in the Timers (forever)! lesson, after students have gained experience with custom functions, callbacks, and events.

  • HSL-Sampler: Use an infinite loop to create a piece of random art usign the hsl function with variations of all three parameters.

  • MovingBackdropRocket: Code a program that flies a rocket or a plane through space indefinitely, passing stars or clouds along the way. Your vehicle should constantly point towards one end of the sceen, while the background constantly moves in the direction opposite to that, thereby generating the illusion of motion. For the background, one option is to use a large sprite that periodically "resets" back to its initial position, so that the same background gets recycled, similar to how it was done in low-budget cartoons from the 1970s and 1980, for example as in this Snagglepuss clip from Hanna-Barbera.

do-while

Many languages, including JavaScript, provide an alternative the the basic while loop that allows the boolean condition to be tested at the end of each iteration, rather than at the beginning. This variant is convenient when the intent is to ensure that the while block is executed at least once every time the script is run. In JavaScript, this is referred to as a dowhile loop, its use is illustrated in the following example:

Blank space

In CoffeeScript, the do keyword is used to immediately invoke a passed function (a callback), which helps explain why CoffeeScript does not make use of this syntax. (Callbacks are the focus of the eponymous lesson, Callbacks!)