260 likes | 335 Views
Explore lazy evaluation concepts in programming streams through examples like infinite streams, prime numbers, Fibonacci sequence, and componentwise addition. Learn how to implement and work with lazy evaluation in functional programming.
E N D
Streams Contract is the same as pairs... (head (pair-stream x str)) = x (tail (pair-stream x str)) = str ...but order of evaluation is different (pair-stream x str) evaluates x immediately, delays evaluation of str (tail str) forces evaluation of the tail
Lazy Evaluation compute values only when needed implement with special form delay (delay expr) make a promise to evaluate expr when forced to (force delayed-expr) collect on the promise
An infinite stream (1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ... (define (ones <stream>) (pair-stream 1 ones)) Compare (define (ones <list>) (pair 1 ones)) ==> ???
An infinite stream (1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ... (define (ones <stream>) (pair-stream 1 ones)) Compare (define (ones <list>) (pair 1 ones)) ==> error: unbound identifier ones
(pair-stream 1 ones) =macro expansion=> (make <pair-stream> head-stream: 1 tail-stream: (delay ones)) =macro expansion=> (make <pair-stream> head-stream: 1 tail-stream: (method () ones)) ==> (1 . {proc () ones})
(head ones) ==> (head-stream ones) ==> 1 (tail ones) ==> (force (tail-stream ones)) ==> (force {proc () ones}) ==> ({proc () ones}) ==> ones ==> (1 . {proc () ones}) so ones is its own tail
Natural numbers (define integers-from (method ((n <integer>)) (pair-stream n (integers-from (inc n))))) (integers-from 42) is the infinite stream (42 43 44 45 46 47 48 49 50 51 52 ... (define (naturals <stream>) (integers-from 0)) naturals is the infinite stream (0 1 2 3 4 5 6 7 8 9 10 11 12 13 ...
(add-method filter (method ((test <boolean>) (s <stream>)) (cond ((empty? s) empty-stream) ((test (head s)) (pair-stream (head s) (filter test (tail s)))) (else: (filter test (tail s)))))) (define (divisible? <function>) (method ((x <integer>) (n <integer>)) (zero? (modulo x n)))) (define (divisible-by-3? <function>) (method ((x <integer>)) (divisible? x 3))) (define (threes <stream>) (filter divisible-by-3? naturals)) this is the infinite stream (0 3 6 9 12 15 ...
(define (print-stream <function>) (method ((s <stream>)) (print (head s)) (print-stream (tail s)))) (print-stream naturals) What does (print-stream naturals) do? Print 0, force the tail (integers-from (inc 0)) evaluate (inc 0) ==> 1 evaluate integers-from ==> (1 . {proc () (integers-from (inc 1))}) Print 1, force the tail... (2 . {promise (integers-from (inc 2))}) Print 2, force the tail... (3 . {promise (integers-from (inc 3))}) Print 3, force the tail...
Sieve of Eratosthenes (300 BC) • 2 is prime • a number n > 2 is prime iff • it is not divisible by any smaller prime
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 ...
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 ...
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 ...
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 ...
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 ...
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 ...
(define (sieve <function>) (method ((s <stream>)) (bind ((next-prime (head s))) ;head is prime (pair-stream next-prime (sieve ;recursively sieve tail after (filter ;removing all multiples of head (method ((x <integer>)) (not (divisible? x next-prime))) (tail s))))))) (define (primes <stream>) (sieve (integers-from 2))) (print-stream primes) 2 3 5 7 11 13 17 19 23 ...
Componentwise addition of streams (define (add-streams <function>) (method ((a <stream>) (b <stream>)) (cond ((empty-stream? a) b) ((empty-stream? b) a) (else: (pair-stream (+ (head a) (head b)) (add-streams (tail a) (tail b))))))) (add-streams (a0 a1 a2 a3 ...) (b0 b1 b2 b3...)) ==> (a0+b0 a1+b1 a2+b2 a3+b3 ...)
Natural numbers revisited (define (naturals <stream>) (pair-stream 0 (add-streams ones naturals))) naturals = (0 1 2 3 4 5 6 7 8 9 10 11 12 ... ones = (1 1 1 1 1 1 1 1 1 1 1 1 1 ... sum = (1 2 3 4 5 6 7 8 9 10 11 12 13 ... Pair 0 onto the front and we get naturals back: (0 1 2 3 4 5 6 7 8 9 10 11 12 13 ...
Fibonacci numbers F(0) = 0 F(1) = 1 F(n) = F(n-1) + F(n-2) for n 2 (define (fibs <stream>) (pair-stream 0 (pair-stream 1 (add-streams fibs (tail fibs))))) fibs = (0 1 1 2 3 5 8 13 21 34 ... (tail fibs) = (1 1 2 3 5 8 13 21 34 55 ... sum = (1 2 3 5 8 13 21 34 55 89 ... Pair 0 and 1 onto the front and we get fibs back: fibs = (0 1 1 2 3 5 8 13 21 34 55 89 ...
A more efficient version (define (fibs-from <function>) (method ((a <integer>) (b <integer>)) (pair-stream a (fibs-from b (+ a b))))) (define (efficient-fibs <stream>) (fibs-from 0 1)) this version: O(n) to create the first n fibs last version: O(p^n) where p = (1+sqrt(5))/2
Divergent computations It’s possible to create streams with nothing in them that are not the empty stream— even worse, empty? can't detect them! (define (lose <stream>) (filter odd? (filter even? naturals)))
Merge — merge two streams into one Example: joint bank account Dexter stream: 1/1/98 23:59 deposit $500 1/15/98 23:59 deposit $500 2/1/98 23:59 deposit $500 2/15/98 23:59 deposit $500 ... Fran stream: 1/3/98 10:36 withdraw $200 1/7/98 14:15 withdraw $150 1/10/98 15:44 withdraw $500 1/19/98 9:05 withdraw $825 ...
Dexter merge account balances Fran Merge by date & time so that all transactions on the output stream are in the right order
(1 3 6 12 17 18 20 ... (2 5 9 10 19 22 31 ... merge (1 2 3 5 6 9 10 12 17 18 19 20 ...
(define (merge <function>) (method ((s1 <stream>) (s2 <stream>)) (cond ((empty? s1) s2) ((empty? s2) s1) ((< (head s1) (head s2)) (pair-stream (head s1) (merge (tail s1) s2))) (else: (pair-stream (head s2) (merge s1 (tail s2)))))))