Section 6.7. Example: Computing Averages


[Page 272 (continued)]

6.7. Example: Computing Averages

Suppose youwant to compute the average of your exam grades in a course. Grades, represented as real numbers, will be input from the keyboard using our KeyboardReader class. To signify the end of the list, we will use a sentinel value9999 or -1 or some other value that won't be confused with a legitimate grade. Because we do not know exactly how many grades will be entered, we will use a noncounting loop in this algorithm. Because there could be no grades to average, we will use a while structure so that it will be possible to skip the loop entirely in the case that there are no grades to average.

Algorithm design: what kind of loop?


The algorithm should add each grade to a running total, keeping track of the number of grades entered. Thus, this algorithm requires two variables: one to keep track of the running total and the other to keep track of the count. Both should be initialized to 0. After the last grade has been entered, the total should be divided by the count to give the average. In pseudocode, the algorithm for this problem is as follows:

initialize runningTotal to 0           // Initialize initialize count to 0 prompt and read the first grade        // Priming read while the grade entered is not 9999 {  // Sentinel test     add it to the runningTotal     add 1 to the count     prompt and read the next grade     // Update } if (count > 0)                         // Guard against divide by 0     divide runningTotal by count output the average as the result 


Algorithm design



[Page 273]

Note that in this problem our loop variable, grade, is read before the loop test is made. This is known as a priming read. It is necessary in this case because the loop test depends on the value that is read. Within the loop's body, the updater reads the next value for grade. This is a standard convention for coding while structures that involve input, as this problem does. Note also that we must make sure that count is not 0 before we attempt to compute the average, because dividing by 0 would cause a divide-by-zero error.

Priming read


Translating the pseudocode algorithm into Java raises several issues. Suppose we store each grade that is input in a double variable named grade. The loop will terminate when grade equals 9999, so its entry condition will be (grade != 9999). Because this condition uses grade, it is crucial that the grade variable be initialized before the bound test is made. This requires a priming read. Reading the first value of grade before the loop-entry condition is tested ensures that the loop will be skipped if the user happens to enter the sentinel (9999) on the very first prompt. In addition to reading the first exam score, we must initialize the variables used for the running total and the counter. Thus, for our initialization step, we get the following code:

double runningTotal = 0; int count = 0; reader.prompt("Input a grade (e.g., 85.3) " +       "or 9999 to indicate the end of the list >> "); double grade = reader.getKeyboardDouble();              // Priming input 


Initialization step


Within the body of the loop we must add the grade to the running total and increment the counter. Since these variables are not tested in the loop-entry condition, they will not affect the loop control. Our loop updater in this case must read the next grade. Placing the updater statement at the end of the loop body will ensure that the loop terminates immediately after the user enters the sentinel value:

while (grade != 9999) {                                 // Loop test: sentinel   runningTotal += grade;   count++;   reader.prompt("Input a grade (e.g., 85.3) " +       "or 9999 to indicate the end of the list >> ");   grade = reader.getKeyboardDouble();                   // Update:input } // while 


Updater step


You can see that it is somewhat redundant to repeat the same statements needed to do the initializating and the updating of the grade variable. A better design would be to encapsulate these into a method and then call the method both before and within the loop. The method should take care of prompting the user, reading the input, converting it to double, and returning the input value. The method doesn't require a parameter:

private double promptAndRead() {   reader.prompt("Input a grade (e.g., 85.3) " +      "or 9999 to indicate the end of the list >> ");   double grade = reader.getKeyboardDouble();            // Confirm input   System.out.println("You input " + grade + "\n");   return grade; } 


Modularity



[Page 274]

Note that we have declared this as a private method. It will be used to help us perform our task but won't be available to other objects. Such private methods are frequently called helper methods.

This is a much more modular design. In addition to cutting down on redundancy in our code, it makes the program easier to maintain. For example, there is only one statement to change if we decide to change the prompt message. It also makes the program easier to debug. Input errors are now localized to the promptAndRead() method.

Effective Design: Modularity

Encapsulating code in a method is a good way to avoid redundancy in a program.


Debugging Tip: Localization

Encapsulating code in a method removes the need to have the same code at several locations in a program. By localizing the code in this way, you make it easier to modify and debug.


Another advantage of encapsulating the input task in a separate method is that it simplifies the task of calculating the average. This task should also be organized into a separate method:

public double inputAndAverageGrades() {   double runningTotal = 0;   int count = 0;   double grade = promptAndRead();   // Priming initializer   while (grade != 9999) {           // Loop test: sentinel     runningTotal += grade;     count++;     grade = promptAndRead();        // Update: get next grade   } // while   if (count > 0)                    // Guard against divide-by-zero     return runningTotal / count;    // Return the average   else     return 0;                       // Special (error) return value } 


Note that we have declared this as a public method. This will be the method you call to calculate your course average.

Because we have decomposed the problem into its subtasks, each subtask is short and simple, making it easier to read and understand. As we saw in the checkerboard example, the use of small, clearly focused methods is a desirable aspect of designing a program.

Method decomposition


The complete Average.java application is shown in Figure 6.10. Its overall design is similar to the application programs we designed in previous chapters. The only instance variable it uses is the KeyboardReader variable. The other variables are declared locally, within the methods. In this case, declaring them locally makes the algorithms easier to read.


[Page 275]

Figure 6.10. A program to compute average grade using a while structure.

import java.io.*; public class Average {                            // Console I/O   private KeyboardReader reader = new KeyboardReader();   private double promptAndRead() {     reader.prompt("Input a grade (e.g., 85.3) " +            "or 9999 to indicate the end of the list >> ");     double grade = reader.getKeyboardDouble();     System.out.println("You input " + grade + "\n"); // Confirm input     return grade;   }   public double inputAndAverageGrades() {     double runningTotal = 0;     int count = 0;     double grade = promptAndRead();               // Initialize: priming input     while (grade != 9999) {                       // Loop test: sentinel       runningTotal += grade;       count++;       grade = promptAndRead();                    // Update: get next grade     } // while     if (count > 0)                                // Guard against divide-by-zero       return runningTotal / count;                // Return the average     else       return 0;                                   // Special (error) return value  }  public static void main(String argv[]) {    System.out.println("This program calculates average grade.");    Average avg = new Average();    double average = avg.inputAndAverageGrades();    if (average == 0)                              // Error check       System.out.println("You didn't enter any grades.");    else       System.out.println("Your average is " + average);  } // main() } // Average class 

One final point about this program: note the care taken in the design of the user interface to explain the program to the user, prompt the user before a value is input, and confirm the user's input after the program has read it.

Effective Design: User Interface

Whenever you ask a user for input, the user should know why you are asking and what you are asking for. Prompts should be used for this purpose. It is also a good idea to confirm that the program has received the correct input.





Java, Java, Java(c) Object-Orienting Problem Solving
Java, Java, Java, Object-Oriented Problem Solving (3rd Edition)
ISBN: 0131474340
EAN: 2147483647
Year: 2005
Pages: 275

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net