Previous | Next | Table of Contents | Index | Program List | Copyright

6.1 Control Structures: The WHILE Statement

In all the loops used so far, the exact number of loop repetitions required could be determined before the start of loop execution. We used the FOR statement to implement these counting loops.

Ada's FOR loop is limited in that counting can proceed only over a range that is discrete (i.e., of an integer or enumeration type). Furthermore, the counter variable is updated by taking its successor (or predecessor if REVERSE is used)-- either adding 1 (subtracting 1) if it is an integer counter or taking the Succ (Pred ) attribute if it is an enumeration counter. This means that counting cannot proceed, for example, by 2s. In many programming situations, the exact number of loop repetitions cannot be determined before loop execution begins. It may depend on some aspect of the data that is not known beforehand but usually can be stated by a condition. For example, we may wish to continue writing checks as long as our bank balance is positive, as indicated by the following pseudocode description.

    WHILE the balance is still positive LOOP
        Read in the next transaction 
        Update and print the balance
    END LOOP;

The actual number of loop repetitions performed depends on the type of each transaction (deposit or withdrawal) and its amount.

To summarize, there are three kinds of looping problems where the Ada FOR statement cannot be used:

The first two cases are called counter-controlled loops; they are still controlled by counters even though a FOR statement cannot be used. The third case is often called an event-controlled loop, because some arriving event, in the input data or some user interaction, triggers the end of the loop. In cases like these there are other alternatives for writing conditional loops in Ada. Ada provides two additional looping statements (WHILE and general LOOP) to implement conditional loops. The WHILE statement is discussed next; the general LOOP statement is introduced later in the chapter.

Example 6.1

Program 6.1 displays the odd numbers from 1 to 39, inclusive. Because the step size is not 1, we cannot use a FOR loop for this. A WHILE structure is used instead. A variable OddNumber is declared and used to control the loop. OddNumber is initialized to 1 before the WHILE statement is reached; the WHILE loop heading

    WHILE OddNumber <= 39 LOOP
controls the condition for continuing the loop. Inside the loop body, OddNumber is incremented:
    OddNumber := OddNumber + 2;

The loop body is repeated as long as the WHILE condition remains true. This is tested at the top of each iteration. If the condition is false, the loop is ended and control passes to the statement following END LOOP.

Program 6.1
Illustration of a WHILE Loop

WITH Ada.Text_IO;
WITH Ada.Integer_Text_IO;
PROCEDURE Odd_Numbers IS
------------------------------------------------------------------------
--| Displays odd numbers from 1 to 39, inclusive
--| Author: Michael B. Feldman, The George Washington University
--| Last Modified: July 1995
------------------------------------------------------------------------

  OddNumber : Integer;

BEGIN -- Odd_Numbers

  OddNumber := 1;
  WHILE OddNumber <= 39 LOOP

    Ada.Integer_Text_IO.Put(Item => OddNumber, Width => 3);
    OddNumber := OddNumber + 2;

  END LOOP;

  Ada.Text_IO.New_Line;

END Odd_Numbers;
Sample Run
  1  3  5  7  9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39

Example 6.2:

Program 6.2 displays a table of Celsius and equivalent Fahrenheit temperatures for the range of temperatures from 100 degrees Celsius to -20 degrees Celsius in steps of -10 degrees. The assignment statement

    Fahrenheit := (1.8 * Celsius) + 32.0;
converts each Celsius value in this range to a real Fahrenheit value. You can check this formula by knowing the freezing points (0 and 32 degrees) and boiling points (100 and 212 degrees) in the two systems. Because an integer can't be multiplied by 1.8 and the step size is not 1, a WHILE statement is used instead of a FOR.

Three Float constants are declared in the program. CStart is the starting value of the Float loop control variable Celsius, CLimit is the limit value, and CStep is the step value. The loop is executed for values of Celsius in the sequence 20.0, 15.0, 10.0, ... , -15.0, -20.0.

Program 6.2
Converting from Celsius to Fahrenheit

WITH Ada.Text_IO;
WITH Ada.Float_Text_IO;
PROCEDURE Temp_Table IS
------------------------------------------------------------------------
--| Displays a table of Fahrenheit and
--| equivalent Celsius temperatures.
--| Author: Michael B. Feldman, The George Washington University 
--| Last Modified: July 1995                                     
------------------------------------------------------------------------

  CStart : CONSTANT Float := 100.0;     -- initial Celsius temp   
  CStep :  CONSTANT Float := -10.0;     -- change in Celsius temp   
  CLimit : CONSTANT Float := -20.0;     -- final Celsius temp   

  Celsius :         Float;              -- Celsius temp   
  Fahrenheit :      Float;              -- Fahrenheit temp   

BEGIN -- Temp_Table   

  Ada.Text_IO.Put(Item => "Celsius     Fahrenheit");  
  Ada.Text_IO.New_Line (Spacing => 2);

  Celsius := CStart;
  WHILE Celsius >= CLimit LOOP

    Fahrenheit := 1.8 * Celsius + 32.0;
    Ada.Float_Text_IO.Put
      (Item => Celsius, Fore => 4, Aft => 0, Exp => 0);  
    Ada.Text_IO.Put(Item => "        ");
    Ada.Float_Text_IO.Put
      (Item => Fahrenheit, Fore => 3, Aft => 1, Exp => 0);  
    Ada.Text_IO.New_Line;

    Celsius := Celsius + CStep;
  END LOOP;

END Temp_Table;
Sample Run
Celsius     Fahrenheit

 100.0        212.0
  90.0        194.0
  80.0        176.0
  70.0        158.0
  60.0        140.0
  50.0        122.0
  40.0        104.0
  30.0         86.0
  20.0         68.0
  10.0         50.0
   0.0         32.0
 -10.0         14.0
 -20.0         -4.0

Example 6.3

Program 6.3 traces the progress of a hungry worm approaching an apple. Each time it moves, the worm cuts the distance between itself and the apple by its own body length until the worm is close enough to enter the apple. A WHILE loop is the correct looping structure to use because we have no idea beforehand how many moves are required.

Program 6.3
Worm and Apple

WITH Ada.Text_IO;
WITH Ada.Float_Text_IO;
PROCEDURE Worm_and_Apple IS
------------------------------------------------------------------------
--| Displays distances between a worm and an apple.  
--| The worm keeps reducing the distance by its body length 
--| until it is close enough to bite the apple.
--| Author: Michael B. Feldman, The George Washington University
--| Last Modified: July 1995
------------------------------------------------------------------------

  SUBTYPE NonNegFloat IS Float RANGE 0.0 .. Float'Last;
  
  WormLength: CONSTANT NonNegFloat := 3.5;  -- worm length in inches
 
  InitialDist: NonNegFloat;  -- input - starting distance from apple
  Distance:    NonNegFloat;  -- output - distance between worm and apple
 
BEGIN -- Worm_and_Apple

  Ada.Text_IO.Put
    (Item => "Initial distance (float) between worm and apple > ");
  Ada.Float_Text_IO.Get(Item => InitialDist);
  Ada.Text_IO.New_Line;

  -- Cut the distance between the worm and the apple by the worm's
  -- body length until the worm is close enough to bite the apple
  Distance := InitialDist;

  WHILE Distance > WormLength LOOP
    Ada.Text_IO.Put(Item => "The distance is ");
    Ada.Float_Text_IO.Put(Item => Distance, Fore=>4, Aft=>2, Exp=>0);
    Ada.Text_IO.New_Line;

    Distance := Distance - WormLength;   -- reduce Distance
  END LOOP;

  -- Display final distance before entering the apple.
  Ada.Text_IO.New_Line;
  Ada.Text_IO.Put(Item => "Final distance between worm and apple is ");
  Ada.Float_Text_IO.Put(Item => Distance, Fore=>4, Aft=>2, Exp=>0);
  Ada.Text_IO.New_Line;
  Ada.Text_IO.Put(Item => "The worm bites the apple.");
  Ada.Text_IO.New_Line;

END Worm_and_Apple;
Sample Run
Initial distance (float) between worm and apple > 12

The distance is   12.00
The distance is    8.50
The distance is    5.00

Final distance between worm and apple is    1.50

The assignment statement just before the loop initializes the variable Distance to the starting distance (12.0), which was previously read into InitialDist. Next, the loop header is reached and the loop repetition condition (or WHILE condition)

     Distance > WormLength

is evaluated. Because this condition is true, the loop body (through END LOOP) is executed. The loop body displays the value of Distance, and the statement

     Distance := Distance - WormLength;      --reduce Distance
reduces the value of Distance, thereby bringing the worm closer to the apple. The loop repetition condition is tested again with the new value of Distance (8.5); because 8.5 > 3.5 is true, the loop body displays Distance again, and Distance becomes 5.0. The loop repetition condition is tested a third time; because 5.0 > 3.5 is true, the loop body displays Distance again, and Distance becomes 1.5. The loop repetition condition is tested again; because 1.5 > 3.5 is false, loop exit occurs, and the statements following the loop end are executed.

It is important to realize that the loop is not exited at the exact instant that Distance becomes 1.5. If more statements appeared in the loop body after the assignment to Distance, they would be executed. Loop exit does not occur until the loop repetition condition is retested at the top of the loop and found to be false.

Just as in the counting loops in Chapter 5, there are three critical steps in Program 6.3 that involve the loop control variable Distance:

Every WHILE loop must contain initialization, test, and update steps. Unlike a FOR, which has a very strict syntax, in a WHILE the initialization and update steps can be arbitrary statements. Therefore, the compiler cannot check to ensure that you have included them, so you must be careful. If the first step is missing, the initial value of Distance will be meaningless. The last step ensures that we make progress toward the final goal (Distance <= WormLength) during each repetition of the loop. If the last step is missing, the value of Distance cannot change, so the loop will execute "forever" (an infinite loop). The WHILE loop is described in the next display.

SYNTAX DISPLAY
WHILE Statement

Form:
WHILE expression LOOP 
    statement sequence 
END LOOP;

Example:
Display powers of 2:
PowerOf2 := 1; 
WHILE PowerOf2 < 10000 LOOP
    MyInt_IO.Put (Item => PowerOf2); 
    PowerOf2 := PowerOf2 * 2;
END LOOP;

Interpretation:
The expression (a condition) is tested, and if it is true, the statement sequence is executed and the expression is retested. The statement sequence is repeated as long as (WHILE) the expression is true. When the expression is tested and found to be false, the WHILE loop is exited and the next program statement after END LOOP is executed. Note: If the expression evaluates to false the first time it is tested, the statement sequence will not be executed.

Example 6.4

It is instructive to compare the two loop forms that we currently know how to write: the FOR loop and the WHILE loop. We can always implement a FOR loop using a WHILE loop, but we cannot always implement a WHILE loop using a FOR loop. The WHILE loop shown below behaves identically to the FOR loop shown above it.

FOR i IN 1..5 LOOP
    Square := i * i;
    Ada.Integer_Text_IO.Put (Item => i, Width => 1);
    Ada.Integer_Text_IO.Put (Item => Square, Width => 1);
    Ada.Text_IO.New_Line;
END LOOP;

i := 1;
WHILE i <= 5 LOOP
    Square := i * i;
    Ada.Integer_Text_IO.Put (Item => i, Width => 1);
    Ada.Integer_Text_IO.Put (Item => Square, Width => 1);
    Ada.Text_IO.New_Line;
    i := i + 1;
END LOOP;

We can make the following observations about the two loop forms just shown:

1. The statement i := 1; before the WHILE loop initializes i to 1. The initialization of the FOR loop control variable i is specified in the FOR loop header statement.

2. The statement i := i+1; in the WHILE loop body increments i by 1. This step is implicit in the FOR loop.

3. Unlike the FOR statement, in which the counter variable is declared implicitly and has no existence outside the loop body, the loop variable in the WHILE is a "normal" variable: It must be declared, and it is known outside the loop body just like any other variable.

Wherever possible, use a FOR loop, rather than a WHILE loop, to implement a counting loop.

Example 6.5

The distance traveled in t seconds by an object dropped from a tower is represented by the formula distance = 1/2 × gt2, where g is the gravitational constant. Program 6.4 displays a table showing the height of a falling object at fixed time intervals after it is dropped from a tower and before it hits the ground. The number of lines in the table depends on the time interval between lines (DeltaT) and the tower height (Tower), both of which are data values. During each iteration, the current elapsed time (t) and the current object height (Height) are displayed. Next, the elapsed time is incremented by DeltaT and the new object height is computed. The message following the table is displayed when the object hits the ground. The sample output shows the result of dropping an object from a tower approximately the height of the Washington Monument (150 meters).

Program 6.4
An Object in Free Fall

WITH Ada.Text_IO;
WITH Ada.Float_Text_IO;
PROCEDURE Free_Fall IS
------------------------------------------------------------------------
--| Displays the height of an object dropped
--| from a tower until it its the ground.
--| Author: Michael B. Feldman, The George Washington University
--| Last Modified: July 1995
------------------------------------------------------------------------

  SUBTYPE NonNegFloat IS Float RANGE 0.0 .. Float'Last;
  
  g : CONSTANT NonNegFloat := 9.80665;      -- gravitational constant
 
  Height: Float;         -- input - height of object
  Tower:  NonNegFloat;   -- input - height of tower
  DeltaT: NonNegFloat;   -- input - time interval

  t:      NonNegFloat;   -- output - elapsed time

BEGIN -- Free_Fall

  -- Enter tower height and time interval.
  Ada.Text_IO.Put
    (Item => "Please enter all values in floating-point form.");
  Ada.Text_IO.New_Line;
  Ada.Text_IO.Put(Item => "Tower height in meters > ");
  Ada.Float_Text_IO.Get(Item => Tower);
  Ada.Text_IO.Put(Item => "Time in seconds between table lines > ");
  Ada.Float_Text_IO.Get(Item => DeltaT);
  Ada.Text_IO.New_Line(Spacing => 2);

  -- Display object height until it hits the ground.
  Ada.Text_IO.Put(Item => "        Time      Height");
  Ada.Text_IO.New_Line;

  t := 0.0;
  Height := Tower;

  WHILE Height > 0.0 LOOP
    Ada.Float_Text_IO.Put(Item => t, Fore => 8, Aft => 3, Exp => 0);
    Ada.Float_Text_IO.Put(Item => Height, Fore=>8, Aft=>3, Exp=>0);
    Ada.Text_IO.New_Line;
    t := t + DeltaT;
    Height := Tower - 0.5 * g * (t ** 2);
  END LOOP;

  -- Object hits the ground.
  Ada.Text_IO.New_Line;
  Ada.Text_IO.Put(Item => "SPLATT!!!");
  Ada.Text_IO.New_Line;

END Free_Fall;
Sample Run
Please enter all values in floating-point form.
Tower height in meters > 150
Time in seconds between table lines > 0.5


        Time      Height
       0.000     150.000
       0.500     148.774
       1.000     145.097
       1.500     138.968
       2.000     130.387
       2.500     119.354
       3.000     105.870
       3.500      89.934
       4.000      71.547
       4.500      50.708
       5.000      27.417
       5.500       1.674

SPLATT!!!

Exercises for Section 6.1

Self-Check

  1. What values would be printed if the order of the statements in the loop body of Progam 6.1 were reversed?
  2. What is the least number of times that the body of a WHILE loop may be executed?
  3. How would you modify the loop in Program 6.3 so that it also determines the number of moves (CountMoves) made by the worm before biting the apple? Which is the loop control variable, Distance or CountMoves?
  4. How many times is the loop body below repeated? What is printed during each repetition of the loop body?
    X := 3;
    Count := 0;
    WHILE Count < 3 LOOP
        X := X * X;
        Ada.Integer_Text_IO.Put(Item => X);
        Count := Count + 1;
    END LOOP;
    
  5. Answer Self-Check Exercise 4 if the last statement in the loop is
    Count := Count + 2;
    
  6. Answer Self-Check Exercise 4 if the last statement in the loop body is omitted.

Programming

  1. There are 9870 people in a town whose population increases by 10% each year. Write a loop that determines how many years (CountYears) it takes for the population to go over 30,000.
  2. Write a program that prints a table showing n and 2n while 2n is less than 10,000.


Previous | Next | Table of Contents | Index | Program List | Copyright

Copyright © 1996 by Addison-Wesley Publishing Company, Inc.