140 likes | 148 Views
Special agenda topics, assignment rubrics & solutions, Towers of Hanoi recursive solution, Factory design pattern, Perl code examples.
E N D
COMP 114 Recitation October 31, 2003
Agenda • Special topics survey • Assignment 5(a-d) rubric • Assignment 3(d) solution • Recursive solution to the Towers of Hanoi • Factory design pattern
Assignment 3(d) Solution #!/usr/bin/perl -w use strict; package StringScanner; sub new { my $class = shift; my $self = bless {}, $class; $self->{"_str"} = shift; return $self; } sub hasmore { my $self = shift; die "instance method called on" ." class" unless ref $self; return ($self->{"_str"} =~ /[^\s]/); } sub nextstr { my $self = shift; die "instance method called on class" unless ref $self; $self->{"_str"} =~ /^\s*(\d+|[^\s])(.*)$/; $self->{"_str"} = $2; return $1; } package TokenScanner; sub new { my $class = shift; my $self = bless {}, $class; $self->{"_strscan"} = StringScanner->new(shift); return $self; } sub hasmore { my $self = shift; die "instance method called on class" unless ref $self; return $self->{"_strscan"}->hasmore(); } sub nexttoken { my %ops = ('+' => 'Token::Operator::Plus','-' => 'Token::Operator::Minus','*' => 'Token::Operator::Multiply','/' => 'Token::Operator::Divide'); my $self = shift; die "instance method" . " called on class" unless ref $self; my $s = $self->{"_strscan"}->nextstr(); my ($t, $c); if ($s =~ /^\d+$/) { $t = Token::Number->new(); $t->assign($s); } else { $c = $ops{$s}; $t = eval("$c->new()"); die "$@" if $@; } return $t; } package Token; sub new { my $class = shift; my $self = bless {}, $class; return $self; } sub tostr { my $self = shift; die "instance method called on class" unless ref $self; return sprintf("%s@%p(%s)", ref($self), $self, $self->decomp()); } sub decomp { return "<token>"; } package Token::Number; @Token::Number::ISA = ("Token"); sub decomp { my $self = shift; die "instance" . " method called on class" unless ref $self; return $self->{"_number"}; } sub value { my $self = shift; die "instance method called on class" unless ref $self; return $self->{"_number"}; } sub assign { my $self = shift; die "instance method called on class" unless ref $self; $self->{"_number"} = shift; } package Token::Operator; @Token::Operator::ISA = ("Token"); sub calc { die "abstract method Token::" . "Operator::calc"; } sub decomp { return "<operator>"; } package Token::Operator::Plus; @Token::Operator::Plus::ISA = ("Token::Operator"); sub calc { my $self = shift; die "instance method" . " called on class" unless ref $self; my ($l, $r) = @_; return $l + $r; } sub decomp { return "+"; } package Token::Operator::Minus; @Token::Operator::Minus::ISA = ("Token::Operator"); sub calc { my $self = shift; die "instance method called on class" unless ref $self; my ($l, $r) = @_; return $l - $r; } sub decomp { return "-"; } package Token::Operator::Multiply; @Token::Operator::Multiply::ISA = ("Token::" . "Operator"); sub calc { my $self = shift; die "instance method called on class" unless ref $self; my ($l, $r) = @_; return $l * $r; } sub decomp {return "*";} package Token::Operator::Divide; @Token::Operator::Divide::ISA = ("Token::Operator"); sub calc { my $self = shift; die "instance method" . " called on class" unless ref $self; my ($l, $r) = @_; return $l / $r; } sub decomp {return "/"; } sub new { my $class = shift; my $self = bless {}, $class; $self->{"_strscan"} = StringScanner->new(shift); return $self; } package Evaluator; sub new { my $class = shift; my $self = bless {}, $class; $self->{"_tscan"} = TokenScanner->new(shift); return $self; } sub calc { my $self = shift; die "instance" . " method called on class" unless ref $self; my (@e, $x) = (); while ($self->{"_tscan"}->hasmore()) { my $t = $self->{"_tscan"}->nexttoken(); push @e, $t; } $x = (shift @e)->value(); while (@e) { my ($o, $v); $o = shift @e; $v = shift @e; $x = $o->calc($x, $v->value()); } return $x; } package MAIN; my ($e, $c); while (1) { print "Please enter a number expression:\n"; $e = <STDIN>; chomp $e; exit if $e eq '.'; $c = Evaluator->new($e); print $c->calc() . "\n"; }
Towers of Hanoi Objective: Move the discs from one tower to another. Constraint #1: Don’t put a big disc on a small one. Constraint #2: Move only one disc at a time. Constraint #3: Only topmost discs may move.
Base Case • Move the tower to the final peg.
Recursive Case • Move the body of the tower to the middle peg. • Move the base of the tower to the final peg. • Move the body of the tower to the final peg.
The Final Pseudocode void Hanoi(int size, Peg source, Peg middle, Peg dest) { if (size == 1) { Move_Disc(source, dest); } else { Hanoi(size - 1, source, dest, middle); Move_Disc(source, dest); Hanoi(size - 1, middle, source, dest); } }
Factories • Defer class instantiation to subclasses, and move decisions to runtime. • Provide a handy abstraction to class creation. • Rather than hardcode your class names everywhere you have a new statement, make a method call to a factory. It gets to store the hardcoded class names.
public abstract class Apple { public abstract String getName(); public void eat() { System.err.println( "Core dumped."); } } public class RedDelicious extends Apple { public String getName() { return "Red Delicious(tm)"; } } public class GrannySmith extends Apple { public String getName() { return "Granny Smith(tm)"; } } public class Macintosh extends Apple { public String getName() { return "iCrap(R)"; } } public class FourMonthOld extends Apple { public String getName() { return "Who knows?"; } public void eat() { System.err.println( "Mmmm! Cafeteria fresh!"); } } Example: Derived Classes
Example: Factory Class public class AppleFactory { public static Apple fromAdjective(String adj) { if (adj.equals("red")) return new RedDelicious(); if (adj.equals("green")) return new GrannySmith(); if (adj.equals("rotten")) return new Macintosh(); return new FourMonthOld(); } }
Example: Main Class public static void main(String[] args) { Apple a; String s; initStdin(); System.out.println("Apple type? "); s = stdin.readLine(); a = AppleFactory.fromAdjective(s); System.out.print("Apple type: "); System.out.print(a.getName()); a.eat(); System.out.print("Yum."); }
The Next Step • Instead of using a static method, why not make the “default apple” instance-dependent? • Make a factory class which creates other factories. We call this the Abstract Factory design pattern. • Don’t instantiate each time, but instead copy the object from a prototype. We’ll discuss this later.