CS 211 — Homework Five

Add it up

Due: 07 March 2013, 11:59p

Worth: 80 points

Expression trees

One use of binary trees is to represent expression trees. An expression tree is a binary tree in which the leaves are operands (constant values or variables) and the inner nodes are operators. This is a useful form because the semantics of the expression are encoded in the tree unambiguously (the order of operations is encoded into the structure of the tree), and evaluation is easily performed through a simple traversal of the tree. To evaluate a particular node, we either just return the value stored in the node (if it is a leaf node), or we evaluate the left and right subtrees and apply the node's operator to the two results. For this assignment we will be playing with arithmetic expression trees — trees for arithmetic expressions, so the operators will be +, -, *, and /. Here is an example tree for the expression x + 5 - 6:

ArithmeticExpression class

For this assignment, your task will be to implement an expression tree data structure. Our data structure will represent a single arithmetic expression, which will be passed into the constructor as a String. The String will be parsed into tree form, and then the user will be able to perform tasks such as evaluating the expression and printing out an unambiguous canonical form of the expression. You will implement this in a class called ArithmeticExpression with the following interface:

ArithmeticExpression(String expression)
This is the constructor. It will take a String containing the expression. The expression will contain operators (+,-,*,/), integer constants, and arbitrary strings that should be interpreted as variables (which can be of any length).There is no guarantee that this expression is valid. If the expression is not valid, this should throw a ParseException (the location of the parse error should be the location of the start of the token when you realized you had a problem). To make your lives a little easier, the expression will be space delimitated. Being the constructor, this function should only perform the parse, building a tree representation of the expression.
String toString()
This method should return a canonical, unambiguous String containing the parsed expression. In this case, that means fully parenthesized according to operator precedence (i.e., * and / have higher precedence than + and -). For example, "6 + 5 * 2" should result in "(6+(5*2))". The result should contain no spaces. Hint: this should sound like some kind of traversal — what kind?
void setVariable(String name, int value)
This allows the user to set a value for a variable in the expression. If the variable does not exist in the function, throw a NameNotFoundException.
int evaluate()
Obviously, this should return the integer result of evaluating the expression. Unset variables should be considered to have the value of 0. The evaluate should be done when this function is called. Do not try to pre-compute the value. The idea is that the user may want to evaluate the expression multiple times with different values for the variables.

Example usage

Code:

ArithmeticExpression expression = new ArithmeticExpression("x + 5 * 6"); System.out.println(expression); expression.setVariable("x", 5); System.out.println(expression.evaluate()); expression.setVariable("x", 10); System.out.println(expression.evaluate());

Output:

(x+(5*6)) 35 40

Implementation details

The hardest part of this is implementing the parser that builds the expression tree so that the correct operator precedence is maintained. We will use the fairly standard convention that multiplicative operators (*,/) have precedence over additive operators (+,-). Within the two classes of operator, we will resolve precedence by reading from left to right. For example, given the expression x + y * 2, assuming that we interpret the left and right sides of the operators as left and right children, there are two trees we could build, but only one of them is correct.

Hw5 Precedence

We would write the canonical form of this expression as "(x+(y*2))". Similarly, since we read left to right for operators in the same class, given the input 6 / 2 * 2, the canonical form would be "((6/2)*2)". This is important, because ((6/2)*2) evaluates to quite a different value than (6/(2*2)).

So, we can't just start chaining nodes together. Instead, we are going to use a pair of stacks to help us out (use java.util.LinkedList for these). One stack will be the operator stack, and it will hold operators that we don't know how to use yet. The other stack will be a tree stack, and will hold the partial binary trees that we have not fit together yet. As we read tokens from the input expression, the operation will then be:

	
If the token is an operand, 
	make it into a tree and push it on the tree stack
If the token is an operator and the operator stack is empty
 	push the operator on the operator stack
If the token is "*" or "/"
	if the operator on top of the operator stack is "*" or "/"
	   pop the operator from the operator stack
	   pop the top two trees from the tree stack
	   create a new tree node with the two trees and the operator
	   push the new tree on the tree stack
	push the new operator on the operator stack
Otherwise:
	while the operator stack is not empty
		pop an operator from the operator stack
		pop the top two trees from the tree stack
		create a new tree with the two trees and the operator
		push the new tree on the tree stack
	push the new operator onto the operator stack
When the String is consumed, repeat the above loop to clear out any unused operators	

At the end, the expression tree should be sitting alone in the tree stack. Clearly, if any of the pops fail, or if there are more trees in the tree stack at the end of the process, the expression was invalid.

To implement the tree, I want you to use the linked approach, and create some kind of "node" class that has links to the left and right subtrees. There is no need for parent pointers for this application — we will never need to walk up the tree from the bottom. Store the value of the node as a String. You can parse constant values into integers at evaluation time. You can leave the node very raw (just instance data and a constructor), or you can add methods like isLeaf() as you see fit.

Another issue you will have is storing the variable values. The obvious answer would be to replace any variables in the tree with the value when they get set. Of course, then you won't be able to change the variable's value and re-evaluate the expression (which would rather remove the value of having variables there). So, we want to build a lookup table to hold the current values of all of the variables. We will build this symbol table using an ArrayList. You will need to create some kind of inner class to hold name/value pairs. Stick these in the ArrayList and just iterate through everything to find the right entry. This isn't terribly efficient, but we are unlikely to have very many variables. Very soon, we will start talking about a data structure that is tailor-made to this, but for right now we will put up with this little bit of inefficiency.

Bonus round

I decided that I would give you the opportunity to earn a little extra credit on this one. If you chose to do any of these, be very clear that you did it in your comments, and provide appropriate test cases demonstrating them in action.

Book keeping

Since the last assignment was pretty easy, I would like you to continue to book keep your time. So please log the amount of time that you spend on:

Try to be as accurate as possible (it would be great if you didn't just get to the end and go "oh right, I need to keep track of my time & well it seemed like five hours..."). I am using this to (a) figure out if you (as a class, and also on a personal level) need more direction on how to direct your energies, and (b) inform the shape of future assignments. The more accurate this information is, the better I can do those two things, so please do not exaggerate in an attempt to get me to trim the assignments down or underestimate because you feel like your classmates didn't need as much time as you. Please include these in a comment at the top of the AssignmentFourMain class.


Last modified: Mon Apr 1 15:16:38 EDT 2013