?? Programming with Streams Reading assignment

Slides:



Advertisements
Similar presentations
Types and Programming Languages Lecture 7 Simon Gay Department of Computing Science University of Glasgow 2006/07.
Advertisements

Type Inference David Walker COS 320. Criticisms of Typed Languages Types overly constrain functions & data polymorphism makes typed constructs useful.
22C:19 Discrete Structures Induction and Recursion Fall 2014 Sukumar Ghosh.
© M. Winter COSC 4P41 – Functional Programming Testing vs Proving Testing –uses a set of “typical” examples, –symbolic testing, –may find errors,
Getting started with ML ML is a functional programming language. ML is statically typed: The types of literals, values, expressions and functions in a.
Chapter 11 Proof by Induction. Induction and Recursion Two sides of the same coin.  Induction usually starts with small things, and then generalizes.
0 PROGRAMMING IN HASKELL Chapter 12 – Lazy evaluation and infinite lists Slides not from Hutton.
0 PROGRAMMING IN HASKELL Chapter 4 - Defining Functions.
Cse536 Functional Programming Lecture #14, Nov. 10, 2004 Programming with Streams –Infinite lists v.s. Streams –Normal order evaluation –Recursive streams.
0 PROGRAMMING IN HASKELL Chapter 6 - Recursive Functions Most of this should be review for you.
Recursion.
Chapter 14 Programming With Streams. Streams  A stream is an infinite sequence of values.  We could define a special data type for them: data Stream.
Advanced Programming Handout 11 Programming With Streams (SOE Chapter 14)
Recursion on Lists Lecture 5, Programmeringsteknik del A.
0 PROGRAMMING IN HASKELL Chapter 4 - Defining Functions.
CS5205Haskell1 CS5205: Foundation in Programming Languages Basics of Functional Programming.
Haskell Chapter 5, Part II. Topics  Review/More Higher Order Functions  Lambda functions  Folds.
CS321 Functional Programming 2 © JAS Programming with Streams A stream is never finite and could be defined as special polymorphic type data stream.
1 Recursive Definitions and Structural Induction CS/APMA 202 Rosen section 3.4 Aaron Bloomfield.
1 Proving Properties of Recursive List Functions CS 270 Math Foundations of CS Jeremy Johnson.
Chapter 13 Recursion Copyright © 2016 Pearson, Inc. All rights reserved.
Lecture 12.
Conditional Expressions
REPETITION CONTROL STRUCTURE
Introduction to Recursion
RECURSION.
Lesson #6 Modular Programming and Functions.
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Spring 2017.
Theory of Computation Lecture 4: Programs and Computable Functions II
6.001 SICP Variations on a Scheme
Lesson #6 Modular Programming and Functions.
A lightening tour in 45 minutes
Haskell Chapter 4.
CS2210:0001Discrete Structures Induction and Recursion
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Winter 2013.
Disjunctive Normal Form
Ch. 8 – Sequences, Series, and Probability
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Zach Tatlock Winter 2018.
Lesson #6 Modular Programming and Functions.
Proving Properties of Recursive List Functions
PROGRAMMING IN HASKELL
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Spring 2013.
Streams Sections 3.5.1,3.5.2 Pages
PROGRAMMING IN HASKELL
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Spring 2016.
PROGRAMMING IN HASKELL
Dynamic Scoping Lazy Evaluation
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Autumn 2017.
CSE341: Programming Languages Lecture 12 Equivalence
Functional Programming
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Autumn 2018.
PROGRAMMING IN HASKELL
CSCE 314: Programming Languages Dr. Dylan Shell
Streams, Delayed Evaluation and a Normal Order Interpreter
Haskell Types, Classes, and Functions, Currying, and Polymorphism
PROGRAMMING IN HASKELL
Lesson #6 Modular Programming and Functions.
CSE-321 Programming Languages Introduction to Functional Programming
CSCE 314: Programming Languages Dr. Dylan Shell
Testing vs Proving Testing uses a set of “typical” examples,
PROGRAMMING IN HASKELL
Common Core Math 8 India Walton
CSE341: Programming Languages Lecture 12 Equivalence
PROGRAMMING IN HASKELL
Brett Wortzman Summer 2019 Slides originally created by Dan Grossman
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Spring 2019.
Presentation transcript:

?? Programming with Streams Reading assignment Infinite lists v.s. Streams Normal order evaluation Recursive streams Stream Diagrams Lazy patterns memoization Inductive properties of infinite lists Reading assignment Chapter 14. Programming with Streams Chapter 15. A module of reactive animations

Infinite lists v.s. Streams data Stream a = a :^ Stream a A stream is an infinite list. It is never empty We could define a stream in Haskell as written above. But we prefer to use lists. This way we get to reuse all the polymorphic functions on lists.

Infinite lists and bottom twos = 2 : twos twos = 2 : (2 : twos) twos = 2 : (2 : (2 : twos)) twos = 2 : (2 : (2 : (2 : twos))) bot :: a bot = bot What is the difference between twos and bot ? Sometimes we write z for bot

Normal Order evaluation Why does head(twos) work? Head (2 : twos) Head(2 : (2 : twos)) Head (2: (2 : (2 : twos))) The outermost – leftmost rule. Outermost Use the body of the function before its arguments Leftmost Use leftmost terms: (K 4) (5 + 2) Be careful with Infix: (m + 2) `get` (x:xs)

Normal order continued Let let x = y + 2 z = x / 0 in if x=0 then z else w Where f w = if x=0 then z else w where x = y + 2 Case exp’s case f x of [] -> a ; (y:ys) -> b

Recursive streams Unfold this a few times fibA 0 = 1 fibA 1 = 1 fibA n = fibA(n-1) + fibA(n-2) Unfold this a few times fibA 8 = fibA 7 + fibA 6 = (fibA 6 + fibA 5) + (fibA 5 + fibA 4) = ((fibA 5 + fibA 4) + (fibA 4 + fibA 3)) +((fibA 4 + fibA 3) + (fibA 3 + fibA 2))

Fibonacci Stream This is much faster! And uses less resources. Why? 1 1 2 3 5 8 13 21 … fibonacci sequence + 1 2 3 5 8 13 21 34 … tail of fibonacci sequence 2 3 5 8 13 21 34 55 …tail of tail of fibonacci sequence fibs :: [ Integer ] fibs = 1 : 1 : (zipWith (+) fibs (tail fibs)) This is much faster! And uses less resources. Why?

Abstract on tail of fibs fibs = 1 : 1 : (add fibs (tail fibs)) Add x y = zipWith (+) x y Abstract on tail of fibs fibs = 1 : 1 : (add fibs (tail fibs)) = 1 : tf where tf = 1 : add fibs (tail fibs) where tf = 1 : add fibs tf Abstract on tail of tf where tf = 1 : tf2 tf2 = add fibs tf Unfold add tf2 = 2 : add tf tf2

Abstract and unfold again fibs = 1 : tf where tf = 1 : tf2 tf2 = 2 : add tf tf2 = 1 : tf tf2 = 2 : tf3 tf3 = add tf tf2 tf3 = 3 : add tf2 tf3 tf is used only once, so eliminate = 1 : 1 : tf2 where tf2 = 2 : tf3

Again This can go on forever. But note how the sharing makes the inefficiencies of fibA go away. fibs = 1 : 1 : 2 : tf3 where tf3 = 3 : tf4 tf4 = 5 : add tf3 tf4 fibs = 1 : 1 : 2 : 3 : tf4 where tf4 = 5 : tf5 tf5 = 8 : add tf4 tf5

Stream Diagrams fibs = [1,1,2,3,5,8,…] (:) [1,2,3,5,8, …] 1 (:) Streams are “wires” along which values flow. Boxes and circles take wires as input and produce values for new wires as output. Forks in a wire send their values to both destinations A stream diagram corresponds to a Haskell function (usually recursive) (:) [1,2,3,5,8, …] 1 (:) [2,3,5,8,…] 1 add

Example Stream Diagram counter :: [ Bool ] -> [ Int ] counter inp = out where out = if* inp then* 0 else* next next = [0] followedBy map (+ 1) out [0] if* inp out +1 next

Example [0] +1 if* next out counter :: [ Bool ] -> [ Int ] counter inp = out where out = if* inp then* 0 else* next next = [0] followedBy map (+ 1) out [0] if* F... 0... +1 1... out next

Example [0] +1 if* next out counter :: [ Bool ] -> [ Int ] counter inp = out where out = if* inp then* 0 else* next next = [0] followedBy map (+ 1) out [0] if* F:F.. 0:1.. +1 1:2.. out next

Example [0] +1 if* next out counter :: [ Bool ] -> [ Int ] counter inp = out where out = if* inp then* 0 else* next next = [0] followedBy map (+ 1) out [0] if* F:F:T.. 0:1:0.. +1 0:1:2 1:2:1.. out next

Client, Server Example type Response = Integer type Request = Integer client :: [Response] -> [Request] client ys = 1 : ys server :: [Request] -> [Response] server xs = map (+1) xs reqs = client resps resps = server reqs Typical. A set of mutually recursive equations

Abstract on (tail reqs) = 1 : tr where tr = server reqs client ys = 1 : ys server xs = map (+1) xs reqs = client resps resps = server reqs reqs = client resps = 1 : resps = 1 : server reqs Abstract on (tail reqs) = 1 : tr where tr = server reqs Use definition of server where tr = 2 : server reqs abstract where tr = 2 : tr2 tr2 = server reqs Since tr is used only once = 1 : 2 : tr2 where tr2 = server reqs Repeat as required

Lazy Patterns Now what happens . . . We can’t unfold Suppose client wants to test servers responses. clientB (y : ys) = if ok y then 1 :(y:ys) else error "faulty server" where ok x = True server xs = map (+1) xs Now what happens . . . Reqs = client resps = client(server reqs) = client(server(client resps)) = client(server(client(server reqs)) We can’t unfold

Solution 1 Rewrite client client ys = 1 : (if ok (head ys) then ys else error "faulty server") where ok x = True Pulling the (:) out of the if makes client immediately exhibit a cons cell Using (head ys) rather than the pattern (y:ys) makes the evaluation of ys lazy

Solution 2 – lazy patterns In Haskell the ~ before a pattern makes it lazy client ~(y:ys) = 1 : (if ok y then y:ys else err) where ok x = True err = error "faulty server” Calculate using where clauses Reqs = client resps = 1 : (if ok y then y:ys else err) where (y:ys) = resps = 1 : y : ys = 1 : resps

Memoization Unfolding we get: fibsFn :: () -> [ Integer ] fibsFn () = 1 : 1 : (zipWith (+) (fibsFn ()) (tail (fibsFn ()))) Unfolding we get: fibsFn () = 1:1: add (fibsFn()) (tail (fibsFn ())) = 1 : tf where tf = 1:add(fibsFn())(tail(fibsFn())) But we can’t proceed since we can’t identify tf with tail(fibsFn()). Further unfolding becomes exponential.

memo1 We need a function that builds a table of previous calls. Memo : (a -> b) -> (a -> b) 1 2 3 4 5 Memo fib x = if x in_Table_at i then Table[i] else set_table(x,fib x); return fib x Memo1 builds a table with exactly 1 entry.

Using memo1 mfibsFn x = let mfibs = memo1 mfibsFn in 1:1:zipWith(+)(mfibs())(tail(mfibs())) Main> take 20 (mfibsFn()) [1,1,2,3,5,8,13,21,34,55,89,144,233,377, 610,987,1597,2584,4181,6765]

Inductive properties of infinite lists Which properties are true of infinite lists take n xs ++ drop n xs = xs reverse(reverse xs) = xs Recall that z is the error or non-terminating computation. Think of z as an approximation to an answer. We can get more precise approximations by: ones = 1 : ones z 1 : z 1 : 1 : z 1 : 1 : 1 : z

Proof by induction To do a proof about infinite lists, do a proof by induction where the base case is z, rather than [] since an infinite list does not have a [] case (because its infinite). 1) Prove P{z} 2) Assume P{xs} is true then prove P{x:xs} Auxiliary rule: Pattern match against z returns z. I.e. case z of { [] -> e; y:ys -> f }

Example Prove: P{x} == (x ++ y) ++ w = x ++ (y++w) Prove P{z} (z ++ y) ++ w = z ++ (y++w) Assume P{xs} (xs ++ y) ++ w = xs ++ (y++w) Prove P{x:xs} (x:xs ++ y) ++ w = x:xs ++ (y++w)

Base Case (z ++ y) ++ w z ++ w z z ++ (y++w) (z ++ y) ++ w = z ++ (y++w) (z ++ y) ++ w pattern match in def of ++ z ++ w z z ++ (y++w)

Induction step Assume P{xs} Prove P{x:xs} (x:xs ++ y) ++ w Def of (++) (xs ++ y) ++ w = xs ++ (y++w) Prove P{x:xs} (x:xs ++ y) ++ w = x:xs ++ (y++w) (x:xs ++ y) ++ w Def of (++) (x:(xs ++ y)) ++ w x :((xs ++ y) ++ w) Induction hypothesis x : (xs ++ (y ++ w)) (x:xs) ++ (y ++ w)