> ### 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 ]