Please make sure you adhere to the policies on academic honesty.
Please see the general programming webpage for details about the programming environment for this course, and specifically for directions in how to submit your programming assignment electronically.
The files you need for this assignment can be downloaded here.
((((3+1)x3)/((9-5)+2)) - ((3 x (7-4))+6))into a valid binary tree which can then be used to evaluate the expression. This particular expression matches that from Figure 6.6 of the text. Please read the discussion of Example 6.5 on pages 232-233 of the text carefully. You will not be writing any low-level tree code, rather you will make use of a modified implementation of the BinaryTree interface from the text.
Parsers for languages such as Java must certainly be able to perform such a task. Arithmetic expression appear in source code and must be evaluated in turn. In fact, they deal with a variety of complications, as they allow unary operators (such as "-3"), and operators which take more than two (such as "4+8+2+12"). Furthermore, they enforce a prescribed precedence for operators in the case where the user does not properly parenthesize an expression (imagine "5 - 3 x 4 + 10").
We will consider a simple version of the problem by using only binary operators and by assuming strict parenthesization in expressions. In particular we will make the following recursive definition for a well-formed expression:
This simple recursive definition allows us to represent arbitrarily complex arithmetic expression (and understanding this recursion will allow you to write a relative simple solution to this assignment!)
You may assume that your routine will be given WELL FORMED expressions. You do not need to be concerned with how to gracefully handle improperly formed expressions.
Examples of well-formed expressions include (one per line):
13 (5 - 8) (3 x (4/2)) ((((3+1)x3)/((9-5)+2))-((3x(7-4))+6))
You are to complete the routines defined in file Arithmetic.java:
Your routine should take this set of tokens, and generate a valid BinaryTree representing the original expression. In particular, every internal node should contain an element which is the token representing an operator. Every external node should contain a token which represents an integer. (The parentheses from the original expression do not explicitly appear in the tree, rather they provide the context for the expression.)
Please think about this task, and work on some examples, before you write any code. It is possible to implement this routine directly, and without recursion, but doing so may quickly become quite complicated. It is also possible to write a relatively simple recursive program which works perfectly.
We will provide you with two drivers for this program.
As usual, if you prefer to type your input into a file for testing, you may give a filename to the driver as a single runtime argument.
Note: this driver does not check to make sure expressions are well-formed. We simply guarantee that we will not give poorly formed expressions while grading.
If you define any additional classes, please submit the additional source code.
What we care most about in this program is that you get it working correctly. Some may be interested in thinking a bit about the efficiency of their routines. A straightforward recursive implementation of parse may lead to a worst case running time of , where n is the number of tokens in the expression. Though not required, we will point out that it is possible to implement parse to run in O(n) worst-case. This can be done using a non-recursive implementation, and it can even be done using a recursive implementation, though it requires a good deal of thought into how to precisely define the recursion. Even the extra credit can be done in O(n) time with care.
You will not be writing any low-level code for maintaining the tree. The book has already done this for you, and we will provide you with such an implementation. Instead, you will need to use the interface as defined in BinaryTree.java, which itself is based on the Position interface. Our implementation includes all of the Tree methods from Chapter 6.1.2, as well as the specialized BinaryTree methods of Chapter 6.3.1. Since you will need not only to inspect a tree, but to modify it as well, our implementation includes the following methods for a given tree T:
Finally, we note that the constructor for our implementation:
BinaryTree T = new LinkedBinaryTree();generates a new tree with a single external node which is the root. The element stored at the root will be initialized to null (though you will certainly want to change this at some point).
One of the beauties of binary trees are that they lead very nicely to the use of recursion. A subtree of a binary tree appears very much to be its own tree.
In terms of implemenation however, there is one catch. If you call a method such as T.leftChild(v) for a given tree T and position v, it does not actually return to you an object of class BinaryTree; it returns to you a Position within the original tree T. If you look carefully at the many code examples in Chapter 6 of the text, they base their recursion on the concept of a subtree.
The recursive procedures generally take two parameters, a reference to the full tree and a particular position v. Together, these parameters are used to identify the subtree of T rooted at v. Now recursion can proceed, for instance by making a call with T and T.leftChild(v) as parameters.
The last detail is that you will often want to use recursion for a public method which takes only a full BinaryTree as a parameter. For example, in this assignment, we have asked for a routine evaluate(BinaryTree T). The recursive pseudo-code of Code Fragment 6.18 of the text is based on the two parameter recursion. The solution is to have the public method serve as a wrapper for the private recursive procedure. Since the public method involves the full tree, you can simply translate this to start the recursive function on the subtree of T rooted at T.root().
Please keep in mind that the way to test strings for equality is to
use the equals() method provided. An example of correct
syntax reads,
if (token[5].equals("+")) {
In the evaluation routine, you will need to convert the tokens which represent integers into actual int values which you can use. Assuming that you have a Position which represents a leaf of the tree, you can get the value by,
Position p; \\ assume this refers to a leaf of the tree. int val; try { val = Integer.parseInt((String) p.element()); } catch (NumberFormatException e) { System.out.println("Unexpected NumberFormatException when parsing " +((String) p.element)); }Although we promise that we will only test your program with well-formed expressions, Java will insist that you catch such a possible exception.
We define a more general grammer for expression as follows:
If you compare this definition to the original, you will notice that it includes all original well-formed expression and many more. Examples of such general expressions include:
1+2+3+4 (5)+7 5-3x4+10 5-3x(4+10)
In parsing general expression, you must follow Java's rules for precedence of operators, namely that:
Thus, the expression "5-3x4+10" is equivalent to "((5-(3x4))+10)", yet the expression "5-3x(4+10)" should be equivalent to "(5-(3x(4+10)))".
Both drivers will allow you to test the extraParse routine.
Using the applet, there is an appropriately labeled button for this
routine. With the text-based driver, it can only be done in tandem
with reading input from a file. The filename is to be given as the
first argument, and a second argument "e" alerts the driver to use
the extra credit routine rather than the standard parse. Using
JDK,
java ExpressionDriver inputfile e
would test the
extraParse routine using the inputfile.
We will award 1 extra point for this task, though we are not in any way implying that this is a minor feat to accomplish. Please make sure that the readme file announces your success.