public class ExprEvaluator
extends java.lang.Object
The ExprEvaluator class is designed to be used in applications and applets when it is desired to allow the user to type in a mathematical expression and have the program calculate its values.
A two step process is used. Given a mathematical expression, a "build"
method creates a binary expression tree representing the expression. It may
be useful to think of the expression tree as the compiled version of the
expression. Then
an evaluate method is used to evaluate the binary expression tree. For
example, the following code could be used in a simple situation to evaluate
expression using the variables t, x, and y:
ExprEvaluator evaluator = new ExprEvaluator( );
double t = ...
double x = ...
double y = ...
String expression = ...
try {
evaluator.buildFromInfix(expression);
double value = evaluator.evaluate(t, x, y);
} catch (Exception e) {
...
}
All calculations are carried out with double precision real numbers.
If you are interested in handling multiple expressions, buildFromInfix returns the expression tree (the "compiled" version of the expression). This allows handling multiple expressions. See the "Evaluating multiple infix expressions" section below.
The class provides "build" methods that can handle prefix, infix and postfix expressions. Those expressions may contain the following:
There are separate build methods for prefix, infix, and postfix expressions. (If you are not familiar with these terms, we normally write mathematical expressions in infix. Postfix is sometimes called reverse Polish notation and is used by HP calculators and is convenient for computer work. You don't need to know how to use prefix and postfix to use this class. You will probably only want to use infix.) The following table show the equivalent prefix, infix, and postfix for some expressions emphasizing notation for functions and "=" signs.
| Prefix | Infix | PostFix |
|---|---|---|
| / + x y - t 7 | (x+y)/(t-7) | x y + t 7 - / |
| = x 4 | x = 4 | x 4 = |
| = a = b = c * x y | a = b = c = x * y | a b c x y * = = = |
| sin x | sin(x) | x sin |
| abs - x y | abs(x - y) | x y - abs |
| max x y | max(x, y) | x y max |
The "=" operator is optional and can be used in multiple assignments. For example: Both x + y and z = x + y (or + x y and = z + x y in prefix and x y + and z x y + = in postfix) return the same value. They differ in second expression also sets the value of z. The expression a = b = c = 3*x (or = a = b = c * 3 x in prefix and a b c 3 x * = = = in postfix) sets the values of a, b and c in addition to returning the value of 3*x (* 3 x in prefix).
Order of evaluation for infix expressions (the same as standard algebra and Visual Basic)
| ( ) | Parenthesized expressions (highest priority) |
| functions | |
| ^ | exponentials (right associative: 2^3^4 = 2^(3^4) ) |
| leading + - | (as in -5) |
| * / | |
| binary + - | (as in 4 - 3) |
| = | assignment (right associative: value on right is assigned to variable on left)) (lowest priority) |
To simplify using ExprEvaluator, it "remembers" the last expression tree
created by a build method. That expression tree is the default for
the evaluate methods. Depending on the variables used, there are three
general ways of evaluating expressions after the tree is built:
Only the "common" variables t, x, and y are used:
Use double evaluate(double t, double x, double y).
Example: evaluator.evaluate(10, 20, 30);.
See the above coding example or
TrivialExample.java.
Only a few variables are used but they include "uncommon" variables:
Use setValueOf(char var, double value) as needed to set values
of variables
and then use evaluate( ). For example, if the expression uses variables
a and b:
evaluator.setValueOf('a', 10);
evaluator.setValueOf('b', 20);
try {
result = evaluator.evaluate( );
} catch (Expression e) {
...
}
See TrivialExample3.java.
Many variables with values stored in an array:
Use evaluate(double[] values).
Example:
double [] variables = new double[26];
...
result = root.evaluate(variables);
See TrivialExample2.java.
Notes:
These techniques are not mutually exclusive. Internally, the same
array is used to store variable values and is independent of the
evaluate method is used. For example: Consider the sequence
evaluator.evaluate(10, 20, 30);
evaluator.evaluate( );
The second evaluate would use the values of t, x, and y resulting from
the first evaluation. Typically, they would be t = 10, x = 20, and y = 30
but it is possible that those values were changed during the course of the
first call to evaluate.
As another example, suppose that the expression is function that must be
evaluated for many (x, y) pairs but depends on "constants" a and b. We could
proceed as follows:
evaluator.setValueOf('a', 20);
evaluator.setValueOf('b', 30);
... inside a loop ...
xval = ...;
yval = ...;
value = evaluator.evaluate(0, xVal, yVal); // t is not used
Evaluating multiple infix expressions
ExprEvaluator has special methods that are useful when processing multiple infix expressions.
Evaluating functions for 2-D graph or table typically has one of the following levels of difficulty:
| Type of problem | Comment |
|---|---|
| Evaluate or plot f(x) | The buildFromInfix and zero parameter evaluate method are completely capable of handling this situation. See TableExample1.java Alternatively, one may choose to use the buildFromInfix and evaluate(ExprTree) methods as suggested in the next item. |
| Evaluate or plot multiple functions of x | Assuming exprStr is string with the expression and valX is a double, use
ExprTree expr = buildFromInfix(exprStr); setX(valX); double val = evaluate(expr);. See TableExample2.java |
| Use parametric functions for x and y. (There may be multiple sets of expressions.) |
Use the technique from the previous example except use the buildFromInfix to build a tree for each x and y expression, set the value of the parameter, and then use evaluate for each of the expression trees to get the x and y values. See TableExample3.java |
| Use polar coordinates. (There may be multiple sets of expressions.) |
ExprEvaluator provides a special method, convertToRect(r, theta), to convert polar coordinates to rectangular coordinates that may be needed to graph polar functions. If there is only 1 expression to be evaluated, then the buildFromInfix, setValueOf, and the zero parameter evaluate methods along with the convertToRect method are completely capable of handling this situation. However, if there is more than one expression to be evaluated, you should use buildFromInfix for each expression, set the values of the parameter(s) and then use evaluate(ExprTree) methods for each expression being evaluated. See TableExample4.java |
While these techniques are designed for producing 2-D graphs, they may also be useful in other situations where it is desired to evaluate multiple functions. In fact, all the above examples just produce tables that could be used for plotting but there could be other uses as well.
The general code format when using multiple infix functions:
ExprEvaluator evaluator = new ExprEvaluator();
double x;
double firstX = ...;
double lastX = ...;
double incX = ...;
double y1, y2;
ExprTree tree1, tree2;
String expr1 = ...;
String expr2 = ...;
try {
tree1 = evaluator.buildFromInfix(expr1);
tree2 = evaluator.buildFromInfix(expr2);
for (x = firstX; x < lastX; x += incX) {
evaluator.setX(x);
y1 = evaluator.evaluate(tree1);
y2 = evaluator.evaluate(tree2);
...
}
} catch (Exception e) {
...
}
The ExprEvaluator can evaluate 34 functions, including 32 functions defined in Java's Math class, one unique function inspired by Excel (roundTo(val, num)) and a user defined function. You can search Java documentation for definitions of functions that you are unfamiliar with. The functions that can be used include the following:
The buildFromInfix method allow the user to define
a function with 0 to 10 parameters. Because of notational difficulties,
the buildFromPrefix and buildFromPostfix methods only allow defining 1
parameter functions. The formats for the string defining
the function are
prefix: define = user parameter prefixExpression
infix: define user(parameter list) = infixExpression
postfix: define parameter user postfixExpression =
For example using infix:
try {
evaluator.buildFromInfix("define user(a, b, x) = x * max(a, b)");
evaluator.setX(10);
evaluator.setY(20);
evaluator.setValueOf('z', 30);
evaluator.buildFromInfix("100000 + user(x, y, z)");
double value = evaluator.evaluate();
} catch (...) {
...
}
The following examples compare defining a function in prefix, infix,
and postfix:
When a build method is used to define a function, the resulting tree is null and it returns null. The parameters follow the same notational rule as variables: one lower case letter. Once defined, the number of parameters is fixed (until user is redefined). The expression used in defining a function can use parameters, constants and any other variables and functions.
When the user function is defined using buildFromPrefix or buildFromPostfix, it must have exactly 1 parameter. However, once defined, the user function can be used in prefix, infix and postfix expressions no matter which of the build functions was used to define it. This is true independently of the number of parameters the function has. (See TrivialExample4.java.)
The roundTo(x, num) function was inspired by the fact that Java often prints doubles with many decimal places. For example, we might see something like 1.5999999999 or 1.600000003 as approximations for 1.6 after a series of calculations. The function was modeled after the ROUND(number, num_digits) function in Excel. 'num' is the number of decimal places to which x is rounded. If num < 0, then the number is rounded to the left of the decimal point. For example, roundTo(12345.6,-3) = 12000.
The roundTo function is available both when running ExprEvaluator and as a public static function in the EasyFormat class.
The toPrefix(), toInfix(U) and toPostfix() methods allow converting from one "..fix" to another after using any of the build... methods. toInfix() provides fully parenthesized output. (I.e. typically there are many unneeded pairs of parenthesis. For Example. x+y*z will be output as (x+(y*z)) .)
Illegal calculations such as 1/0, log(-1) or sqrt(-1) will typically result in infinity, -infinity, or NaN as is normal in Java.
Beta 3: Modified for more efficient evaluations by adding fields to the binary nodes to avoid having to repeatedly check for values, types, and so on. Also ignores leading (unary) + signs.
Beta 4.0: Modified to allow variables a, b, ..., z.
Beta 4.1: Added the public static function roundTo
Allow = sign as a right associative operator.
Added the function roundTo(x, numDecPlaces)
Changed the name of the class for ExpressionTree to ExprEvaluator
Implemented buildFromPrefix.
Beta 4.2: Added help messages for infix function and used EasyFormat.roundTo.
Beta 4.3: Added methods that were previously in the subclass GraphEvaluator2D.
Beta 4.4: Added many new functions.
Beta 4.5: Added user defined function (and report error method), added help for prefix and postfix.
Beta 5.0: Implemented the toInfix() method and added TrivialExample5 to test it.
Beta 6.0: Added roundTo(x, num) as a public static method so it can be used in ap or applet similar to the way Math.sqrt is used. It is actually the same as EasyFormat.roundTo(x, num).
| Modifier and Type | Field and Description |
|---|---|
java.lang.String |
DELIMITERS
The allowed delimiters (operators).
|
static java.lang.String |
infixHelp
## infixHelp can be used in a help message that explains to the user
what is allowed as input to the ExprEvaluator when infix expressions
are used.
|
static java.lang.String |
introHelp
## introHelp can be used as the first part of a help message in
applications using ExprEvaluator.
|
static double |
PI
## The number PI = 3.14159...
|
static java.lang.String |
postfixHelp
## postfixHelp can be used in a help message that explains to the user
what is allowed as input to the ExprEvaluator when postfix expressions
are used.
|
static java.lang.String |
prefixHelp
## prefixHelp can be used in a help message that explains to the user
what is allowed as input to the ExprEvaluator when prefix expressions
are used.
|
static java.lang.String |
varHelp
## varHelp can be used as the middle part of a help message in those
situations where all 26 variables can be used.
|
static java.lang.String |
variables
A String holding the 26 variables "a" to "z" allowed in Expression tree and
10 parameters allowed in the user function.
|
| Constructor and Description |
|---|
ExprEvaluator()
## Initializes an ExprEvaluator object with an empty expression tree.
|
| Modifier and Type | Method and Description |
|---|---|
ExprTree |
buildFromInfix(java.lang.String infix)
## Builds an expression tree given an infix string of tokens.
|
ExprTree |
buildFromPostfix(java.lang.String postfix)
## Builds an expression tree given a postfix string of tokens.
|
ExprTree |
buildFromPrefix(java.lang.String prefix)
## Builds an expression tree given a prefix string of tokens.
|
ExprTree |
buildTree(java.lang.String s)
Deprecated.
Starting with beta 4.5, buildFromInfix returns the tree and
should be used instead of buildTree.
|
static int |
comparePrecedence(java.lang.String op1,
java.lang.String op2)
Compares precedence of two operators.
|
void |
convertToRect(double r,
double theta)
## Converts polar coordinates into rectangular coordinates.
|
void |
debugMode(boolean debugging)
Sets the debug mode in buildFromInfix.
|
void |
displayStacks(boolean debug)
Sets the display stack mode in buildFromInfix.
|
double |
evaluate()
## Returns the value of a binary expression tree previously created by
one of the build methods using the current values of the variables.
|
double |
evaluate(double[] params)
## Returns the value of a binary expression tree previously created by
one of the build methods using the supplied parameters.
|
double |
evaluate(double t,
double x,
double y)
## Returns the value of a binary expression tree previously created by
one of the build methods using the supplied values of t, x, and y and the
current values of the other variables.
|
double |
evaluate(ExprTree tree)
## Evaluates the specified tree using the current values for the
variables 'a', 'b', ..., 'z'.
|
java.lang.String |
getInputStr()
## Returns the last input string processed by buildFromPrefix, buildFromInfix,
or buildFromPostfix methods.
|
double |
getT()
## Gets the value of t.
|
java.lang.String |
getUserFunction()
## Returns the current user function as a string.
|
double |
getValueOf(char var)
## Gets the value of the variable var where var is 'a', 'b', ..., or 'z'.
|
double[] |
getValues()
## Returns the array of 26 doubles corresponding to the the variables
"a", "b", ..., or "z".
|
double |
getX()
## Gets the value of x.
|
double |
getY()
## Gets the value of y.
|
static boolean |
isDouble(java.lang.String s)
Determines if the string is an double.
|
static int |
isFunction(java.lang.String s)
Returns a number >= 0 if the string s is a function name.
|
boolean |
isOperator(java.lang.String s)
Determines if the string is an operator.
|
static int |
numFuncParams(java.lang.String s)
Returns the number of parameters if the string is a function name.
|
static int |
precedence(java.lang.String operator)
Determines the precedence of an operator.
|
static double |
roundTo(double x,
double num)
## roundTo(x, num) rounds x to num decimal places where num less
than 0 is permitted.
|
void |
setT(double value)
## Sets the value of t.
|
void |
setValueOf(char var,
double value)
## Sets the value of the specified variable ('a', 'b', ..., or 'z') to
the specified value.
|
void |
setValues(double[] values)
## Sets the value of all 26 variables.
|
void |
setX(double value)
## Sets the value of x.
|
void |
setY(double value)
## Sets the value of y.
|
java.lang.String |
toInfix()
## Creates a fully parenthesized infix expression String.
|
java.lang.String |
toPostfix()
## Creates a postfix expression from the expression tree.
|
java.lang.String |
toPrefix()
## Creates a prefix expression from the expression tree.
|
java.lang.String |
toString()
Output the tree structure -- used in testing/debugging.
|
public static double PI
public static final java.lang.String variables
public java.lang.String DELIMITERS
public static java.lang.String introHelp
ExprEvaluator.introHelp + ExprEvaluator.varHelp
+ ExprEvaluator.infixHelpvarHelp,
prefixHelp,
infixHelp,
postfixHelppublic static java.lang.String varHelp
introHelp,
prefixHelp,
infixHelp,
postfixHelppublic static java.lang.String prefixHelp
ExprEvaluator.introHelp + ExprEvaluator.varHelp
+ ExprEvaluator.prefixHelppublic static java.lang.String infixHelp
ExprEvaluator.introHelp + ExprEvaluator.varHelp
+ ExprEvaluator.infixHelppublic static java.lang.String postfixHelp
ExprEvaluator.introHelp + ExprEvaluator.varHelp
+ ExprEvaluator.postHelppublic ExprEvaluator()
public void debugMode(boolean debugging)
debugging - - sets the debug mode (true turns the mode on).public void displayStacks(boolean debug)
debug - - sets the display stack mode (true turns the mode on.public double getX()
setX(double)public void setX(double value)
value - - the value of variable 'x'getX()public double getY()
setY(double)public void setY(double value)
value - - the value of variable 'y'getY()public double getT()
setT(double)public void setT(double value)
value - - the value of variable 't'getT()public double getValueOf(char var)
var - - the variable name as a character.setValueOf(char, double),
getX(),
getY(),
getT()public void setValueOf(char var,
double value)
var - - a character in the range 'a' to 'z'. The name of one of
the 26 variables.value - - the value to which the variable is set.getValueOf(char),
setX(double),
setY(double),
setT(double)public double[] getValues()
setValues(double[])public void setValues(double[] values)
values - - an array of doubles with the 26 variable values. If the
array has less than 26 values, only corresponding values are set. If
array has more than 26 values, only the first 26 are used.getValues()public java.lang.String getInputStr()
public java.lang.String getUserFunction()
public static boolean isDouble(java.lang.String s)
(Note: if the DoubleScanner is used, number tokens cannot begin with + or -.)
public boolean isOperator(java.lang.String s)
Actually this routine should be called isDelimiter as it only recognizes the operators that can be used in expressions.
public static int isFunction(java.lang.String s)
s - - the string which will be tested.public static int numFuncParams(java.lang.String s)
s - - the string which will be tested.public static int precedence(java.lang.String operator)
operator - - the operator.public static int comparePrecedence(java.lang.String op1,
java.lang.String op2)
op1 - the first operator.op2 - the second operator.public ExprTree buildFromPrefix(java.lang.String prefix) throws java.lang.Exception
Examples of postfix functions:
sqrt 16 : (square root of 16)
* sqrt + ^4 2 ^ 3 2 100 : (square root(4^2 + 3^2) * 100)
toDegrees atan2 1 sqrt 3 : (atan2(1, sqrt(3)) converted to
degrees)
The tree built from the prefix string is assigned to root.
prefix - the infix string to be processed.java.lang.Exceptionpublic ExprTree buildFromInfix(java.lang.String infix) throws java.lang.Exception
The precedence of operators:
The tree built from the infix string is assigned to root.
infix - the infix string to be processed.java.lang.Exception - - if an identifier name is invalid or the expression
is invalid.public ExprTree buildTree(java.lang.String s) throws java.lang.Exception
s - - the infix string to be processed.- - Exception - if an identifier name, a value, or the expression
is invalid.java.lang.Exceptionpublic ExprTree buildFromPostfix(java.lang.String postfix) throws java.lang.Exception
Examples of postfix functions:
16 sqrt : (square root of 16)
4 2 ^ 3 2 ^ + sqrt 100 * : (sq root(4^2 + 3^2) * 100)
1 3 sqrt atan2 toDegrees : (atan2(1, sqrt(3)) converted to
degrees)
The tree built from the postfix string is assigned to root.
postfix - the postfix string to be processed.java.lang.Exceptionpublic java.lang.String toPrefix()
public java.lang.String toInfix()
public java.lang.String toPostfix()
public double evaluate()
throws java.lang.Exception
In the case of illegal calculations such as 1/0, log(-1), or sqrt(-1), the result will typically be infinity, -infinity, or NaN (Not a Number). Note: This version assumes the values of the 26 values are unchanged from the last evaluation or the time they were supplied. If they have never been defined, their value will be 0.
java.lang.Exception - - if a function cannot be evaluated or has the wrong
number of parameters.setT(double),
setX(double),
setY(double),
setValueOf(char, double),
setValues(double[])public double evaluate(double t,
double x,
double y)
throws java.lang.Exception
In the case of illegal calculations such as 1/0, log(-1), or sqrt(-1), the result will typically be infinity, -infinity, or NaN (Not a Number).
Note: This version allows setting the values of the variables "t", "x", and "y". This may be completely adequate for many situations. The values of the other 23 variables are unchanged from any previous evaluations. If they have never been defined, there value will be 0.
t - - value of variable t.x - - value of variable x.y - - value of variable y.java.lang.Exception - - if a function cannot be evaluated or has the wrong
number of parameters.public double evaluate(double[] params)
throws java.lang.Exception
In the case of illegal calculations such as 1/0, log(-1), or sqrt(-1), the result will typically be infinity, -infinity, or NaN (Not a Number).
In this version of evaluate, the values of all 26 variables are supplied in an array.
params - - an array of 26 double values corresponding to the
values of variables "a", "b", ..., "z"java.lang.Exception - - if a function cannot be evaluated or has the wrong
number of parameters.getValues()public double evaluate(ExprTree tree) throws java.lang.Exception
Use set... functions to assign values to variables before calling this methods.
tree - - the ExprTree to be evaluated.java.lang.Exception - - if a function cannot be evaluated or has the wrong
number of parameters.setX(double),
setY(double),
setT(double),
setValueOf(char, double),
setValues(double[]),
evaluate()public void convertToRect(double r,
double theta)
r - - the radius in polar coordinates.theta - - the angle in polar coordinates. It is measured in radians.getX(),
getY(),
getValueOf(char)public java.lang.String toString()
toString in class java.lang.Objectpublic static double roundTo(double x,
double num)