noter ch.3 modeling recursive structure by class hierarchy ... · recursive data structures noter...
TRANSCRIPT
Recursive data structuresnoter ch.3
Modeling recursive structure by class hierarchy
Recursive traversal of structure
Recursive data structure• Recursive structure:
– list, tree• Object oriented representation
– Interface– Implementing classes
• Traversal of recursive structure– External recursive method– Internal recursive method (Composite design pattern)– Visitor design pattern
• Building a recursive structure– Parsing using mutually recursive methods
File system
File system
• A directory containing (smaller) file systems
• Or a single file
Arithmetic expression
• An expression is either– a constant, or– an operator and two smaller expressions
Expression as binary tree
• Recursive structureNode
Leaf
A binary tree is• an internal node (root)
and two smaller trees, or• a leaf
Classes for modeling recursive expression
public interface Tree { }
public class Leaf implements Tree {private int value;public Leaf(int n) { value = n; }public int getValue() { return value; }
}
public class Node implements Tree {private Operator op;private Tree left;private Tree right;public Node(Tree l, Operator o, Tree r){ op = o; left = l; right = r; }
public Operator getOp() { return op; }public Tree getLeft() { return left; }public Tree getRight() { return right; }
}
Enumeration type: Operatorpublic enum Operator {PLUS("+"), MINUS("-"), MULT("*"), DIV("/");private String name;private Operator(String name) { this.name = name; }public String toString() { return name; }public static Operator parseOp(String s) {
if (Operator.PLUS.name.equals(s)) return Operator.PLUS;if (Operator.MINUS.name.equals(s)) return Operator.MINUS;if (Operator.MULT.name.equals(s)) return Operator.MULT;if (Operator.DIV.name.equals(s)) return Operator.DIV;throw new UnsupportedOperationException(s);
}public int apply(int left, int right) {
switch (this) {case PLUS: return left + right; case MINUS: return left - right; case MULT: return left * right; case DIV: return left / right; default:
throw new UnsupportedOperationException(this.toString());}
}}
representation of an expression
Tree t =new Node(new Leaf(6),Operator.PLUS,new Node(new Node(new Leaf(8),Operator.MULT,new Leaf(2)),Operator.MINUS,new Leaf(1)
));
Recursive bullet list
• In webbrowser • html source code<ul>
<li>a simple bullet list</li><li>containing smaller lists</li><li><ul>
<li>a smaller sublist</li><li>
<ul><li>a tiny list</li><li>with several entries</li>
</ul></li><li>look: recursive lists!</li>
</ul></li>
</ul>
QUIZ UML and recursion
Which UML diagram models recursive bullet lists best?
1.
4.
2.
3.
5. I don’t know
Traversal of recursive structure
• traversal– calculate value of expression– print expression
• traversal techniques– external recursive method– internal mutually recursive methods
(composite pattern)– visitor pattern
traversal: expression evaluation• post order traversal: visit children (subexpressions)
before visiting root node (operator)
evaluate(v):if v is a leaf:return number stored at v
elsex = evaluate(left subexpression stored at v)y = evaluate(right subexpression stored at v)return x o y (where o is operator stored at v)
evaluation by single external recursive method
public int evaluate(Tree w) {int answer;if ( w instanceof Leaf ) answer = ((Leaf)w).getValue();else {Tree l = ((Node)w).getLeft();Tree r = ((Node)w).getRight();Operator o = ((Node)w).getOp();int left_answer = evaluate(l);int right_answer = evaluate(r);answer = o.apply(left_answer,right_answer);
}return answer;
}
instanceof-test necessary
Java technicality:lots of casts obscures the code
Recursive bullet list• In webbrowser • html source code
<ul><li>a simple bullet list</li><li>containing smaller lists</li><li><ul>
<li>a smaller sublist</li><li>
<ul><li>a tiny list</li><li>with several entries</li>
</ul></li><li>look: recursive lists!</li>
</ul></li>
</ul>
• UML model
public String print(HTML h) {String result = "";if (h instanceof Text) result = h.getText();else {
result += "<ul>";for (HTML k : h.getEntries())
result += "<li>"+print(k)+"</li>";result += "</ul>";
}return result;
}
How should the compiler errors be fixed?1. There should be no errors – update java compiler to newer version!2. Declare getText() and getEntries() in interface HTML3. Type cast h to Text and BulletList, respectively4. Fix in some other way5. I don’t know
QUIZTraversal by externalrecursive method
internal mutually recursive methods• Ensure that all classes implementing Tree define a getValue method
• In interface Treepublic abstract int getValue();
• in class Leaf:public int getValue() { return value; }
• in class Node:public int getValue() {
return op.apply(left.getValue(),right.getValue());
}
instanceof-tests and casts are no longer
necessary!
Node expr1 =new Node(new Leaf(8),Operator.MULT,new Node(new Leaf(5),Operator.PLUS,new Leaf(2)
));
Example: 8 * (5 + 2)
• in class Leaf:public int getValue() { return value; }
• in class Node:public int getValue() {
int l = left.getValue();int r = right.getValue();return op.apply(l,r);
}
Example: 8 * (5 + 2)expr1.getValue()
Example: 8 * (5 + 2)expr1.getValue()
Example: 8 * (5 + 2)expr1.getValue()
Example: 8 * (5 + 2)expr1.getValue()
Example: 8 * (5 + 2)expr1.getValue()
Example: 8 * (5 + 2)expr1.getValue()
Example: 8 * (5 + 2)expr1.getValue()
Example: 8 * (5 + 2)expr1.getValue()
Recursive bullet list• In webbrowser • html source code
<ul><li>a simple bullet list</li><li>containing smaller lists</li><li><ul>
<li>a smaller sublist</li><li>
<ul><li>a tiny list</li><li>with several entries</li>
</ul></li><li>look: recursive lists!</li>
</ul></li>
</ul>
• UML model
QUIZ
Code in class BulletList:
public String getText() {String result = "<ul>";for (HTML h : getEntries())
result += "<li>"+h.getText()+"</li>";return result+"</ul>";
}
Traversal by internal recursive method
How should the compiler error be fixed?1. There should be no errors – update java
compiler to newer version!2. Declare getText() in interface HTML3. Type cast h to Text4. Type cast h to BulletList5. Fix in some other way6. I don’t know
Comparing traversal techniques
• external recursive method:– obscures code by instanceof test and casts
• internal mutually recursive methods:– for each new kind of traversal it is necessary to mess
around with the code of all classes to insert new methods
• Visitor pattern (avoids above problems):– inserts a single method in all classes once and for all– these methods make call back to problem specific
external methods
Visitor design pattern
• Decoupling of Tree hierarchy and problem specific traversal.
• The Tree hierarchy is prepared by adding an accept method capable of performing callback
• A problem specific traversal (such as value computation) requires an implementation of interface TreeVisitor
• EvaluateVisitor is the client.• Mutually recursive accept / visitNode• Recursion stops when calling visitLeaf
Modification of Tree classes• The recursive structure is prepared (once and for all) by adding an
accept method to Tree and all its implementing classes
• In interface Treepublic <T> T accept(TreeVisitor<T> v) ;
• In class Leafpublic <T> T accept(TreeVisitor<T> v){return v.visitLeaf(this);
}
• In class Nodepublic <T> T accept(TreeVisitor<T> v){return v.visitNode(this);
}
callback to problem specific method
callback to problem specific methods defined
by external visitor
Problem specific TreeVisitor• A problem specific traversal requires an implementation
of interface TreeVisitor:public interface TreeVisitor<T> {
public T visitLeaf(Leaf l);public T visitNode(Node n);
}
• For traversal, the TreeVisitor methods send the tree object an accept message.
• The accept methods in turn make call back to the appropriate visitLeaf or visitNode method
• methods visitLeaf/visitNode and accept are mutually recursive.
Visitor example: expression evaluation
• instanceof test not needed (handled by call back)
class EvaluateVisitor implements TreeVisitor<Integer> {
public Integer visitLeaf(Leaf l) {return l.getValue();
}
public Integer visitNode(Node n) {int l = n.getLeft().accept(this);int r = n.getRight().accept(this);return n.getOp().apply(l,r);
}}
• In class EvaluateVisitorpublic Integer visitLeaf(Leaf l){ return l.getValue(); }
public Integer visitNode(Node n) {int l = n.getLeft().accept(this);int r = n.getRight().accept(this);return n.getOp().apply(l,r);
}• In class Leafpublic <T> T accept(TreeVisitor<T> v){return v.visitLeaf(this);
}
• In class Nodepublic <T> T accept(TreeVisitor<T> v){return v.visitNode(this);
}
Example: 8 * (5 + 2)expr1.accept(new EvaluateVisitor())
Example: 8 * (5 + 2)expr1.accept(new EvaluateVisitor())
Example: 8 * (5 + 2)expr1.accept(new EvaluateVisitor())
Example: 8 * (5 + 2)expr1.accept(new EvaluateVisitor())
Example: 8 * (5 + 2)expr1.accept(new EvaluateVisitor())
Example: 8 * (5 + 2)expr1.accept(new EvaluateVisitor())
Example: 8 * (5 + 2)expr1.accept(new EvaluateVisitor())
Example: 8 * (5 + 2)expr1.accept(new EvaluateVisitor())
Example: Expression evaluation
• Given some treeTree t = ...
• Evaluate by external recursive methodevaluate(t)
• or by internal mutually recursive methodst.getValue()
• or by using visitor patternt.accept(new EvaluateVisitor())
Recursive bullet list• In webbrowser • html source code
<ul><li>a simple bullet list</li><li>containing smaller lists</li><li><ul>
<li>a smaller sublist</li><li>
<ul><li>a tiny list</li><li>with several entries</li>
</ul></li><li>look: recursive lists!</li>
</ul></li>
</ul>
• UML model
QUIZ
public class PrintVisitor implements HTMLVisitor<String> {
public String visitText(Text t) { return t.getText(); }
public String visitBulletList(BulletList b) {String result = "<ul>";for (HTML h : b.getEntries()) result += "<li>"+ +"</li>";return result+"</ul>”;
}}
What code should replace ?1. b.getText()2. b.accept(this)3. b.accept(new PrintVisitor());4. h.getText()5. h.accept(this)6. h.accept(new PrintVisitor());7. None of the above8. I don’t know
Traversal by visitor
traversal: printing expression• in order traversal: visit left child (subexpression) before
visiting root node (operator), and finally visit right child
text(v):if v is a leaf:return number
elsereturn "("+ text( left subexpression)+ operator+ text( right subexpression )+ ")"
Printing expression using visitor• Computing String representation of expression (printing)
class PrintVisitor implements TreeVisitor<String> {
public String visitLeaf(Leaf l) {return new Integer(l.getValue()).toString();
}
public String visitNode(Node n) {return ("(" + n.getLeft().accept(this)
+ n.getOp()+ n.getRight().accept(this) + ")");
}}• Application: System.out.println(t.accept(new PrintVisitor()));
Traversal example: drawing expression
state : a current drawing position (x,y)initially (x,y) = (0,0)
drawSymbol(s):increment x and draw s;
draw(v):if v is a leaf:drawSymbol( number );
elseincrement y;draw( left subexpression );decrement y;drawSymbol( operator );increment y;draw( right subexpression );decrement y;
pseudocode ignores connection lines.
Adding connection lines to drawingstate : a current drawing position (x,y)
initially (x,y) = (0,0)drawSymbol(s): // returns position where s is drawnincrement x and draw s;return (x,y)
draw(v): // returns where root of expression is drawnif v is a leaf:return drawSymbol( number );
elseincrement y;l = draw( left subexpression );decrement y;c = drawSymbol( operator );draw line from l to c;increment y;r = draw( right subexpression );decrement y;draw line from r to c;return c;
drawing of connection lines requires that
draw methods return positions for later use
class Draw ...class DrawVisitor extends JComponent implements
TreeVisitor<Point> {private static final int UNIT = 30;private Tree t;private Point pen_pos;private Graphics2D g;public DrawVisitor(Tree t) {this.t = t;JFrame f = new JFrame();f.add(this);f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.setSize(400,400);f.setVisible(true);
}...public void paintComponent(Graphics g) {this.g = (Graphics2D)g;pen_pos = new Point(UNIT,UNIT);t.accept(this);
}}
... implements TreeVisitorprivate Point drawSymbol(Object ob) {pen_pos.x += UNIT;g.drawString(ob.toString(),pen_pos.x,pen_pos.y-4);return (Point) pen_pos.clone();
}public Point visitLeaf(Leaf l) {return drawSymbol( new Integer(l.getValue()) );
}public Point visitNode(Node n) {pen_pos.y += UNIT;Point left_pos = n.getLeft().accept(this);pen_pos.y -= UNIT;Point node = drawSymbol(n.getOp());g.draw(new Line2D.Double(left_pos,node));pen_pos.y += UNIT;Point right_pos = n.getRight().accept(this);pen_pos.y -= UNIT;g.draw(new Line2D.Double(right_pos,node));return node;
}
• Actual drawing made by new DrawVisitor(t)
• where Tree t = new Node(new Leaf(6),Operator.PLUS,
new Node(new Node(new Leaf(8),Operator.MULT,new Leaf(2)),Operator.MINUS,new Leaf(1)));
Recursive bullet list• In webbrowser • html source code
<ul><li>a simple bullet list</li><li>containing smaller lists</li><li><ul>
<li>a smaller sublist</li><li>
<ul><li>a tiny list</li><li>with several entries</li>
</ul></li><li>look: recursive lists!</li>
</ul></li>
</ul>
• UML model
QUIZ
public class PrintVisitor implements HTMLVisitor<String> {
public String visitText(Text t) { return t.getText(); }
public String visitBulletList(BulletList b) {String result = "<ul>";for (HTML h : b.getEntries())
result += "<li>"+h.accept(this)+"</li>";return result+"</ul>”;
}} How would you declare accept in interface HTML?
1. public <E> E accept(HTMLVisitor<E> v);2. public E accept(HTMLVisitor<E> v);3. public String accept(HTMLVisitor<String> v);4. public String accept(PrintVisitor v);5. I don’t know
Traversal by visitor
Parsing an expression
Tree t =new Node(
new Leaf(6),Operator.PLUS,new Node(
new Node(new Leaf(8),Operator.MULT,new Leaf(2)),Operator.MINUS,new Leaf(1)
));
Easier:
Tree t = new Parser(“6+(8*2-1)”).parseExpression();
Parsing an expression
• Problem:– build the recursive data structure for
arithmetic expressions such as3 + 4 * 5
(3 + 4) * 51 – (2 – (3 – (4 – 5)))
• Precedence rules: – * and / take precedence over + and –– may overrule using parentheses ( ... )
Syntax diagram for expression
number
Syntax tree for two expressions
Mutually recursive methods
• Implement 3 methods that call each other recursivelyparseExpressionparseTermparseFactor
• An ExpressionTokenizer is used to group input in tokens. A token being a string of digits or one of "+", "-", "*", "/", "(", ")". Methods:peekTokennextToken
public class Expression Parser {public ExpressionParser(String anExpression) {
tokenizer = new ExpressionTokenizer(anExpression);}
public Tree parseExpression() { ... }
public Tree parseTerm() { ... }
public Tree parseFactor() { ... }
private ExpressionTokenizer tokenizer;}
public Tree parseExpression() {Tree t = parseTerm();while ("+".equals(tokenizer.peekToken())
|| "-".equals(tokenizer.peekToken())) {Operator op = Operator.parseOp(
tokenizer.nextToken());Tree t2 = parseTerm();t = new Node(t,op,t2);
}return t;
}
public int parseTerm() {Tree t = parseFactor();while ("*".equals(tokenizer.peekToken())
|| "/".equals(tokenizer.peekToken())) {Operator op = Operator.parseOp(
tokenizer.nextToken());Tree t2 = parseFactor();t = new Node(t,op,t2);
}return t;
}
public int parseFactor() {Tree t;if ("(".equals(tokenizer.peekToken())) {
tokenizer.nextToken();t = parseExpression();tokenizer.nextToken(); // read ")"
} elset = new Leaf(
Integer.parseInt(tokenizer.nextToken()));return t;
}
QUIZpublic Tree parseTerm() { }
if ("!".equals(tokenizer.peekToken())) {tokenizer.nextToken();return new BoolNode(
BoolOperator.NOT,parseFactor());}return parseFactor();
BoolTree t = parseFactor();if ("!".equals(tokenizer.peekToken())) {
tokenizer.nextToken();t = new BoolNode(BoolOperator.NOT,t);
}return t;
a
b
(Part of) syntax diagram for Boolean expression
Which code could replace ?
1. a2. b3. a and b4. Neither a nor b5. I don’t know
Parse Expression