Lecture 6B: Streams Pt. 2
the key idea is that we decouple the apparent order of events in our
programs from the actual order of events in the computer
(define (nth-stream n s) (if (= n 0) (head s) (nth-stream (-1+ n) (tail s))))
elements aren't calculated until they are forced
elements are calculated when they are forced
(define (integers-from n) (cons-stream n (integers-from (+ n 1)))) (define integers (integers-from 1)) (define (no-seven x) (not (= (remainder x 7) 0))) (define no-sevens (filter no-seven integers))
the sieve of eratosthenes
for finding all primes
(define (sieve s) (cons-stream (head s) (sieve (filter (lambda (x) (not (divisible? x (head s)))) (tail s))))) (define primes (sieve (integers-from 2)))
–> break
there's another way to think about stream programming
– not operating on distinct elements, but streams as a whole
(define (add-streams s1 s2) (cond ((empty-stream? s1) s2) ((empty-stream? s2) s1) (else (cons-stream (+ (head s1) (head s2)) (add-streams (tail s1) (tail s2)))))) (define (scale-stream c s) (map-stream (lambda (x) (* x c)) s))
(define ones (cons-stream 1 ones)) ;; infinite stream of 1s (define ints (cons-stream 1 (add-streams ints ones)))
harry: this is very sneaky
(define (integral s initial-value dt) (define int (cons-stream initial-value (add-streams (scale-stream dt s) int))) int)
(define fibs (cons-stream 0 (cons-stream 1 (add-streams fibs (tail fibs)))))
"this is like learning recursion again, but instead of recursive
procedures, we have recursive data"
"this shouldn't be surprising to you" as there is no difference
between data and procedures
(define y (integral dy 1 .001)) (define dy (map square y)) ;; this doesn't work ;; recursively defined
delayed integral:
(define (integral delayed-s initial-value dt) (define int (cons-stream initial-value (let ((s (force delayed-s))) (add-streams (scale-stream dt s) int)))) int)
(define y (integral (delay dy) 1 .001)) (define dy (map square y)) ;; this does work ;; bc of delay
–> break
"before the break, something nasty started to happen"
the explicit use of delay is messy
we could rewrite the language to attach a delay to every procedure
it would be a "normal order" evaluation language
vs what we've been using, "applicative order"
instead of evaluating arguments, just provide promises, passing
promises around until you reach a primitive
applicative order carries a price:
we are trying to abstract away time, but if we go too far,
we lose expressiveness in favor of elegance
we can no longer really express iterative procedures
BUT there is another, more striking reason:
normal order and side effects don't mix
"it's very confusing for a very deep reason":
we've thrown away time
the debate over FUNCTIONAL PROGRAMMING
a purely functional language has no side effects
no assignment
a bank acct w/o state:
(define (make-deposit-account balance deposit-stream) (cons-stream balance (make-deposit-account (+ balance (head deposit-stream)) (tail deposit-stream))))
"the poor ppl sitting at the bank account window have the misfortune
to exist in time"