Upload
david-harvey
View
280
Download
1
Embed Size (px)
Citation preview
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
File system• single file • or a directory containing (smaller) file
systems
Binary tree
• a leaf • or an internal node (root) and two smaller trees
Binary tree example: recursive expression
• An expression is either– a constant, or– an operator and two smaller expressions
• Recursive structure
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: Operator
• Recall how to make enumeration type
public enum Operator { PLUS("+"), MINUS("-"), MULT("*"), DIV("/"); private String name; private Operator(String name) { this.name = name; } public String toString() { return name; } public int apply(int left, int right) { if (this == Operator.PLUS) return left + right; if (this == Operator.MINUS) return left - right; if (this == Operator.MULT) return left * right; /* this == Operator.DIV */ return left / right; }}
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) ) );
QUIZWhich UML diagram models recursive bullet
lists best?
1. 2.
3. 4.
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 else x = 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
<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>
For QUIZ: HTML source code
QUIZ
Which – if any – errors arise?
1. Compiler error(s)
2. Runtime exceptions
3. No errors – method is correct!
4. I don’t know
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;}
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)
Example: 8 * (5 + 2)expr1.getValue()
QUIZ
Which – if any – errors arise?
1. Compiler error(s)
2. Runtime exceptions
3. No errors!
4. I don’t know
Code in class BulletList:
public String getText() { String result = "<ul>"; for (HTML h : getEntries()) result += "<li>"+h.getText()+"</li>"; return result+"</ul>";}
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
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 Node public <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); }}
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())
QUIZ
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
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>”; }}
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 else return "(" + 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 ); else increment 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 drawn increment x and draw s; return (x,y)draw(v): // returns where root of expression is drawn if v is a leaf: return drawSymbol( number ); else increment 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 list
• the empty list
[ ]
• or an element followed by a shorter list
[ 2, 7, 13, 1]
class hierarchy representing list
• Interfacepublic interface ConsList {}
• class representing empty listpublic class Nil implements ConsList {}
• class representing element and shorter listpublic class Cons implements ConsList { private Object hd; private ConsList tl; public Cons(Object x, ConsList y) { hd = x; tl = y; }}
• The list ["Easter","Xmas"] may be represented asnew Cons("Easter",new Cons("Xmas",new Nil())).
public interface ConsListVisitor<T> { public T visitNil(Nil n); public T visitCons(Cons c);}
ConsList classes prepared for visits
• The recursive structure is prepared (once and for all) by adding an accept method
• In interface ConsListabstract <T> T accept(ConsListVisitor<T> v);
• In class Nilpublic <T> T accept(ConsListVisitor<T> v) { return v.visitNil(this);}
• In class Conspublic <T> T accept(ConsListVisitor<T> v) { return v.visitCons(this);}
callback to problem specific method
callback to problem specific methods defined
by external visitor
Problem Specific ConsListVisitor: Linear Search
public class SearchVisitor implements ConsListVisitor<Boolean> {
private Object key; public SearchVisitor(Object k) { key = k; }
public Boolean visitNil(Nil n) { return false; }
public Boolean visitCons(Cons c) { return c.head().equals(key) || c.tail().accept(this); }}
• Search in recursive list l byl.accept(new SearchVisitor("Xmas"));
(simple) labyrinth
• empty labyrinth • or a wall and two smaller labyrinths
=
Recursive labyrinth: solving it while constructing it...
...solving it while constructing it