[Instructions] [Search] [Current] [Syllabus] [Links] [Handouts] [Outlines] [Labs] [More Labs] [Assignments] [Quizzes] [Exams] [Examples] [Book] [Tutorial] [API]
Carl and Carla Caffeinated have decided to combine ideas that we've recently seen (or will see) and have built a program that averages values typed on the command line. Here's the class, Average, that they've come up with.
import SimpleOutput;
/**
* Compute the average of a list of numbers entered on the command
* line. For example, to compute the average of a, b, and c, you
* would write
* % java Average a b c
*
* @author Carla Caffeinated
* @author Carl Caffeinated
* @author Samuel A. Rebelsky
* @author YOUR NAME HERE
* @version 1.0 of September 1999
*/
public class Average {
public static void main(String[] args) {
int sum = 0; // The sum of all the values entered.
int count = 0; // The number of values entered.
SimpleOutput out = new SimpleOutput();
// Step through the values, adding each in turn. Skip those
// that aren't valid.
for (int i = 0; i < args.length; i = i+1) {
try {
sum = sum + Integer.parseInt(args[i]);
count = count + 1;
}
catch (NumberFormatException nfe) {
// Skip it.
i = i + 1;
}
} // for
// Print the average.
out.println("The average grade is: " + (sum/count));
} // main(String[])
} // class Average
In all their tests, the program seems to work fine
% ji Average 3 4 5 The average grade is: 4 % ji Average 5 4 3 The average grade is: 4 % ji Average 10 100 The average grade is: 55 % ji Average -10 20 The average grade is: 5 % ji Average -10 aardvark zebra 20 The average grade is: 5 % ji Average 10.5 9.5 10 The average grade is: 10
Unfortunately, they've made the mistake of turning in their program without enough testing. They get their program back with a D and the note that
I'm sure the average of 1, 1, and 3 is closer to 1.67 than 1. I'm also sure that the average of 0, 100.1, and 200.2 is not 0. Please think more about possible problems and errors.
The Caffeinateds return their code and, sure enough, when they try the examples, things work ``oddly''.
% ji Average 1 1 3 The average grade is: 1 % ji Average 0 100.1 200.2 The average grade is: 0
Your first step is to help the Caffeinateds deal with the errors that their instructor has identified.
All of the problems seem to have to do with instances in which
the inputs or outputs involve fractional parts. It is likely that
this is because the program use ints rather than
floats or doubles.
Repairs:
sum from an int to a
double.
double instead of an
int.
For this problem, you were to deal with all of the problems that the instructor observed. Some of you left some of the problems (e.g., dealing with doubles as in put) to part 2, which meant that you ignored the other issues in part 2. I didn't take off for you leaving you fix to part 2, but I did take off if you didn't handle other issues.
Part of the problem was to summarize the difficulties. If you neglected to summarize the difficulties, I took off 5 points.
Depending on how you dealt with the previous errors, there is at least one (and possibly more) logical errors in their program which will lead it to behave ``inappropriately''. Identify those errors and update the code so that it behaves correctly.
Problem: The program does not check if there are no numbers to average. If there are none, it reports that the average grade is ``NaN'' (not a number).
% ji Average The average grade is: NaN
Solution: Give the user a clearer error message.
if (count == 0) {
out.println("I need values to average.");
out.println("Usage:");
out.println(" java Average val1 val2 ... valn");
} // if (count == 0)
Here's a sample of the updated output.
% ji Average I need values to average. Usage: java Average val1 val2 ... valn
Problem: The user may not understand that non-numeric values are not counted. After all, what's wrong with entering ``five''?
Solution: Print out a note when you catch an exception. Here's some new sample output.
% ji Average 1 2 three Skipping 'three' [Values must be numbers in 'digit form'] The average grade is: 1.5
Problem: In spite of all the updates, the program still behaves incorrectly when invalid values are entered.
% ji Average zero 1 100 Skipping 'zero' [Values must be numbers in 'digit form'] The average grade is: 100.0
Similarly, n examples with multiple invalid values, we only get error messages some of the time. Here's one example that prints both error messages and one that does not.
% ji Average 5 one 2 ten Skipping 'one' [Values must be numbers in 'digit form'] Skipping 'ten' [Values must be numbers in 'digit form'] The average grade is: 5.0 ji Average one two 5 Skipping 'one' [Values must be numbers in 'digit form'] The average grade is: 5.0
Solution: A closer analysis of the examples suggests that one of the input values is being skipped whenever there's an error. This means that the error most-likely happens in the code for handling that error. What does that code do? We've just made it print an error message. Printing should not otherwise affect the program. But there's also the line
i = i + 1;
Do we need that line? Each time through the loop, iis
incremented. This seems to do an extra increment. Not a
good idea. Drop the line. Here's the new output.
% ji Average one two 5 Skipping 'one' [Values must be numbers in 'digit form'] Skipping 'two' [Values must be numbers in 'digit form'] The average grade is: 5.0 % ji Average one 2 three 4 ji Average one 2 three 4 Skipping 'one' [Values must be numbers in 'digit form'] Skipping 'three' [Values must be numbers in 'digit form'] The average grade is: 3.0
We're basically done. Here's the final version of my program.
import SimpleOutput;
/**
* Compute the average of a list of numbers entered on the command
* line. For example, to compute the average of a, b, and c, you
* would write
* % java Average a b c
*
* This is a modified version of the program found on the Web at
* http://www.math.grin.edu/~rebelsky/Courses/CS152/99F/Examples/Exam1/Average.java
* All modifications are listed below.
*
* @author Carla Caffeinated
* @author Carl Caffeinated
* @author Samuel A. Rebelsky
* @version 1.0 of September 1999
*/
public class Average {
public static void main(String[] args) {
// The following line was modified by Sam Rebelsky on
// Friday, September 24, 1999 as part of answer A.1.
double sum = 0; // The sum of all the values entered.
int count = 0; // The number of values entered.
SimpleOutput out = new SimpleOutput();
// Step through the values, adding each in turn. Skip those
// that aren't valid.
for (int i = 0; i < args.length; i = i+1) {
try {
// The following line was modified by Sam Rebelsky on
// Friday, September 24, 1999 as part of answer A.1.
sum = sum + (new Double(args[i]).doubleValue());
count = count + 1;
// The next few lines were added by Sam Rebelsky on
// Friday, 24 September 1999, as part of an attempt to
// understand difficulties in the program.
}
catch (NumberFormatException nfe) {
// The next two statements were added by Sam Rebelsky on
// Friday, 24 September 1999 to give the user more
// information on values that are skipped.
out.println("Skipping '" + args[i] + "'");
out.println(" [Values must be numbers in 'digit form']");
// The code that was here was removed by Sam Rebelsky on
// Friday, 24 September 1999 to correct a logical error.
}
} // for
// Print the average (assuming that it can be computed).
// Except as mentioned below, the following lines were
// added by Sam Rebelsky on 24 September 1999 in response
// to problem A.2.
if (count != 0) {
// Original line
out.println("The average grade is: " + (sum/count));
}
// You can't compute an average of 0 values. Tell the user.
else {
out.println("I need values to average.");
out.println("Usage:");
out.println(" java Average val1 val2 ... valn");
} // No values entered
// End of additions from 24 September 1999
} // main(String[])
} // class Average
As the discussion above suggests, the line that reads
i = i+1;
is incorrect. The best solution is to remove it. If you did not remove it, your program has serious errors, as you will discover if you try any of the following:
% ji Average hello 1 10 20 % ji Average hello 5 % ji Average one two three
This is a serious enough problem that I took off 7 points if you failed to identify and correct it.
The incorrect way to solve this problem is to change the line to read
i = i;
Why is this incorrect? Because the line is essentially meaningless. It says ``keep i the same'' and will only serve to confuse the reader. I took off 2 points if you left this odd line.
We'd talked about potential troubles in average computations earlier (in our discussion of exceptions) and we'd all noted that it is an error to have no valudes to average. Your program should have printed a useful error message. I took off 5 points if you didn't print an error message on no input.
I expected that you would observe that your users would not necessarily know when their input was being skipped. Hence, you should have printed an error message when you caught the exception. If you failed to do so, I took off 2 points.
A few of you were able to identify these errors, but not to fix them. I took off 5 points in such cases.
Since I emphasized formatting of code on problem C, I expected that you would be careful on your formatting on problem A. Some of you weren't and I took off about 2 points.
As you may know, the registrar's office recently upgraded to a new system. Suppose that the registrar had instead decided to hire us to build the new system. We would, of course, begin that task by identifying important objects in the system.
We went over some issues in class. I'm not going to take the time to include a detailed sample solution.
List the fields and methods you might expect to find in the
Student class. Don't worry about implementing the
methods; just summarize their purpose. If you use another class as the
type of a field, make sure that you describe that class in your answer
to B.2.
Course objects (or something similar), I took off
3 points.
String is a reasonable default, you shouldn't
use it for everything.
double is nice for many kinds of numbers, it
is an inexact representation and should not be used for
things that need to be represented exactly (such as phone numbers).
What are the other core data classes you might expect to find in a
registration system? Don't worry about the objects involved in the
interface. Rather, consider the underlying objects that store the
important information, such as Student. For each class,
list the name and a summary of its purpose and content.
Write a short narrative describing the objects and methods that might be involved in the operation ``Print T. Smith's Transcript''. Your narrative might begin with something like the following.
After connecting to the system and entering account and password, the registrar finds the object,
current, in classStudentthat corresponds to T. Smith. The registrar then callscurrent.toString()to generate a printable version of the transcript. ThetoString()method first callsgetGraduationYear()(also in classStudent) ...
Our friends the Caffeinateds have realized that they don't know how to
indent and comment code. Please turn their Book.java into
something that is more readable. You should certainly add comments, fix
the badly formatted code, add whitespace where appropriate, and
reorganize the code so that similar things are near each other. Your
goal is to create a class that is likely to meet my high standards of
indentation, organization, uniformity, and commenting. Use the
existing code (which does work); do not rewrite from scratch.
public class Book {
public int getNumChapters() {
return this.numChapters;
}
public Book() {
this.chapters = new String[20];
this.numChapters = 0;
}
public String getTitle() {
return this.title;
}
protected String title;
// Needs significant formatting help!
public String getAuthor() { return
this.author; } protected String author; public String
toString() { String
stuff = "\"" +
this.title + "\" by "
+ this.author + "\n";
for (
int i = 0;
i < numChapters; ++i) { if (chapters[i] != null) {
stuff = stuff + " " + (i+1) + ": " + chapters[i] + "\n";
}
}
return stuff; } int numChapters;
public Book(int numChapters) {
this.chapters = new String[numChapters];
this.numChapters = 0;
}
public Book(String author, String title) {
this.author = author;
this.title = title;
this.chapters = new String[20];
this.numChapters = 0;
}
protected String[] chapters;
public void addChapter(String chapterName)
throws Exception
{
if (numChapters >= chapters.length) {
throw new Exception();
}
chapters[numChapters] = chapterName;
numChapters = numChapters + 1;
}
// Needs more help
public String
getChapter(int chapterNum)
throws Exception { chapterNum=chapterNum -
1; if (chapterNum >= numChapters)
{ throw new Exception(); } if
(chapterNum < 0) { throw new Exception(); }
if
(chapters[chapterNum] == null) {
throw new Exception(); }
return chapters[chapterNum]; } }
You may find it helpful to use the following test program.
import SimpleOutput;
/**
* Test our sample book class. Does not yet test all issues
* (such as zero-parameter constructors).
*
* @author Samuel A. Rebelsky
* @version 1.0 of September 1999
*/
public class BookTester {
public static void main(String[] args)
throws Exception
{
SimpleOutput out = new SimpleOutput();
Book eij = new Book("Sam Rebelsky", "Experiments in Java");
// Print the basic information.
out.println("* Test: Print basic information");
out.print(eij.toString());
// Set two chapters.
out.println("\n*Test: Set two chapters");
eij.addChapter("J1: An Introduction to Java");
eij.addChapter("J2: Objects and Classes");
out.print(eij.toString());
// Get an valid chapter.
out.println("\n*Test: Get valid chapter");
out.println("Chapter 2: " + eij.getChapter(2));
// Get an invalid chapter.
out.println("\n*Test: Get invalid chapter");
try {
out.println("Chapter 4: " + eij.getChapter(4));
}
catch (Exception e) {
out.println("As expected, that failed.");
}
// Add too many chapters
out.println();
for (int i = eij.getNumChapters(); i < 25; ++i) {
try {
out.print("*Test: Adding chapter " + i + "...");
eij.addChapter("Untitled");
out.println("okay");
}
catch (Exception e) {
out.println("failed");
} // caught "too many chapters" exception
} // add a lot of chapters
// And see what we have
out.println("\n*Final version:");
out.println(eij.toString());
} // main(String[])
} // class BookTester
Here's the solution that I came up with. Yours may be more detailed.
/**
* A simple, <Q>write-once</Q> representation of books. Once the
* author, title, and chapters are in place, they cannot be easily
* changed (except by directly accessing the fields).
* Note that rather than supporting a
* setChapter(int n, String * chapterName)
* method, which some clients might misuse by setting the 5th chapter
* but not the prior chapters, we have decided to limit clients to
* adding chapters to the end of the book. This lets client build but
* not change the table of contents.
*
* The maximum number of chapters is 20. This is a bad design. It
* would be better to allow the consturctor to take the number of
* potential chapters as a parember.
*
* @author Carl Caffeinated
* @author Carla Ceffeinated
* @author Samuel A. Rebelsky
* @version 1.0 of 24 September 1999
*/
public class Book {
// +--------+--------------------------------------------------
// | Fields |
// +--------+
/** The title of work. */
protected String title;
/** The author of the book. */
protected String author;
/**
* The number of chapters currently stored in the work.
* Note that this is always 1 higher than the index of
* the highest-numbered chapter in the chapters array.
*/
protected int numChapters;
/**
* The chapters of the book (or, more precisely, the names of
* the chapters of the book.
*/
protected String[] chapters;
// +--------------+--------------------------------------------
// | Constructors |
// +--------------+
/** Create a new book that supports up to twenty chapters. */
public Book() {
this.chapters = new String[20];
this.numChapters = 0;
} // Book()
/** Create a new book that supports up to numChapters chapters. */
public Book(int numChapters) {
this.chapters = new String[numChapters];
this.numChapters = 0;
} // Book(int)
/**
* Create a new book with a specified author and title and up
* to twenty chapters.
*/
public Book(String author, String title) {
this.author = author;
this.title = title;
this.chapters = new String[20];
this.numChapters = 0;
} // Book(String,String)
// +-----------+-----------------------------------------------
// | Accessors |
// +-----------+
/** Get the author of the book. */
public String getAuthor() {
return this.author;
} // getAuthor()
/**
* Get the title of chapter chapterNum.
*
* @exception Exception
* If there is no such chapter.
*/
public String getChapter(int chapterNum)
throws Exception
{
// Move from "human" indexing (starting at 1) to Java
// indexing (starting at 0).
chapterNum=chapterNum - 1;
// Verify that the chapter number is valid.
// a. It must be <= numChapters-1. See above for more info.
if (chapterNum >= numChapters) {
throw new Exception();
} // if it's too large
// b. It must be >= 0 since the array indices start with 0.
if (chapterNum < 0) {
throw new Exception();
} // if it's too small
// c. The chapter must be there. This test is not strictly
// necessary, as we've carefully worked to ensure that the
// chapters between 0 and numChapter-1 (Java numbering) are
// all defined. However, we may later change that decision
// and it seems safest to be prepared for all situations.
if (chapters[chapterNum] == null) {
throw new Exception();
}
// That's it, just return the correct element.
return chapters[chapterNum];
} // getChapter(int)
/** Determine the number of chapters in the book. */
public int getNumChapters() {
return this.numChapters;
} // getNumChapters()
/** Get the title of the book. */
public String getTitle() {
return this.title;
} // getTitle()
/** Convert the book to a string in preparation for printing. */
public String toString()
{
// Add the author and title. The title is in quotes.
// Coding note: in Java, \" is how you get a quote in the
// middle of a string.
String stuff = "\"" + this.title + "\" by " + this.author + "\n";
// Step through the chapters, adding each as you go. Since
// most people start their numbering at 1 rather than 0, we
// add 1 to the index into the chapters array.
for (int i = 0; i < numChapters; ++i) {
if (chapters[i] != null) {
stuff = stuff + " " + (i+1) + ": " + chapters[i] + "\n";
} // if the chapter is not null
} // for
// That's it, we're done.
return stuff;
} // toString()
// +-----------+-----------------------------------------------
// | Modifiers |
// +-----------+
/**
* Add a new chapter to the end of the table of contents.
* See introductory notes for the rationale behind this.
*
* @exception Exception
* If there's not room for another chapter.
*/
public void addChapter(String chapterName)
throws Exception
{
// If there is not room for another chapter, throw an exception.
if (numChapters >= chapters.length) {
throw new Exception();
}
// Add the chapter.
// Coding note: Since we start indexing the array at 0, the
// number of chapters is 1 higher than the largest index used
// so far. Hence, we can use it as the index of the first free
// element.
chapters[numChapters] = chapterName;
// Move on to the next chapter.
numChapters = numChapters + 1;
} // addChapter(String)
} // class Book
The designers of this class had a particularly interesting design strategy with regards to chapters. That is, rather than allowing clients to change chapter titles, they only allow authors to add chapter titles. It might be useful to document the possible motivatoin for such a design.
It's fairly obvious when things are fields and methods. So, in your introductory comment you don't need to say ``A class that ...'', ``A field for ...'', or a ``A method that ...''. I didn't take off for this, but expect you to be more careful about it in the future.
A few of you claimed that toString ``prints'' something.
It doesn't. It generates a string that might later be printed (or
written to a file, or modified, or ...).
Two of the constructors limited the number of chapters in the book to twenty. It is necessary to document such limits. If you didn't, I took off 2 points.
Two of the methods (getChapter and
addChapter) had some fairly confusing code. If you
didn't comment that code, I took off 5 points.
Both of those methods threw exceptions. If you didn't indicate when the method threw an exception, I took off 2 points.
You updated the code and therefore belong as an author. If you didn't include your name, I took off 1 point.
As I've mentioned many times, it's important that you comment end braces. If you didn't, I took off 3 points.
There's a weird "\"" in the code. You should have tried
to explain it. If you didn't, I didn't take off anything, since that was
particularly confusing.
Friday, 24 September 1999
Monday, 27 September 1999
Wednesday, 29 September 1999
[Instructions] [Search] [Current] [Syllabus] [Links] [Handouts] [Outlines] [Labs] [More Labs] [Assignments] [Quizzes] [Exams] [Examples] [Book] [Tutorial] [API]
Disclaimer Often, these pages were created "on the fly" with little, if any, proofreading. Any or all of the information on the pages may be incorrect. Please contact me if you notice errors.
This page may be found at http://www.math.grin.edu/~rebelsky/Courses/CS152/99F/Exams/examsoln.01.html
Source text last modified Wed Sep 29 08:44:30 1999.
This page generated on Sun Oct 17 20:55:59 1999 by Siteweaver. Validate this page's HTML.
Contact our webmaster at rebelsky@grinnell.edu