Object-Oriented Design Lecture 13 CSU 370 Fall 2008 (Pucella) Friday, Oct 31, 2008

Similar documents
Subclassing for ADTs Implementation

16 Multiple Inheritance and Extending ADTs

Object-Oriented Design Lecture 11 CS 3500 Spring 2010 (Pucella) Tuesday, Feb 16, 2010

Object-Oriented Design Lecture 3 CSU 370 Fall 2007 (Pucella) Friday, Sep 14, 2007

Code Reuse: Inheritance

3 ADT Implementation in Java

17 Multiple Inheritance and ADT Extensions

20 Subclassing and Mutation

Lecture 5: Implementing Lists, Version 1

Equality for Abstract Data Types

Putting the fun in functional programming

6.037 Lecture 7B. Scheme Variants Normal Order Lazy Evaluation Streams

11 Parameterized Classes

6.001 Notes: Section 17.5

CS3500: Object-Oriented Design Fall 2013

The Java Type System (continued)

Object-Oriented Design Lecture 23 CS 3500 Fall 2009 (Pucella) Tuesday, Dec 8, 2009

Recursive Data and Recursive Functions

12 Implementing Subtyping

CS/ENGRD 2110 FALL Lecture 7: Interfaces and Abstract Classes

Algebraic Specifications

Intro. Scheme Basics. scm> 5 5. scm>

Object-Oriented Design Lecture 16 CS 3500 Fall 2010 (Pucella) Friday, Nov 12, 2010

Problem Set 4: Streams and Lazy Evaluation

CS450 - Structure of Higher Level Languages

CS/ENGRD 2110 SPRING Lecture 7: Interfaces and Abstract Classes

8 Hiding Implementation Details

CMPSCI 187: Programming With Data Structures. Lecture 12: Implementing Stacks With Linked Lists 5 October 2011

CSCI0170. Today s topics. Predicates Natural Number recursion Recursion Diagrams List recursion A first glance at the design recipe

15 Multiple Inheritance and Traits

Object-Oriented Design Lecture 5 CSU 370 Fall 2008 (Pucella) Friday, Sep 26, 2008

Midterm Exam (REGULAR SECTION)

CSE332: Data Abstractions Lecture 23: Programming with Locks and Critical Sections. Tyler Robison Summer 2010

A Model of Mutation in Java

Discussion 2C Notes (Week 3, January 21) TA: Brian Choi Section Webpage:

CMSC131. Inheritance. Object. When we talked about Object, I mentioned that all Java classes are "built" on top of that.

Lecture 10 Notes Linked Lists

8 Understanding Subtyping

INTERFACES IN JAVA. Prof. Chris Jermaine

Lecture 10 Notes Linked Lists

Whereweare. CS-XXX: Graduate Programming Languages. Lecture 7 Lambda Calculus. Adding data structures. Data + Code. What about functions

Types, Expressions, and States

SCHEME 10 COMPUTER SCIENCE 61A. July 26, Warm Up: Conditional Expressions. 1. What does Scheme print? scm> (if (or #t (/ 1 0)) 1 (/ 1 0))

Homework 12 Due Wednesday, 12/8/10

Collections and Combinatorial Search

CS152: Programming Languages. Lecture 7 Lambda Calculus. Dan Grossman Spring 2011

CS61B Lecture #3. Homework: Please see Homework #1 on the lab page. Last modified: Wed Sep 10 20:34: CS61B: Lecture #3 1

Programming Languages. Streams Wrapup, Memoization, Type Systems, and Some Monty Python

COMP 202 Recursion. CONTENTS: Recursion. COMP Recursion 1

Discussion 11. Streams

Cosc 241 Programming and Problem Solving Lecture 9 (26/3/18) Collections and ADTs

GRAMMARS & PARSING. Lecture 7 CS2110 Fall 2013

Array Based Lists. Collections

Recursion. What is Recursion? Simple Example. Repeatedly Reduce the Problem Into Smaller Problems to Solve the Big Problem

CS 61B Discussion 5: Inheritance II Fall 2014

INTERFACES IN JAVA. Prof. Chris Jermaine

CS 231 Data Structures and Algorithms, Fall 2016

infix expressions (review)

Object-Oriented Design Lecture 21 CSU 370 Fall 2008 (Pucella) Tuesday, Dec 9, 2007

21 Subtyping and Parameterized Types

Repetition Through Recursion

Lecture 24 Search in Graphs

COMP-202. Recursion. COMP Recursion, 2011 Jörg Kienzle and others

7 Code Reuse: Subtyping

Harvard School of Engineering and Applied Sciences CS 152: Programming Languages

CS/ENGRD 2110 SPRING 2018

CS61A Discussion Notes: Week 11: The Metacircular Evaluator By Greg Krimer, with slight modifications by Phoebus Chen (using notes from Todd Segal)

Topic 6: Inner Classes

Thread Safety. Review. Today o Confinement o Threadsafe datatypes Required reading. Concurrency Wrapper Collections

CS 455 Final Exam Fall 2013 [Bono] December 12, 2013

Dealing with Bugs. Kenneth M. Anderson University of Colorado, Boulder CSCI 5828 Lecture 27 04/21/2009

Linked Lists. private int num; // payload for the node private Node next; // pointer to the next node in the list }

Linked List Implementation of Queues

The Lambda Calculus. notes by Don Blaheta. October 12, A little bondage is always a good thing. sk

Lecture 3: Recursion; Structural Induction

Lecture Transcript While and Do While Statements in C++

Programming Languages. Thunks, Laziness, Streams, Memoization. Adapted from Dan Grossman s PL class, U. of Washington

Implementing a List in Java. CSE 143 Java. Just an Illusion? List Interface (review) Using an Array to Implement a List.

Discussion 4. Data Abstraction and Sequences

CS61A Summer 2010 George Wang, Jonathan Kotker, Seshadri Mahalingam, Eric Tzeng, Steven Tang

CIS Fall Data Structures Midterm exam 10/16/2012

Object-Oriented Design Lecture 14 CSU 370 Fall 2007 (Pucella) Friday, Nov 2, 2007

CMSC 341 Hashing (Continued) Based on slides from previous iterations of this course

CS 61A Interpreters, Tail Calls, Macros, Streams, Iterators. Spring 2019 Guerrilla Section 5: April 20, Interpreters.

Stacks (5.1) Abstract Data Types (ADTs) CSE 2011 Winter 2011

CS61B Spring 2016 Guerrilla Section 2 Worksheet

CS558 Programming Languages

Linked Lists

Insertions and removals follow the Fist-In First-Out rule: Insertions: at the rear of the queue Removals: at the front of the queue

Section 05: Solutions

Functional abstraction. What is abstraction? Eating apples. Readings: HtDP, sections Language level: Intermediate Student With Lambda

Functional abstraction

Topic 8: Lazy Evaluation

Streams. CS21b: Structure and Interpretation of Computer Programs Spring Term, 2004

Computer Programming. Basic Control Flow - Loops. Adapted from C++ for Everyone and Big C++ by Cay Horstmann, John Wiley & Sons

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define

CS 112 Introduction to Computing II. Wayne Snyder Computer Science Department Boston University

n! = 1 * 2 * 3 * 4 * * (n-1) * n

SML Style Guide. Last Revised: 31st August 2011

Lecture 24 Notes Search in Graphs

Transcription:

Object-Oriented Design Lecture 13 CSU 370 Fall 2008 (Pucella) Friday, Oct 31, 2008 Laziness For this lecture, I want to return to something that came up during the last homework, the third homework where you had to implement pictures. It is a sufficiently common phenomenon that we should make it precise. A Slight Detour First, a slight detour to talk about functional iterators, again. mileage we can get out of this simple concept. It s amazing how much We ve implemented functional iterators for Stacks a few times, you ve implemented picture iterators. We re pros. But man, what work. If we follow the recipe to the letter, we get a concrete subclass for every creator for our ADT, and each concrete subclass has a method getfunciterator that gives us back an iterator specific for the kind of data embodied by the concrete subclass. (See Lecture 11 for the Stack example.) That s a lot of classes to write. And the iterators are not doing things very much different than the class itself. This general approach of having a getfunciterator works well in general, and is doubly interesting for actual Java iterators, as we ll see. But if we have functional iterators and especially if we have immutable classes we want to iterator over, we can sometimes do something a bit more clever. Instead of having a method getfunciterator that extracts an iterator for an instance of an ADT, we can have an instance of the ADT itself act as an iterator. Let s do this for stacks. Here is the specification I have in mind: public static Stack empty (); public static Stack push (Stack, int); public boolean isempty (); public int top (); public Stack pop (); public String tostring (); public boolean haselement (); public Integer current (); public FuncIterator<Integer> advance (); empty().isempty() = true 1

push(s,i).isempty() = false push(s,i).top() = i push(s,i).pop() = s empty().tostring() = "<bottom of stack>" push(s,i).tostring() = i + " " + s.tostring() empty().haselement() = false push(s,i).haselement() = true push(s,i).current() = i push(s,i).advance() = s Note that stacks directly implement the FuncIterator<Integer> interface. Note also that the advance method is required to return a FuncIterator<Integer>. It is perfectly fine to return a stack here (see the specification for push(s,i).advance() because a stack will be a subclass of FuncIterator<Integer>, and subclassing says that it s fine to return a class that is more precise than a class you are expecting to return. (Can you reason out why?) Here is the corresponding implementation: Stack ADT with iterators public abstract class Stack implements FuncIterator<Integer> { public static Stack emptystack () { return new EmptyStack (); public static Stack push (Stack s, int i) { return new PushStack (s,i); public abstract boolean isempty (); public abstract int top (); public abstract Stack pop (); public abstract String tostring (); public abstract boolean haselement (); public abstract Integer current (); public abstract FuncIterator<Integer> advance (); Concrete subclass for empty() class EmptyStack extends Stack { public EmptyStack () { 2

public boolean isempty () { return true; public int top () { throw new IllegalArgumentException ("in EmptyStack.top()"); public Stack pop () { throw new IllegalArgumentException ("in EmptyStack.pop"); public String tostring () { return "<bottom of stack>"; public boolean haselement () { return false; public Integer current () { throw new IllegalArgumentException ("in EmptyStack.current()"); public FuncIterator<Integer> advance () { throw new IllegalArgumentException ("in EmptyStack.advance()"); Concrete subclass for push() class PushStack extends Stack { private int topval; private Stack rest; public PushStack (Stack s, int v) { topval = v; rest = s; public boolean isempty () { return false; public int top () { return this.topval; public Stack pop () { return this.rest; public String tostring () { return this.top() + " " + this.pop(); 3

public boolean haselement () { return true; public Integer current () { return new Integer (this.topval); public FuncIterator<Integer> advance () { return this.rest; Note that we need the current method to return an Integer, that is, a boxed integer (wrapped inside a class) because FuncIterator<T> requires T to be a class type. That s an annoyance we have to deal with. Streams Back to what I really wanted to talk about. In the homework, you had to define pictures. The key operation pictures was to draw them. Creators were provided to let you build more complicated pictures from simpler ones. One question I was asked quite a few times is when the lines are computed. Let s pick an example. A basic picture is a grid. The flip operation takes a picture and produces a new flipped picture. Now, when we flip a grid, do we go in an actual do the flipping of the lines in the grid? If we flip a more complex picture, do we go into the picture, and flip all the grids inside it? That turns out to be very hard to get right. If you tried it, you know what I mean. If not, then try to think how that would work. In fact, the problem is that if we flip a rotated picture, we need to flip all the lines that have been rotated in the underlying grids. The alternative is to be lazy. We do not do any work when creating a flipped picture. We simply record the underlying picture to be flipped. The work gets done if it s need, when we are asked to draw the flipped picture. Happily enough, this is exactly what comes out of the recipe I gave you. Here is the concrete subclass corresponding to the flip creator that my implementation uses:... in the abstract Picture class: public static Picture flip (Picture p) { return new PicFlip(p);... class PicFlip extends Picture { private Picture pic; public PicFlip (Picture p) { 4

pic = p; public void draw (Point a, Point b, Point c) { this.pic.draw(a.add(b),b.scale(-1),c); Amazingly simple. No work to be done when we construct a flipped picture. The work is done when we draw, and even then, it s not an amazing amount of work, because of the way we can use bounding boxes. This general approach, only doing the work when it s needed, and not at creator-invocation time, is called lazy construction, or just plain laziness. (In contrast, if we build something at creator-invocation time, we often call it eager construction.) Laziness is very powerful. In particular, it lets us work with infinite data rather straightforwardly. The classical example of infinite data is streams, which you can think of infinite lists. Of course, you cannot simply represent an infinite list directly by listing all its elements. Instead, a stream can be thought of simply as a promise to deliver its elements, if you ask for them. If you ask for the first 10 elements of the stream, it will compute them, and then give them to you. If you ask for the 200th element of the stream, it will compute it and give it to you. Until you ask for it, though, it is not explicitly represented. Just like in the pictures case, we can also create new streams from old streams, and here again, we do not do any work at creation time, only when we ask for elements of the stream. We implement the following interface for streams: public static Stream Cons (int, Stream); public static Stream Tail (Stream); public static Stream IntsFrom (int, Stream); public static Stream Sum (Stream, Stream); public static Stream Filter (Predicate<Integer>, Stream); public boolean haselement (); public Integer current (); public FuncIterator<Integer> advance(); other operations? public boolean equals (Object); public String tostring (); Note that we have streams be their own iterators, following our approach above. public abstract class Stream implements FuncIterator<Integer> { 5

public static Stream cons (int i, Stream s) { return new StreamCons(i,s); public static Stream tail (Stream s) { return new StreamTail(s); public static Stream intsfrom (int i, Stream s) { return new StreamIntsFrom(i,s); public static Stream sum (Stream s1, Stream s2) { return new StreamSum(s1,s2); public static Stream filter (Predicate<Integer> p, Stream s) { return new StreamFilter(p,s); public abstract boolean haselement (); public abstract Integer current (); public abstract FuncIterator<Integer> advance(); We now implement the concrete subclasses. All are rather trivial, and all do not do any work until asked to what the current() element of a stream is, or what the advance() stream is. Concrete subclass for cons() class StreamCons extends Stream { private Integer first; private Stream rest; public StreamCons (Integer f, Stream r) { first = f; rest = r; public boolean haselement () { return true; public Integer current () { return this.first; public FuncIterator<Integer> advance () { return this.rest; 6

Concrete subclass for tail() class StreamTail extends Stream { private Stream str; public StreamTail (Stream s) { this.str = s; public boolean haselement () { return true; public Integer current () { return this.str.advance().current(); public FuncIterator<Integer> advance () { return Stream.tail((Stream) this.str.advance()); For StreamTail, notice the cast required for advance to type check. You should be able to figure out why it s needed. (Hint: again, it s all about loss of typing information due to subclassing. See Lecture 12.) Concrete subclass for intsfrom() class StreamIntsFrom extends Stream { private Integer n; public StreamIntsFrom (Integer n) { this.n = n; public boolean haselement () { return true; public Integer current () { return this.n; public FuncIterator<Integer> advance () { return Stream.intsFrom(this.n+1); Concrete subclass for sum() 7

class StreamSum extends Stream { private Stream stream1; private Stream stream2; public StreamSum (Stream s1, Stream s2) { stream1 = s1; stream2 = s2; public boolean haselement () { return true; public Integer current () { return stream1.current() + stream2.current(); public FuncIterator<Integer> advance () { return Stream.sum((Stream) stream1.advance(), (Stream) stream2.advance()); The filter creator is a bit more interesting. It takes a predicate as argument, and intuitively returns the stream of all elements of the supplied stream argument for which the predicate is true. In Scheme, we can represent a predicate as a function (or a lambda), In Java and most OO language, we need to wrap a function you want to pass as an argument in an object. Let s define an interface for predicates over values of type T: public interface Predicate<T> { public boolean pred (T arg); Here is an example of a predicate over integers, that returns true if and only if an integer is not divisible by a given number: public class NotDivByPredicate implements Predicate<Integer> { private Integer divisibleby; private NotDivByPredicate (Integer y) { divisibleby = y; 8

public static NotDivByPredicate mk (Integer y) { return new NotDivByPredicate(y); public boolean pred (Integer x) { return (x % this.divisibleby!= 0); Thus, NotDivByPredicate.mk(4) returns a predicate that can used to test if an integer is not divisible by 4. Concrete subclass for filter() class StreamFilter extends Stream { private Predicate<Integer> pred; private Stream str; private Filter (Predicate<Integer> p, Stream str) { this.pred = p; this.str = str; public boolean haselement () { return true; public Integer current () { FuncIterator<Integer> tmp = this.str; loop until we have found an element that satisfies the predicate while (!(this.pred.pred(tmp.current()))) { tmp = tmp.advance(); return tmp.current(); public FuncIterator<Integer> advance() { FuncIterator<Integer> tmp = this.str; loop until we have found an element that satisfies the predicate while (!(this.pred.pred(tmp.current()))) tmp = tmp.advance(); return Stream.filter(this.pred, (Stream) tmp.advance()); 9

If we define a function for printing the first n elements out of a functional iterator over the integers: public static void printfirstnfromiterator (FuncIterator<Integer> it, int n) { int i; FuncIterator<Integer> tmp = it; for (i=0; i<n; i++) { System.out.println(" " + tmp.current()); tmp = tmp.advance(); We can now try out something such as: Stream s1 = Stream.intsFrom(3); Stream s2 = Stream.intsFrom(10); printfirstnfromiterator(stream.sum(s1,s2),10); This prints the first 10 elements of the stream obtained by summing the stream starting from 3 and the stream starting from 10. This yields: 13 15 17 19 21 23 25 27 29 31 As expected. Make sure you understand what is happening. As a cute exampe, we can try to compute, using (a variant of) the Sieve of Eratosthenes, the stream of all prime numbers. The sieve computes the list of prime numbers by essentially starting with all integers from 2 on, and then keeping 2 and removing all multiples of 2, then moving to the next unremoved integer (3), keeping it and removing all multiples of 3, 10

moving to the next unremoved integer (5), keeping it and removing all multiples of 5, and so on. You can convince yourself that what you are left with is the stream of all prime numbres. Here is a concrete subclass of Stream that implements the sieve the creator, instead of being shoved away into Stream, is here available under the name Sieve.mk(). Concrete subclass for sieve (no creator in Stream class) public class Sieve extends Stream { private Stream str; private Sieve (Stream s) { this.str = s; public static Stream mk (Stream s) { return new Sieve(s); public boolean haselement () { return true; public Integer current () { return this.str.current(); public FuncIterator<Integer> advance () { return Sieve.mk(Stream.filter(NotDivByPredicate.mk(this.str.current()), Stream.tail(this.str))); And indeed, executing Stream primes = Sieve.mk(Stream.intsFrom(2)); printfirstnfromiterator(primes,14); yields 2 3 5 7 11 13 11

17 19 23 29 31 37 41 43 Note, however, that this is far from being an efficient way for computing prime numbers, as you can tell immediately by running the above code. 12