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

5.2 Problem Solving: Generalizing a Solution

After you finish a program, someone will often ask a "What if?" question. The person asking the question usually wants to know whether the program would still work if some of the restrictions implied by the problem statement were removed. If the answer is "No," you may have to modify the program to make it work. Try to anticipate these questions in advance and make your programs as general as possible right from the start. Sometimes this can be as easy as changing a program constant to a problem input.

One question that comes to mind for the last problem is: What if we wanted to find the sum and average of a list of any numbers, not just the sum of the first N integers. Would the program still work? Clearly, the answer to this question is "No." However, it would not be too difficult to modify the program to solve this more general problem.


Case Study: General Sum Problem

PROBLEM SPECIFICATION

Write a program that finds and displays the sum of a list of numbers.

ANALYSIS

To add any list of numbers, a new variable (CurrentValue) would be needed to store each value to be summed. The numbers must be provided as input data. Because the numbers are not necessarily positive, we will make CurrentValue and Sum type Integer.

Data Requirements

Problem Inputs

number of items to be summed (NumValues : Natural)

temporary storage for each data value to be summed (CurrentValue: Integer)

Problem Outputs:

sum of the NumValues data values (Sum: Integer)

DESIGN

Initial Algorithm

1. Prompt the user for the number (NumValues) of values to be summed.

2. Prompt the user for each data value and add it to the sum.

3. Display the sum.

This algorithm is very similar to the earlier one. Step 2 is modified slightly and is refined below.

Algorithm Refinements

Step 2 Refinement

2.1. Initialize Sum to 0.

2.2. FOR each data value LOOP      

Read the data value into CurrentValue and add CurrentValue to Sum.

END LOOP;

In this refinement, the variable CurrentValue is used to store each number to be summed. After each number is read into CurrentValue, it is added to Sum. If there are more data items, the loop body is repeated and the next data item replaces the last one in CurrentValue. The number of data values to be summed is read into NumValue before the loop is reached. NumValues determines the number of loop repetitions that are required. A loop counter is needed to count the data items as they are processed and to ensure that all data are summed.

Program Variables

loop counter--the number of data items added so far (Count : Positive)

IMPLEMENTATION

The general program to find the sum of a list of data items is shown in Program 5.4. We leave the test plan and testing as an exercise.

Program 5.4
Finding the Sum of a Series of Data Items

WITH Ada.Text_IO;
WITH Ada.Integer_Text_IO;
PROCEDURE Sum_Items IS
------------------------------------------------------------------------
--| Finds and displays the sum of a list of data items.
--| Author: Michael B. Feldman, The George Washington University
--| Last Modified: July 1995
------------------------------------------------------------------------

  NumValues :    Natural;   -- input - the number of items to be added
  CurrentValue : Integer;   -- input - the next data item to be added
  Sum :          Integer;   -- output - the sum being accumulated

BEGIN  -- Sum_Items

  -- Read the number of items to be summed
  Ada.Text_IO.Put
    (Item => "Enter number of integer items to be summed > ");
  Ada.Integer_Text_IO.Get(Item => NumValues);
  Ada.Text_IO.New_Line;

  -- Read each data item and add it to Sum
  Sum := 0;
  FOR Count IN 1 .. NumValues LOOP
    Ada.Text_IO.Put(Item => "Integer item no. ");
    Ada.Integer_Text_IO.Put(Item => Count, Width => 1);
    Ada.Text_IO.Put(Item => " to be summed > ");
    Ada.Integer_Text_IO.Get(Item => CurrentValue);
    Sum := Sum + CurrentValue;
  END LOOP;

  -- Print the sum
  Ada.Text_IO.Put(Item => "The Sum is ");
  Ada.Integer_Text_IO.Put(Item => Sum, Width => 1);
  Ada.Text_IO.New_Line;

END Sum_Items;
Sample Run
Enter number of integer items to be summed > 6

Integer item no. 1 to be summed > 4
Integer item no. 2 to be summed > -7
Integer item no. 3 to be summed > 0
Integer item no. 4 to be summed > 24
Integer item no. 5 to be summed > -10
Integer item no. 6 to be summed > 1
The Sum is 12

We can further generalize this solution to find the minimum, maximum, and average of a list of data values--for example, the results of a class examination. The average is computed by finding the sum of all the values, then dividing by the number of values. From the previous example, we know how to find the sum. The minimum and maximum can be found at the same time, using our package Min_Max.


Case Study: Minimum, Maximum, and Average of a List of Numbers

PROBLEM SPECIFICATION

Write a program that finds and displays the minimum, maximum, and average of a list of integers.

ANALYSIS

This is quite similar to the previous problem. We can use the variables CurrentValue and Sum as above. As each value is read, it must be added into the sum and also compared against the current minimum, Smallest, and the current maximum, Largest. The comparisons can be handled by the Minimum and Maximum functions already provided in the MinMax package.

Because each new value, including the first, needs to be compared to Smallest and Largest, what initial values should these two variables have? It might be tempting to simply initialize them to zero, like the sum. This would be a mistake: Suppose that all the values to be read happened to be positive? The program would give incorrect results, since it would report that the smallest value was zero, instead of the really smallest value (which in this case would be greater than zero).

One way to solve this problem is to initialize Smallest to the largest possible integer value we will accept from the user. For now, we will just let this be the largest possible value of the type Integer. This way, any value the user could enter would automatically be no larger than this initial value. Luckily, Ada gives us an easy way to discover the largest possible value of Integer: It is an attribute called Integer'Last. (Notice the use of the apostrophe in the syntax.) This value is a large number whose actual value depends upon the compiler you are using. Because we also need to find the largest number, we should initialize Largest to the smallest possible Integer value, which Ada calls Integer'First.

Data Requirements

Problem Inputs

number of items to be averaged (NumValues : Positive)

temporary storage for each data value (CurrentValue: Integer)

Problem Outputs

minimum of the NumValues data values (Smallest: Integer)

largest of the NumValues data values (Largest: Integer)

average of the NumValues data values (Average: Integer)

Initial Algorithm

1. Prompt the user for the number (NumValues) of values to be summed.

2. Prompt the user for each data value; add it to the sum, check to see if it is a new minimum, and check to see if it is a new maximum.

3. Compute the average of the values.

4. Display the minimum, maximum, and average.

This algorithm is very similar to the earlier one. Step 2 is modified and is refined below; there is a new step 3.

Algorithm Refinements

Step 2 Refinement

2.1. Initialize Sum to 0, Smallest to Integer'Last, and Largest to Integer'First.

2.2. FOR each data value LOOP

      Read the data value into CurrentValue and add CurrentValue to Sum;

      determine whether the data value is a new minimum or maximum

END LOOP;

In this refinement, the variable CurrentValue is used to store each number to be summed. After each number is read into CurrentValue, it is added to Sum. If there are more data items, the loop body is repeated and the next data item replaces the last one in CurrentValue. The number of data values to be summed is read into NumValues before the loop is reached. NumValues determines the number of loop repetitions required. A loop counter is needed to count the data items as they are processed and ensure that all data are summed.

We need a further refinement of step 2.2:

Step 2.2 Refinement:

2.2 FOR each data value LOOP

     2.2.1 Read the data value into CurrentValue and add CurrentValue to Sum;

     2.2.2 Replace Smallest with the smaller of itself and CurrentValue;

     2.2.3 Replace Largest with the larger of itself and CurrentValue;

END LOOP;

Program Variables

loop counter--the number of data items added so far (Count : Natural)

IMPLEMENTATION

Program 5.5 shows the entire program. Note that this program finds the average as an integer value, by dividing Sum by NumValues. This is because all the numbers are integers and the division throws away the fractional part of the quotient. In Chapter 7 we will examine how to convert between integer and floating-point values.

Program 5.5
Finding Minimum, Maximum, and Average Values

WITH Ada.Text_IO;
WITH Ada.Integer_Text_IO;
WITH Min_Max;
PROCEDURE Min_Max_Average IS
------------------------------------------------------------------------
--| Finds and displays the minimum, maximum, and average
--| of a list of data items.
--| Author: Michael B. Feldman, The George Washington University
--| Last Modified: July 1995
------------------------------------------------------------------------

  NumValues:    Positive;  -- input - the number of items to be averaged
  CurrentValue: Integer;   -- the next data item to be added

  Sum:          Integer;   -- program variable - the sum being accumulated

  Smallest:     Integer;   -- output - minimum of the data values
  Largest:      Integer;   -- output - maximum of the data values
  Average:      Integer;   -- output - average of the data values

BEGIN  -- Min_Max_Average

  -- Read the number of items to be averaged
  Ada.Text_IO.Put(Item =>
    "Enter number (at least 1) of integer items to be averaged > ");
  Ada.Integer_Text_IO.Get(Item => NumValues);
  Ada.Text_IO.New_Line;

  -- Initialize program variables
  Smallest := Integer'Last;
  Largest := Integer'First;
  Sum := 0;

  -- Read each data item, add it to Sum,
  -- and check if it is a new minimum or maximum
  FOR Count IN 1 .. NumValues LOOP
    Ada.Text_IO.Put(Item => "Integer item no. ");
    Ada.Integer_Text_IO.Put(Item => Count, Width => 1);
    Ada.Text_IO.Put(Item => " > ");
    Ada.Integer_Text_IO.Get(Item => CurrentValue);

    Sum := Sum + CurrentValue;
    Smallest := 
      Min_Max.Minimum(Value1 => Smallest, Value2 => CurrentValue);
    Largest  := 
      Min_Max.Maximum(Value1 => Largest,  Value2 => CurrentValue);
  END LOOP;

  -- compute the average; since Sum and NumValues are integers,
  -- the average is truncated, that is, the fractional part is discarded

  Average := Sum / NumValues;

  -- Display the results
  Ada.Text_IO.Put(Item => "The Smallest is ");
  Ada.Integer_Text_IO.Put(Item => Smallest, Width => 1);
  Ada.Text_IO.New_Line;
  Ada.Text_IO.Put(Item => "The Largest is ");
  Ada.Integer_Text_IO.Put(Item => Largest, Width => 1);
  Ada.Text_IO.New_Line;
  Ada.Text_IO.Put(Item => "The Average is ");
  Ada.Integer_Text_IO.Put(Item => Average, Width => 1);
  Ada.Text_IO.New_Line;

END Min_Max_Average;
Sample Run
Enter number (at least 1) of integer items to be averaged > 7

Integer item no. 1 > -5
Integer item no. 2 > 2
Integer item no. 3 > 29
Integer item no. 4 > 16
Integer item no. 5 > 0
Integer item no. 6 > -17
Integer item no. 7 > 4
The Smallest is -17
The Largest is 29
The Average is 4

Using an External File for Input Data

A modification of Program 5.5 could use an external (disk) file for the input data. In fact, most real-world computer programs make heavy use of external files. The user prepares a file of data using an editor, then uses it later as input to the program. If the program is being developed and debugged, requiring several test runs, preparing the data this way saves having to enter them interactively each time the program is tested. We shall cover this topic more systematically in Chapters 8 and 9; for now, let's just consider how Program 5.5 would be changed to allow an external file for input.

The Get operations we have been working with all assume that input is coming interactively from the keyboard. In fact, each Get (for characters, strings, integers, floating-point quantities, and enumeration literals) has a second form requiring an additional parameter that names a disk file. For example, the input operation to read an integer value from a disk file called, say, TestScores, would be

Ada.Integer_Text_IO.Get(File => TestScores, Item => CurrentValue);

In general these operations look just like the interactive ones except for the file name. TestScores is an Ada variable, which must be declared as

TestScores: Ada.Text_IO.File_Type;

The type File_Type is provided by Ada.Text_IO.

Now suppose that the user prepared the input data with an editor and stored them in a disk file called scores.dat. The program needs a way to associate the name of the file in the program (TestScores in this case) with the name of the file as it is known to the operating system (scores.dat in this case). This is done by means of an operation called Ada.Text_IO.Open. In this case the operation would look like

Ada.Text_IO.Open(File => TestScores, Mode => Ada.Text_IO.In_File, Name
=> "scores.dat");

The parameter Mode indicates whether we are reading from the file (Text_IO.In_File, as in this example) or writing to it (Text_IO.Out_File). Notice also that the operating-system file name must appear in quotes.

It is important to type the name of the file exactly as it is listed in the directory you get from the operating system. Many operating systems use case-sensitive file names, which means that if the operating system file name is uppercase (e.g., SCORES.DAT), your parameter in the Open statement must also be uppercase (as in our example); if the operating system file name is in lowercase, your parameter must be also. If you supply to Open a file name that does not exist in your current directory, the Ada exception Name_Error will be raised.

Program 5.6 shows a modification of MinMaxAverage in which input values are read from a file instead of from the terminal keyboard. Notice that there are no prompts, because there is no interactive user entering the data. The file SCORES.DAT, created with an editor, will contain first the number of values to be read, then the actual values, one value per line. The program "logs," or displays on the terminal, the values as they are read from the file and processed; finally the results are displayed as before.

Program 5.6
Finding Minimum, Maximum, and Average Values, File Version

WITH Ada.Text_IO;
WITH Ada.Integer_Text_IO;
WITH Min_Max;
PROCEDURE Min_Max_Average_File IS
------------------------------------------------------------------------
--| Finds and displays the minimum, maximum, and average
--| of a list of data items; the data items are read from a file.
--| Author: Michael B. Feldman, The George Washington University 
--| Last Modified: July 1995                                   
------------------------------------------------------------------------

  NumValues:    Positive;  -- input - the number of items to be averaged
  CurrentValue: Integer;   -- input - the next data item to be added        

  Smallest:     Integer;   -- output - minimum of the data values
  Largest:      Integer;   -- output - maximum of the data values
  Average:      Integer;   -- output - average of the data values

  Sum:          Integer;   -- program variable - the sum being accumulated
  TestScores:   Ada.Text_IO.File_Type; 
                           -- program variable - names the input file

BEGIN  -- Min_Max_Average_File

  -- Open the file and associate it with the file variable name
  Ada.Text_IO.Open
    (File => TestScores, Mode => Ada.Text_IO.In_File, 
     Name => "scores.dat");

  -- Read from the file the number of items to be averaged
  Ada.Integer_Text_IO.Get(File => TestScores, Item => NumValues);
  Ada.Text_IO.Put("The number of scores to be averaged is ");
  Ada.Integer_Text_IO.Put(Item => NumValues, Width => 1);
  Ada.Text_IO.New_Line;

  -- Initialize program variables
  Smallest := Integer'Last;
  Largest := Integer'First;
  Sum := 0;

  -- Read each data item, log to the screen, add it to Sum,
  -- and check if it is a new minimum or maximum
  FOR Count IN 1 .. NumValues LOOP
    Ada.Integer_Text_IO.Get(File => TestScores, Item => CurrentValue);
    Ada.Text_IO.Put("Score number ");
    Ada.Integer_Text_IO.Put(Item => Count, Width => 1);
    Ada.Text_IO.Put(" is ");
    Ada.Integer_Text_IO.Put(Item => CurrentValue, Width => 1);
    Ada.Text_IO.New_Line;

    Sum := Sum + CurrentValue;
    Smallest := 
      Min_Max.Minimum(Value1 => Smallest, Value2 => CurrentValue);
    Largest  := 
      Min_Max.Maximum(Value1 => Largest,  Value2 => CurrentValue);
  END LOOP;

  -- compute the average; since Sum and NumValues are integers,
  -- the average is truncated, that is, the fractional part is discarded

  Average := Sum / NumValues;

  -- display the results
  Ada.Text_IO.Put(Item => "The Smallest is ");
  Ada.Integer_Text_IO.Put(Item => Smallest, Width => 1);
  Ada.Text_IO.New_Line;
  Ada.Text_IO.Put(Item => "The Largest is ");
  Ada.Integer_Text_IO.Put(Item => Largest, Width => 1);
  Ada.Text_IO.New_Line;
  Ada.Text_IO.Put(Item => "The Average is ");
  Ada.Integer_Text_IO.Put(Item => Average, Width => 1);
  Ada.Text_IO.New_Line;

END Min_Max_Average_File;
Sample Run
The number of scores to be averaged is 10
Score number 1 is 57
Score number 2 is 22
Score number 3 is 100
Score number 4 is 42
Score number 5 is 37
Score number 6 is 70
Score number 7 is 81
Score number 8 is 92
Score number 9 is 100
Score number 10 is 87
The Smallest is 22
The Largest is 100
The Average is 68


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

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