Let's consider still another way to represent sequences in Scheme: We can implement a sequence as an object with internal state, an object that processes messages that are transmitted to it (as arguments).
In particular, our ``sequence objects'' should respond to four messages,
represented by the four symbols at-end?, next, so-far,
and reset.
The idea is that we can advance through a given sequence by repeatedly
sending it the next message, which should cause the object to
generate and return the next element of the sequence.
The object should respond to the at-end? message by returning a
Boolean value indicating whether it can generate another element. If so,
the object should return #t; if not -- that is, if all of the
elements of the sequence have been returned in previous calls to next -- the object should return #f.
Sending the object the reset message should cause it to start over
at the beginning of the sequence (so that it responds to the next next message with the first element of the sequence). The value that the
object returns when it receives the reset message is unspecified; in
other words, it could be anything, and programmers who send this message
should not try to extract any information from that return value.
Finally, when the object receives a so-far message, it returns the
number of elements that it has generated and returned since receiving the
last reset message (or since it was created, if it has never
received a reset message).
It is an error to send a sequence object the next message when it
cannot generate any more elements (that is, when it would respond to the
at-end? message with #t). It is also an error to send any
message other than one of these four to a sequence object.
To illustrate these ideas, here is the definition for an object one-to-four that represents the finite sequence of integers from 1 to 4:
(define one-to-four
(let ((next-element 1))
(lambda (message)
(cond ((eq? message 'at-end?) (< 4 next-element))
((eq? message 'next)
(if (< 4 next-element)
(error "NEXT: ONE-TO-FOUR has no more elements.")
(let ((current next-element))
(set! next-element (+ next-element 1))
current)))
((eq? message 'reset) (set! next-element 1))
((eq? message 'so-far) (- next-element 1))
(else (error (string-append "ONE-TO-FOUR: "
(symbol->string message)
" is not a recognized sequence message.")))))))
And here is the transcript of some interactions with the one-to-four
object:
> (one-to-four 'next) 1 > (one-to-four 'next) 2 > (one-to-four 'at-end?) #f > (one-to-four 'so-far) 2 > (one-to-four 'reset) > (one-to-four 'next) 1 > (one-to-four 'clear) [bug] ONE-TO-FOUR: clear is not a recognized sequence message. > (one-to-four 'next) 2 > (one-to-four 'next) 3 > (one-to-four 'so-far) 3 > (one-to-four 'next) 4 > (one-to-four 'at-end?) #t > (one-to-four 'next) [bug] NEXT: ONE-TO-FOUR has no more elements.
Define and test an object that represents the infinite sequence of squares of non-negative integers (0, 1, 4, 9, 16, ...).
Define and test an object that represents the empty sequence (which has no elements).
Define and test a list->sequence-object procedure that
takes any list as argument and returns a sequence object whose successive
elements are the successive elements of the list.
Define and test a sequence-object->list procedure that takes as
argument any sequence object that represents a finite sequence and
returns a list of the elements of the sequence it represents (in the same
order).
Define and test a sequence-object-cumulate procedure that takes any
sequence object as its argument and returns another sequence object, one
that represents the sequence of cumulative sums of elements of the given
sequence object.
This exercise will be due at 9 a.m. on Monday, April 5.
This document is available on the World Wide Web as
http://www.cs.grinnell.edu/~stone/courses/scheme/exercises/7.xhtml
created March 26, 2004
last revised March 26, 2004