### Solution for

Programming Exercise 9.4

THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to
the following exercise from this on-line
Java textbook.

**Exercise 9.4:**
The file Expr.java defines
a class, `Expr`, that can be used to represent mathematical
expressions involving the variable `x`. The expression can
use the operators +, -, *, /, and ^, where ^ represents the operation
of raising a number to a power. It can use mathematical functions
such as `sin`, `cos`, `abs`, and `ln`.
See the source code file for full details. The `Expr` class
uses some advanced techniques which have not yet been covered in this
textbook. However, the interface is easy to understand. It contains
only a constructor and two public methods.

The constructor `new Expr(def)`
creates an `Expr` object defined by a given expression. The parameter,
`def`, is a string that contains the definition. For
example, `new Expr("x^2")` or `new Expr("sin(x)+3*x")`.
If the parameter in the constructor call does not represent a legal
expression, then the constructor throws an `IllegalArgumentException`.
The message in the exception describes the error.

If `func` is a variable of type `Expr` and
`num` is of type `double`, then
`func.value(num)` is a function
that returns the value of the expression when the number `num`
is substituted for the variable `x` in the expression.
For example, if `Expr` represents the expression `3*x+1`,
then `func.value(5)` is `3*5+1`, or 16.
If the expression is undefined for the specified value of `x`,
then the special value `Double.NaN` is returned.

Finally, `func.getDefinition()`
returns the definition of the expression. This is just the string
that was used in the constructor that created the expression object.

For this exercise, you should write a program that lets the
user enter an expression. If the expression contains an error,
print an error message. Otherwise, let the user enter some
numerical values for the variable `x`. Print the value
of the expression for each number that the user enters. However,
if the expression is undefined for the specified value of `x`,
print a message to that effect. You can use the `boolean`-valued
function `Double.isNaN(val)` to check whether a number,
`val`, is `Double.NaN`.

The user should be able to enter as many values of `x` as
desired. After that, the user should be able to enter a new expression.
Here is an applet that simulates my solution to this exercise, so that
you can see how it works:

**Discussion**

A pseudocode algorithm for the program is given by:

while (true):
Get a line of input from the user
if the line is empty:
break
Convert the input line to an Expr
Read and process the user's numbers

Converting the input line into an object of type `Expr`
involves calling the constructor from the `Expr` class. This
call might generate an `IllegalArgumentException`. The algorithm
must be expanded to handle this exception and print an error message if
it occurs. When an error occurs, I use a `continue` statement
to jump back to the start of the loop without reading any numbers from
the user:

while (true):
Get a line of input from the user
if the line is empty:
break
try {
Let expression = new Expr(line)
}
catch (IllegalArgumentException e) {
Print an error message
continue // jumps back to start of loop
}
Read and process the user's numbers

The last step, reading and processing the user's numbers, expands
into a loop, which is nested inside the main `while` loop.
In this loop, I could use `TextIO.getDouble()` to read one
of the user's numbers, but instead I choose to read the user's input
into a string and convert that string into a value of type
`double`. This has two advantages: I can end the loop when the
user presses return. And I can do nicer error handling than the default
error-handling that is provided by `TextIO`. The conversion from
a string, `line`, to a `double` is done using a method
that was introduced in Section 7.4. The conversion
will generate a `NumberFormatException` if the user's input is not
a legal number. The algorithm for reading and processing the user's
numbers becomes:

while (true):
Get a line of input from the user
if the line is empty:
break
try {
Let x = Double.parseDouble(line)
}
catch (NumberFormatException e) {
Print an error message
continue
}
Let val = expression.value(x)
if val is Double.NaN:
Print an error message
else:
Output val

All this can be easily translated into the complete solution, which follows.

**The Solution**

public class FunctionEvaluator {
public static void main(String[] args) {
String line; // A line of input read from the user.
Expr expression; // The definition of the function f(x).
double x; // A value of x for which f(x) is to be calculated.
double val; // The value of f(x) for the specified value of x.
TextIO.putln("This program will evaluate a specified function, f(x), at");
TextIO.putln("specified values of the variable x. The definition of f(x)");
TextIO.putln("can use the operators +, -, *, /, and ^ as well as mathematical");
TextIO.putln("functions such as sin, abs, and ln.");
while (true) {
/* Get the function from the user. A line of input is read and
used to construct an object of type Expr. If the input line is
empty, then the loop will end, and the program will terminate. */
TextIO.putln("\n\n\nEnter definition of f(x), or press return to quit.");
TextIO.put("\nf(x) = ");
line = TextIO.getln().trim();
if (line.length() == 0)
break;
try {
expression = new Expr(line);
}
catch (IllegalArgumentException e) {
// An error was found in the input. Print an error
// message and go back to the beginning of the loop.
TextIO.putln("Error! The definition of f(x) is not valid.");
TextIO.putln(e.getMessage());
continue;
}
/* Read values of x from the user, until the user presses return.
If the user's input is not a legal number, print an error message.
Otherwise, compute f(x) and print the result. */
TextIO.putln("\nEnter values of x where f(x) is to be evaluated.");
TextIO.putln("Press return to end.");
while (true) {
TextIO.put("\nx = ");
line = TextIO.getln().trim();
if (line.length() == 0)
break;
try {
x = Double.parseDouble(line);
}
catch (NumberFormatException e) {
TextIO.putln("\"" + line + "\" is not a legal number.");
continue;
}
val = expression.value(x);
if (Double.isNaN(val))
TextIO.putln("f(" + x + ") is undefined.");
else
TextIO.putln("f(" + x + ") = " + val);
} // end while
} // end while
TextIO.putln("\n\n\nOK. Bye for now.");
} // end main();
} // end class FunctionEvaluator

[ Exercises
| Chapter Index
| Main Index
]