CS 541 Lecture -*- Outline -*- > module Streams where > import Prelude hiding (zipWith) > infixl 0 # * Streams (aka Lazy or Infinite Lists) and Lazy evaluation ** Goals ------------------------------------------ THE AMAZING UTILITY OF STREAMS (LAZY LISTS, INFINITE LISTS) Want to: ------------------------------------------ ... capture common patterns (paradigms) once and for all esp. pipeline architecture ... efficiently and clearly express algorithms *** Example from Abelson and Sussman, 3.4.1 show code first, then discuss ------------------------------------------ TWO EXAMPLES TO GENERALIZE > data Tree a = Lf > | Br a (Tree a) (Tree a) > square x = x * x > sum_odd_squares Lf = 0 > sum_odd_squares (Br v t1 t2) = > (if odd v > then square v > else 0) > + sum_odd_squares t1 > + sum_odd_squares t2 > fib :: Integer -> Integer > fib 0 = 0 > fib 1 = 1 > fib n = fib(n-2) + fib(n-1) > odd_fibs n = next 1 > where next k = > if k > n > then [] > else let f = (fib k) > in > if odd(f) > then f : next(1 + k) > else next (1 + k) ------------------------------------------ Q: What do sum_odd_squares and odd_fibs do? sum_odd_squares = enumerate | filter odd | map square | accumulate (+) 0 odd_fibs = enumerate | map fib | filter odd Q: how are these similar? Q: what parts of each are enumeration? mapping? filtering?accumulation? want to separate out the enumeration, mapping, filtering, accumulation *** expressing enumeration, mapping, filtering, accumulation as functionals ------------------------------------------ THE PARTS ENUMERATION > -- of trees > preorder Lf = [] > preorder (Br v t1 t2) = > v : (preorder t1 ++ preorder t2) > -- of integers from 1 to k > to k = [1 .. k] ACCUMULATION, FILTERING, MAPPING ------------------------------------------ Q: What have we seen that does accumulation like this? foldr Q: What have we seen that does filtering? filter Q: What have we seen that does mapping? map *** putting the pieces together ------------------------------------------ PUTTING THE PIECES TOGETHER ------------------------------------------ > sum_odd_squares2 tree = > (foldr (+) 0 > (map square > (filter odd > (preorder tree)))) > odd_fibs2 n = > (filter odd > (map fib > (to n))) easy to combine these in different ways: e.g., list of squares of fibionnaci numbers list of odd squares... *** improving the syntax The pipelines seem to go backwards from the way we write the architectural diagram. But we can do better. Q: What do we want? we want to write the applications the other way around. ------------------------------------------ PIPELINE SYNTAX > sum_odd_squares3 tree = > preorder tree # filter odd > # map square > # foldr (+) 0 > odd_fibs3 n = > to n # map fib > # filter odd ------------------------------------------ To do that, we declare, (above in this file), a very low precedence operator (#) that is left associative reverse function application. ... module Streams where infixl 0 # > x # f = f x ** Power of map, filter, accumulate paradigm R. Waters (1979), about 60% of traditional Fortran programs have loops that can be viewed as instances of map, filter and accumulate *** representing standard data as lazy lists Q: How would you represent a polynomial as a lazy list? a vector? a matrix? polynomials as stream of coefficients vectors as streams of numbers matricies as streams of vectors ** Lazy evaluation, computing with infinite objects *** Infinite lists (streams) **** computations with infinite streams page 171 of Abelson and Sussman, sieve takes a stream S and returns a stream consisting of head of S and all numbers in the tail not divisible by the head ------------------------------------------ SIEVE TO FIND PRIMES > p `divides` x = (x `mod` p) == 0 > sieve :: Integral a => [a] -> [a] > sieve (p:ps) = > p : sieve > (filter (\x -> > not (p `divides` x)) > ps) > primes :: [Integer] > primes = sieve [2 ..] > first_50 = take 50 primes ------------------------------------------ Q: How could you use this to write a test for being a prime? is_prime x = x `elem` primes -- no good if not in the list! Does your solution regenerate the list of primes each time? Does it generate more primes than needed? *** implicit definition of streams use lazy evaluation ------------------------------------------ IMPLICITLY DEFINED STREAMS > ones :: [Integer] > ones = 1 : ones > zipWith :: (a->b->c) -> [a]->[b]->[c] > zipWith f (a:as) (b:bs) > = f a b : zipWith f as bs > zipWith _ _ _ = [] > add_lists :: [Integer] -> [Integer] > -> [Integer] > add_lists = zipWith (+) > integers :: [Integer] > integers = 1 : add_lists ones integers > nats :: [Integer] > nats = 0 : add_lists ones nats > fibs :: [Integer] > fibs = 0 : 1 > : (add_lists (tail fibs) > fibs) ------------------------------------------ Note that the following is wrong. > bad_add_lists xs ys = [x+y | x <- xs, y <- ys] For example, bad_add_lists [1..2] [1..2] = [2, 3, 3, 4] This is because the list comprehension generates all pairings and then the results. For example [(x,y) | x <- [1..2], y <- [1..2]] = [(1,1), (1,2), (2,1), (2,2)]