If you need help reviewing Streams, take a look at these resources:
Each question has a "Toggle Solution" button -- click it to reveal that question's solution.
Conceptual Questions
Question 1
What are the advantages to using a stream as opposed to a regular list?
A stream can be used to "store" infinite amounts of data. Because the data is not computed until you need it, you can use memory more efficiently.
Question 2
What is a promise? How is this related to the force and delay functions?
A promise is an object that encapsulates an expression. You can create promises
by using the delay function:
scm> (delay (/ 1 0))
#[promise (not forced)]
The expression (/ 1 0), when evaluated, will cause a ZeroDivisionError.
However, since we are delaying its evaluation, we don't actually compute (/ 1
0) just yet.
We can force evaluation of a promise's expression by using the force
function:
scm> (define p (delay (/ 1 0)))
p
scm> p
#[promise (not forced)]
scm> (force p)
ZeroDivisionError
It is only after we try to force the promise p that we evaluate (/ 1 0)
and cause the ZeroDivisionError.
Question 3
Consider the following stream:
(define my-stream
(cons-stream 1 (cons-stream 2 (cons-stream 3 nil))))
What type of object is (cdr my-stream)
(cdr my-stream) — in other words, the rest of my-stream, is a promise
object.
Remember that
(cons-stream first rest)is equivalent to(cons first (delay rest))
What type of object is (stream-cdr my-stream)?
(stream-cdr my-stream) is a stream, specifically the stream (cons-stream
2 (constream 3 nil)).
Remember that
(stream-cdr my-stream)is equivalent to(force (cdr my-stream))
Code-Writing questions
Question 4
Define a function called odd-stream that returns a stream of odd-numbered
integers, starting from a given odd number n (you can always assume n is
odd).
(define (odd-stream n)
; YOUR-CODE-HERE
)
In other words, odd-stream should have the following behavior:
scm> (stream-to-list (odd-stream 1) 5)
1 3 5 7 9
(define (odd-stream n)
(cons-stream n (odd-stream (+ n 2))))
Question 5
Write a function add-streams that takes two (possibly infinite) streams called
s1 and s2 and returns a stream whose elements are elements of s1 and s2
added pairwise. For example calling add-streams on
s1 = 1 2 3 4 ...
s2 = 5 6 7 8 ...
would return the following stream:
6 8 10 12 ...
If either of streams is finite, the returned stream contain as many elements as the shorter input stream.
(define (add-streams s1 s2)
; YOUR-CODE-HERE
)
(define (add-streams s1 s2)
(if (or (null? s1) (null? s2))
nil
(cons-stream (+ (car s1) (car s2))
(add-streams (stream-cdr s1)
(stream-cdr s2))))
)
Question 6
Write a function reduce-first-k() that reduces the first k
elements in a given stream, using a given combiner. If the stream has
fewer than k elements, reduce all the elements in the stream.
(define (reduce-first-k s k combiner)
; YOUR-CODE-HERE
)
You may assume the input stream
scontains at least 2 elements, and thatkalways starts out as greater than or equal to 2. Do not usestream-to-list.
; Tests
scm> (define s (ints 1))
s
scm> (reduce-first-k s 4 +) ; 1 + 2 + 3 + 4
10
scm> (define s1 (cons-stream 1 (cons-stream 2 nil))
s1
scm> (reduce-first-k s1 4 +) ; 1 + 2
3
(define (reduce-first-k s k combiner)
(if (or (= k 2) (null? (cdr (cdr s))))
(combiner (car s)
(car (cdr s)))
(combiner (car s)
(reduce-first-k (stream-cdr s)
(- k 1)
combiner))))
Question 7
Given the following function, list the first 5 numbers in the Stream
that my-stream returns.
(define (my-stream)
(cons-stream 0
(add-streams (ints 1)
(map-stream (lambda (x) (* 3 x))
(ints 1)))))
0, 4, 8, 12, 16
Here's how I like to solve these types of questions. We know that the first
element of my-stream is 0, but we don't know what the rest of the elements
are. Assign variables to those unknown elements:
my-stream = 0 a b c d
Our goal is to figure out what a, b, and c are. How do we compute the rest
of my-stream? With the second argument of cons-stream:
(add-streams (ints 1)
(map-stream (lambda (x) (* 3 x))
(ints 1)))))
Here, we are adding two streams together:
(ints 1): this is just the stream1, 2, 3, 4, ...(map-stream (lambda (x) (* 3 x)) (ints 1))))): this is the stream1, 2, 3, 4, ..., but with each element multiplied by 3. In other words, this stream contains the elements3, 6, 9, 12, ...
In other words, the rest of my-stream (remember we called this a, b, c, d is
going to be the result of adding 1, 2, 3, 4, ... and 3, 6, 9, 12, ....
Diagramatically:
my-stream = 0 | a b c d
--------------|------------
| 1 2 3 4
+ | 3 6 9 12
--|------------
| 4 8 12 16
Thus, the first 5 elements of my-stream are 0, 4, 8, 12, and 16.