330 likes | 437 Views
five. implementing streams using classes. Exercise 1. [repeat list count ] Returns count copies of list , in order [random-pattern count elements … ] Returns a list of count objects randomly chosen from elements. Limitations of lists. What wrong with these?
E N D
five implementing streams using classes
Exercise 1 • [repeat list count] • Returns count copies of list, in order • [random-pattern count elements …] • Returns a list of count objects randomly chosen from elements
Limitations of lists • What wrong with these? • [repeat [list 1 2 3 4]∞] • [random-pattern ∞ 1 2 3 4 5] • SuperCollider lets you make patterns that run infinitely • But lists have to be finite length because they store all the elements in memory at once • And memory is finite
Streams • Streams are like lists • They “contain” a set of objects • In a specified order • But unlike lists • You can’t ask for an arbitrary elelement (i.e. you can’t to get or second) • You can only ask for the “next” element • On the other hand, they can be infinitely long
The basic idea • Rather than storing all the elements in memory at once • We store just enough information to figure out what the next element should be • And then run code to figure it out each time we need a new element
Implementing streams using classes • Make a “base class” called Stream • Implement different Pseq, etc. operators as subclasses of stream • Each stream supports two generic functions • [next-element s]Returns the next element of s • [reset s]Goes back to the first element • [done? s]Returns true is s has no more elements Stream ListStream RandomStream JoinedStream RandomStreamNoRepeats
Last time • [class [Name fields …]Parent-typeadditional-fields …] • Creates a new data type • Has all the fields of Parent-type, plus fields, and additional-fields • Note: last week, this was called “class-type”; I’ve decided to shorten the name • [new Type field-values …] • Creates a new data object of the specified Type (class) • Field-values are used as values for fields mentioned in the class definition, but not additional-fields.
Last time • [generic-procedure] • Makes a kind of procedure whose behavior depends on the types of its arguments • [define-method [generic [type arg] …]code …] • Specifies what code to run a generic procedure is given arguments with a certain set of types • When each arg is passed a value of its respective type, then code is run.
Code for the base class [using Packages.SimpleClasses] [define Stream [class [Stream] Object]]] [define next-element [generic-procedure]] [define reset[generic-procedure]] [define done? [generic-procedure]]
The ListStream class • Simple example • Idea: make a stream from a list • [new ListStream list]Should return a stream that will generate each of list, in order, and then end
Code for the ListStream class [define ListStream [class [ListStream list] Stream position]] [define-method [reset [ListStream s]][s.position ← 0]] [define-method [next-element [ListStream s]][with answer = [get s.list s.position] [s.position ← [+ s.position 1]] answer]] [define-method [done? [ListStream s]][= s.position [length s.list]]]
Initializing objects [define ListStream [class [ListStream list] Stream position]] • Okay we know that the list field gets its value from the call to new: [new ListStream list] • But what value does position get? • Answer: null • This is a pain, because we need position to be a number
The initialize generic procedure [define-method [initialize [ListStream s]] [s.position ← 0]] • When an object is created • Meta calls the initialize procedure on it • Before returning it from new • So you can provide any special code you want to run by providing a method for your new class
But wait … [define-method [initialize [ListStream s]] [s.position ← 0]] [define-method [reset [ListStream s]][s.position ← 0]] • This is the same code as reset…
But wait … [define-method [initialize [ListStream s]] [reset s]] [define-method [reset [ListStream s]][s.position ← 0]] • So why not just have initialize call reset?
But wait … [define-method [initialize [Stream s]] [reset s]] [define-method [reset [ListStream s]][s.position ← 0]] • In fact, we can make it a method for the whole Stream class, not just for ListStreams • It will still run for ListStreams because they’re a kind of Stream
Random streams • ListStreams don’t let us do anything we couldn’t do with lists • Let’s think about how to implement the random streams in such a way that the streams can be “infinite” • [new RandomStream list-of-elements desired-length] • Should generate a stream of desired-length elements from list-of-elements • The objects need to store • The list of objects to choose (randomly) from • The number of objects we’ve generated • The number of objects we need to generate
RandomStream code [define RandomStream [class [RandomStream list desired-length] Stream current-count]] [define-method [reset [RandomStream s]][s.current-count ← 0]] [define-method [done? [RandomStream s]][= s.current-count s.desired-length]] [define-method [next-element [RandomStream s]][s.current-count ← [+ s.current-count 1]][get s.list [random-integer 0 [length s.list]]]] Note no initialize method Why?
Testing it out [define stream→list[stream max-length → [with elements = [new System.Collections.ArrayList] count = 0 [while [and [< count max-length] [not [done? stream]]] [count ← [+ count 1]] [elements.Add [next-element stream]]] elements]]] • We need some way of testing out whether it works • The easiest way is to make a procedure that will read a list of numbers from the stream and print them
Testing it out ► [stream→list [new RandomStream [list 1 2 3] ∞] 20] [2 2 1 3 3 3 2 3 1 3 3 2 1 1 1 3 2 2 1 1] ►
Random without repeats • Now let’s do the equivalent of SuperCollider’s Pxrand • All we need to do is • Remember one additional piece of information: the last number we generated • Change the next-element method
RandomStreamNoRepeats code [define RandomStreamNoRepeats [class [RandomStreamNoRepeats list desired-length] RandomStream last-value]] [define-method [reset [RandomStreamNoRepeats s]][s.last-value ← null][call-next-method]] [define-method [next-element [RandomStreamNoRepeats s]][with new-value = s.last-value [while [= new-value s.last-value] [new-value ← [get s.list [random-integer 0 [length s.list]]]]] [s.last-value ← new-value] new-value]]
Testing it out ► [stream→list [new RandomStreamNoRepeats [list 1 2] 100] 20] [1 1 1 2 2 2 1 1 2 2 2 2 2 1 2 2 2 1 1 1] • Oops….
RandomStreamNoRepeats code [define RandomStreamNoRepeats [class [RandomStreamNoRepeats list desired-length] RandomStream last-value]] [define-method [reset [RandomStreamNoRepeats s]][s.last-value ← null][call-next-method]] [define-method [next-element [RandomStreamNoRepeats s]][with new-value = s.last-value [while [= new-value s.last-value] [new-value ← [get s.list [random-integer 0 [length s.list]]]]] [s.current-count ← [+ s.current-count 1]][s.last-value ← new-value] new-value]]
Testing it out ► [stream→list [new RandomStreamNoRepeats [list 1 2] 100] 20] [1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2] • Oops….
RandomStreamNoRepeats code [define RandomStreamNoRepeats [class [RandomStreamNoRepeats list desired-length]RandomStream last-value]] [define-method [reset [RandomStreamNoRepeats s]][s.last-value ← null][call-next-method]] [define-method [next-element [RandomStreamNoRepeats s]][with new-value = s.last-value [while [= new-value s.last-value] [new-value ← [get s.list [random-integer 0 [length s.list]]]]] [s.current-count ← [+ s.current-count 1]] [s.last-value ← new-value] new-value]] This time, the parent class is RandomStream
RandomStreamNoRepeats code [define RandomStreamNoRepeats [class [RandomStreamNoRepeats list desired-length] RandomStream last-value]] [define-method [reset [RandomStreamNoRepeats s]][s.last-value ← null][call-next-method]] [define-method [next-element [RandomStreamNoRepeats s]][with new-value = s.last-value [while [= new-value s.last-value] [new-value ← [get s.list [random-integer 0 [length s.list]]]]] [s.current-count ← [+ s.current-count 1]] [s.last-value ← new-value] new-value]] Notice there’s no done? method Why?
RandomStreamNoRepeats code [define RandomStreamNoRepeats [class [RandomStreamNoRepeats list desired-length] RandomStream last-value]] [define-method [reset [RandomStreamNoRepeats s]][s.last-value ← null][call-next-method]] [define-method [next-element [RandomStreamNoRepeats s]][with new-value = s.last-value [while [= new-value s.last-value] [new-value ← [get s.list [random-integer 0 [length s.list]]]]] [s.current-count ← [+ s.current-count 1]] [s.last-value ← new-value] new-value]] calls the method of the parent class (so we don’t need to init s.current-count)
Pasting streams together • Now we need a way to paste streams together • We just make a new class • That remembers the list of streams to paste together • And its position in the list • And calls the substreams’ next-element methods to get new elements • [new JoinedStream list-of-streams] • Should return a stream that generates all the elements of all the streams
JoinedStream class [define JoinedStreams [class [JoinedStreams stream-list] Stream position]] [define-method [reset [JoinedStreams j]][for-each reset j.stream-list][j.position ← 0]] • To reset the stream, we reset all the substreams • Now, how do we figure out if we’re done?
The done? method [define-method [done? [JoinedStreams j]] [and [= j.position [− [length j.stream-list] 1]] [done? [get j.stream-list j.position]]]] • Check that we’re on the last substream • And the last substream is done • How do we generate the next element?
The next-element method [define-method [next-element [JoinedStreams j]][when [done? [get j.stream-list j.position]] [j.position ← [+ j.position 1]]][next-element [get j.stream-list j.position]]] • We check whether we need to move on to the next stream yet • Note that when is a variant of if that doesn’t take an else clause • Then we generate an element from whatever the current stream is
Testing it out ► [stream→list [new JoinedStreams [list [new ListStream [list 3 2 1]] [new RandomStreamNoRepeats [list 1 2] 100]]] 20] [3 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2] • It works! ListStream RandomStreamNoRepeats