What do we know about first possible characters of Factor, Term, or Expression?
Can use to help catch errors.
Look at how 3 * 7 + 6 / 2 - (3 + 7) would be understood as a tree.
3*7 is a term because both 3 and 7 are factors, similarly 6/2 is a term.
(3+7) is a factor because 3 + 7 is is an expression.
Because it is a factor, it is also a term.
Therefore 3 * 7 + 6 / 2 - (3 + 7) is of the form term1 + term2 - term3, and hence is an expression!
Exercise: Write out the corresponding tree (remember operations of the same precedence are done from left to right.)
Once you have the tree, how do you evaluate it?
See Parser code on-line.
Two things to notice:
Game is held as tree with answers at leaves and questions as internal nodes.
Asking questions corresponds to path from root to a leaf, where leaf holds animal which is best guess.
If guess wrong, then add new animal and corresponding question (w/old animal, too) in tree in place of old animal.
Game is much more interesting if save old games so can build tree again.
Suggest writing out in either prefix or postfix traversal. Indicate whether question or answer by writing 'Q' or 'A', otherwise might not be able to reconstruct tree.
Can load in exactly same way: read root, read and attach left subtree, read and attach right subtree.
Unfortunately can't use files in applet. However it is easy to convert an applet into an application. Add main method (static) as follows:
/* post: If program is being run as an application, creates a frame for it to run in. Ignored if run as applet. */ public static void main(String[] args) { // Set up and show frame AnimalAppletFrame app = new AnimalAppletFrame("Animals Game"); app.resize(400,160); app.show(); }
and add a new class at the end of the file containing your applet class:
/* This class supplied a frame (window) to be used when the program is run as an application. */ class AnimalAppletFrame extends Frame { /* Create frame holding the animals applet. */ public AnimalAppletFrame(String frameTitle) { super(frameTitle); // Put title on window // create the animals applet and stick it in the middle // of the frame (filling it!) AnimalApplet applet = new AnimalApplet(); // Tell the applet it is being run as an application applet.setApplication(); add("Center",applet); applet.init(); // Initialize the applet applet.start(); // Start the applet running } }
Just set up project to be an application and can run as application (and can still use as applet as well - just use applet project).
File operations are pretty straightforward:
// create a new output file with name fileName. DataOutputStream outFile = new DataOutputStream(new FileOutputStream(fileName)); // read character from inFile and store in letter char letter = inFile.readChar(); // read string from inFile and store in contents String contents = inFile.readUTF(); outfile.close(); // close file // create a new input file with name fileName. DataInputStream inFile = new DataInputStream(new FileInputStream(fileName)); // write character from letter onto outFile outFile.writeChar(letter); // write string from contents onto outFile outFile.writeUTF(contents); inFile.close(); // close file
public class BinaryTreeNode { protected Object val; protected BinaryTreeNode parent; protected BinaryTreeNode left; protected BinaryTreeNode right; public BinaryTreeNode(Object value, BinaryTreeNode left, BinaryTreeNode right) // post: returns a node referencing value & subtrees { val = value; setLeft(left); setRight(right); } public BinaryTreeNode left() // post: returns reference to left subtree, or null public BinaryTreeNode right() // post: returns reference to right subtree, or null public BinaryTreeNode parent() // post: returns reference to parent node, or null public void setLeft(BinaryTreeNode newLeft) // post: sets left subtree to newLeft // reparents newLeft if not null { if (left != null && (left.parent() == this)) left.setParent(null); left = newLeft; if (left != null) left.setParent(this); } public void setRight(BinaryTreeNode newRight) protected void setParent(BinaryTreeNode newParent) // post: reparents this node to parent reference, or null public static int size(BinaryTreeNode n) // post: returns the size of the subtree rooted at n public static BinaryTreeNode root(BinaryTreeNode n) // post: returns the root of the tree containing node n { if ((n == null) || (n.parent() == null)) return n; else return root(n.parent()); } public static int height(BinaryTreeNode n) // post: returns the height of a node n in its tree { if (n == null) return -1; return 1 + Math.max(height(n.left()),height(n.right())); } public static int depth(BinaryTreeNode n) // post: returns the depth of a node in the tree { if (n == null) return -1; return 1 + depth(n.parent()); } ... public boolean isLeftChild() // post: returns true if this is a left child of parent. { if (parent() == null) return false; return this == parent().left(); } public Object value() // post: returns value associated with this node. public void setValue(Object value) // post: sets the value associated with this node }
Come back and talk about iterators later. Nothing really surprising above.
Notice must be careful in setting element to left or right (must connect both ways) - extra test at beginning setting parent field of elt currently there to null isn't really necessary.
Notice root, height, and depth are all recursive (and are all static - i.e., belong with class, not object!).
BinaryTree class relatively straightforward, but watch cursor:
public class BinaryTree { protected BinaryTreeNode root; // root of the tree protected BinaryTreeNode cursor; // ptr to current node protected BinaryTreeNode prior; // cursor's prior value // cursor result of moving left protected boolean wentLeft; protected int size; // the size of the tree public BinaryTree() // post: creates an empty binary tree { clear(); } public void clear() // post: removes all nodes from tree { root = cursor = null; size = 0; } public void insert(Object value) // pre: cursor is invalid // post: if tree empty, value inserted at root, otherwise // value inserted where cursor last moved off tree { Assert.pre(cursor == null, "Insertion does not overwrite value."); if (prior == null) { Assert.pre(root == null, "Insertion at root only in empty tree."); cursor = root = new BinaryTreeNode(value); } else { cursor = new BinaryTreeNode(value); if (wentLeft) { prior.setLeft(cursor); } else { prior.setRight(cursor); } } size++; } public Object remove() // pre: cursor is valid and has no children // post: leaf is removed, cursor moved to parent, if any { Assert.pre(cursor != null, "Node to be removed exists."); Assert.pre(!(hasLeft()||hasRight()), "Node to be removed is leaf."); Object value = cursor.value(); if (isLeftChild()) { moveUp(); cursor.setLeft(null); } else if (isRightChild()) { moveUp(); cursor.setRight(null); } else { root = cursor = prior = null; } size--; return value; } public Object value() // pre: cursor valid // post: returns value of object at cursor { return cursor.value(); } public void setValue(Object value) // pre: cursor valid // post: sets value found at cursor { cursor.setValue(value); } public void reset() // post: moves the cursor to the root, if any { cursor = root; prior = null; } public boolean valid() // post: returns true if cursor points to a valid node. { // be aware that the cursor could become null if // a precondition is violated. Otherwise the cursor // is null only if the tree is empty return cursor != null; } public boolean hasLeft() // post: returns true iff cursor has left child { return (cursor != null) && (cursor.left() != null); } public boolean isLeftChild() // post: return true if cursor has parent & is left child { return (cursor != null) && cursor.isLeftChild(); } public void moveLeft() // pre: cursor is valid // post: cursor moves to left child of pre-cursor, // or off tree { prior = cursor; wentLeft = true; cursor = cursor.left(); } public int height() // post: returns height of cursor in tree // or -1 if tree is empty { return BinaryTreeNode.height(cursor); } }
Reasonably efficient in terms of time and space complexity. 3 references per node, but makes operations easier. Can also get away w/ just left and right subtree references.
Is there an array implementation of trees?