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:- Start by setting your
estimate
equal to a guess (any postive number will do, such asn/2
, or perhaps even just1
). - Compute a new estimate by averaging your guess with
n/estimate
. - 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.
- Start by setting your
- 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.
- 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 do
…while
loop, its use is illustrated in the following example:
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!)