#lang racket
;; Racket has built-in streams, but this is the book implementation. Use
;; define-syntax-rule to create the new special forms my-delay and cons-stream.
;; STREAMS
(define (memo-proc proc)
(define already-run? false)
(define result false)
(lambda ()
(cond [(not already-run?)
(set! result (proc))
(set! already-run? true)
result]
[else result])))
(define-syntax-rule (my-delay e) (memo-proc (lambda () e)))
(define (my-force delayed-object) (delayed-object))
(define (stream-car s) (car s))
(define (stream-cdr s) (my-force (cdr s)))
;; Use this definition of cons-stream for memoized streams.
(define-syntax-rule (cons-stream a b) (cons a (my-delay b)))
;; ;; Use this definition of cons-stream for un-memoized streams.
;; (define-syntax-rule (no-memo-delay e) (lambda () e))
;; (define-syntax-rule (cons-stream a b) (cons a (no-memo-delay b)))
(define the-empty-stream empty)
(define stream-null? empty?)
(define (my-stream-ref s n)
(if (zero? n)
(stream-car s)
(my-stream-ref (stream-cdr s) (sub1 n))))
(define (my-stream-map proc s)
(if (stream-null? s)
the-empty-stream
(cons-stream (proc (stream-car s))
(my-stream-map proc (stream-cdr s)))))
(define (my-stream-filter pred s)
(cond [(stream-null? s) the-empty-stream]
[(pred (stream-car s))
(cons-stream (stream-car s)
(my-stream-filter pred (stream-cdr s)))]
[else (my-stream-filter pred (stream-cdr s))]))
(define (my-stream-for-each proc s)
(cond [(stream-null? s) 'done]
[else (proc (stream-car s))
(my-stream-for-each proc (stream-cdr s))]))
(define (display-stream s) (my-stream-for-each displayln s))
(define (stream-enumerate-interval low high)
(if (> low high)
the-empty-stream
(cons-stream low
(stream-enumerate-interval (add1 low) high))))
;;;;;;;;;;
;; 3.50 ;;
;;;;;;;;;;
(define (my-generalized-stream-map proc . argstreams)
(if (stream-null? (car argstreams))
the-empty-stream
(cons-stream (apply proc (map stream-car argstreams))
(apply my-generalized-stream-map
(cons proc (map stream-cdr argstreams))))))
;; TEST
(define s1 (stream-enumerate-interval 1 5))
(define s2 (stream-enumerate-interval 11 15))
(define t (my-generalized-stream-map + s1 s2))
(my-stream-ref t 3)
;; 18
(display-stream t)
;; 12
;; 14
;; 16
;; 18
;; 20
;; 'done
;;;;;;;;;;
;; 3.51 ;;
;;;;;;;;;;
(define (show x)
(displayln x)
x)
(define x (my-stream-map show (stream-enumerate-interval 0 10)))
;; WITH MEMOIZATION
(my-stream-ref x 5)
;; 0
;; 1
;; 2
;; 3
;; 4
;; 5
;; 5
(my-stream-ref x 7)
;; 6
;; 7
;; 7
;; Suppose proc has a side-effect, like show does here. How is this side-effect
;; expressed when we stream-map proc down a stream s? What would be the
;; difference if we used un-memoized streams?
;; Let t be (my-stream-map proc s). When t is evaluated, the stream-car is
;; evaluated, but evaluation of the stream-cdr is delayed. The stream-car of t is
;; proc of the stream-car of s. This application of proc will produce a
;; side-effect. But since a stream is stored as a pair consisting of a value and
;; a delayed expression, the stream-car will never again produce its side-effect
;; because that computation is never repeated. (In effect the definition of
;; stream automatically caches the stream-car.) This is true for both memoized
;; and un-memoized streams.
;; For later stream elements, however, there is a difference. With memoized
;; streams, later elements will only express their side-effect the first time they
;; are forced, simply because caching prevents the repetition of the computation
;; that produces the side-effect. With un-memoized streams, however, the
;; side-effect will be repeated every time the later elements are forced.
;; For example, repeating the above with un-memoized streams:
;; ;; WITHOUT MEMOIZATION
;; (my-stream-ref x 5)
;; ;; 0
;; ;; 1
;; ;; 2
;; ;; 3
;; ;; 4
;; ;; 5
;; ;; 5
;; (my-stream-ref x 7)
;; ;; 1 ; notice there is no 0 this time
;; ;; 2
;; ;; 3
;; ;; 4
;; ;; 5
;; ;; 6
;; ;; 7
;; ;; 7
;;;;;;;;;;
;; 3.52 ;;
;;;;;;;;;;
;; WITH MEMOIZATION
(define sum 0)
(define (accum x)
(set! sum (+ x sum))
sum)
(define seq (my-stream-map accum (stream-enumerate-interval 1 20)))
sum ; sum 1
;; 1
(define y (my-stream-filter even? seq))
sum ; sum 2
;; 6
(define z (my-stream-filter (lambda (x) (zero? (remainder x 5))) seq))
sum ; sum 3
;; 10
(my-stream-ref y 7)
;; 136
sum ; sum 4
;; 136
(display-stream z)
;; 10
;; 15
;; 45
;; 55
;; 105
;; 120
;; 190
;; 210
;; 'done
sum ; sum 5
;; 210
;; How are we getting these sums?
;; seq is: 1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 210
;; y is: 6 10 28 36 66 78 120 136 190 210
;; z is: 10 15 45 55 105 120 190 210
;; Whenever a new element of seq is evaluated, accum has the side-effect of adding
;; its index to sum.
;; At sum 1, seq is evaluated. So its stream-car is evaluated. So accum is
;; applied to 1. Hence sum = sum + 1 = 0 + 1 = 1.
;; At sum 2, y is evaluated. So its stream-car is evaluated. This means forcing
;; two more elements to get the first even element of seq. So accum is applied to
;; 2 and 3. Hence sum = sum + 2 + 3 = 1 + 2 + 3 = 6.
;; At sum 3, z is evaluated. So its stream-car is evaluated. This means forcing
;; another element from seq, the fourth element, in order to get the first element
;; divisible by 5. So accum is applied to 4. Hence sum = sum + 4 = 6 + 4 = 10.
;; At sum 4, (my-stream-ref y 7) evaluates the first eight elements of y, which
;; means evaluating the first 16 elements of seq (in order to get the first eight
;; even elements). The first four of these sixteen elements have already been
;; forced, so accum gets applied to 5, 6, 7, up through 16. Hence sum = sum + 5 +
;; 6 + 7 + ... + 16 = 10 + 5 + 6 + 7 + ... + 16 = 136.
;; At sum 5, all of the elements of z are evaluated, which means all twenty
;; elements of seq get evaluated. The first sixteen of those have already been
;; forced. So accum gets applied to 17, 18, 19, and 20. Hence sum = sum + 17 +
;; 18 + 19 + 20 = 136 + 17 + 18 + 19 + 20 = 210.
;; How would this change for un-memoized streams?
;; With un-memoized streams, stream elements repeat their side-effect every
;; time they are evaluated; except for the stream-car, which only ever expresses
;; its side-effect once, as discussed in 3.51.
;; Also note that seq is no longer deterministic, but its values will change
;; depending on how many times it has been invoked. This is because invoking seq
;; calls accum, which increases sum, which in turn changes accum and seq. This
;; means y and z will also change depending on how often seq has been invoked,
;; because they too depend on seq. This complicates the answers we get below.
;; ;; WITHOUT MEMOIZATION
;; (define sum 0)
;; (define (accum x)
;; (set! sum (+ x sum))
;; sum)
;; (define seq (my-stream-map accum (stream-enumerate-interval 1 20)))
;; sum
;; ;; 1
;; (define y (my-stream-filter even? seq))
;; sum
;; ;; 6
;; (define z (my-stream-filter (lambda (x) (zero? (remainder x 5))) seq))
;; sum
;; ;; 15
;; (my-stream-ref y 7)
;; ;; 162
;; sum
;; ;; 162
;; (display-stream z)
;; ;; 15
;; ;; 180
;; ;; 230
;; ;; 305
;; ;; 'done
;; sum
;; ;; 362
Comments
0 B
|👍
/👎
0 B
|👍
/👎