260 likes | 372 Vues
In this lecture, we explore fundamental concepts of functional programming, focusing on using calculations to demonstrate that two functions are equal. We illustrate this with examples from class and homework assignments. Topics include laws governing monads, the sequence structure, and improving functions through calculations. We also delve into proofs by induction and the concept of qualified types, spotlighting the structure of pictures in programming—how to flatten and draw them while maintaining order. Key proofs and lemmas are discussed, reinforcing the connection between theory and practice in functional programming.
E N D
Lecture #11, Nov. 4, 2002 • Todays Topics • Using Calculation to prove 2 functions are equal • An example from class drawPic • Laws about Monads • Laws about sequence • Using Calculation to improve functions • An example from the first Homework assignment • Read Chapter 11 - Proofs by induction • Read Chapter 12 – Qualified types
Proof from Class drawPic :: Window -> Picture -> IO () drawPic w (Region c r) = drawReg w (c,r) drawPic w (p1 `Over` p2) = do { drawPic w p2 ; drawPic w p1 } drawPic w EmptyPic = return () flattenPict :: Picture -> [(Color,Region)] flattenPict EmptyPic = [] flattenPict (Region c r) = [(c,r)] flattenPict (p1 `Over` p2) = flattenPict p2 ++ flattenPict p1 draw2 w p = sequence (map (drawReg w) (flattenPict p))
We would like to Prove drawPict w p = draw2 w p • We can draw a picture OR • We can flatten a picture, then draw each of the regions in the order they appear in the flattened list. • This is important, because for some applications we want to maintain the list of regions for other reasons. • Recall the structure of a picture: data Picture = Region Color Region | Picture `Over` Picture | EmptyPic
Structure of the proof • To Prove : drawPic w p = draw2 w p • Prove 3 cases • case 1) p = EmptyPic prove:drawPic w EmptyPic = draw2 w EmptyPic • case 2) p = (Region c r) prove:drawPic w (Region c r) = draw2 w (Region c r) • case 3) p = (p1 `Over` p2) assume: a) drawPic w p1 = draw2 w p1 b) drawPic w p2 = draw2 w p2 prove:drawPic w (p1 `Over` p2) = draw2 w (p1 `Over` p2)
First a few laws • Laws about do and return (we saw these when we first studied monads) Monad1: do { a <- x; return a } = x Monad2: do { x <- return y; e } = e[y/x] Monad3: do { a <- do { b <- e; c}; d } = do { b <- e; a <- c; d } provided b not in FreeVars(d) Monad4: do { x<- a; b} = do {a; b} provided x not in FreeVars(b)
Draw2 uses sequence • Definition sequence :: [ IO () ] -> IO () S1: sequence [] = return () S2: sequence (x:xs) = do { x; sequence xs } • Lemmas Lemma 1: sequence [ x ] = x Lemma 2: sequence(a ++ b) = do {sequence a; sequence b} Lemma 3: map f x ++ map f y = map f (x ++ y)
Proof Lemma1 Lemma 1: sequence [ x ] = x sequence [ x ] = By def of [ x ] syntax sequence (x : []) = by def of sequence rule s2 do { x; sequence [] } = by def of sequence rule S1 do { x; return () } = by Monad4 backwards do { a <- x; return () } = Since a has type (), and () is unique do { a <- x; return a } = by Monad1 x
Lemma 2: sequence(a ++ b) = do {sequence a; sequence b} P{a} = sequence(a ++ b) = do {sequence a; sequence b} Proof by induction over the structure of a Base Case - Prove: sequence([] ++ b) = do {sequence []; sequence b} sequence([] ++ b) = by def of ++ sequence(b) = by Monad2 (backwards) do { x <- return (); sequence b } = by def sequence do { x <- sequence []; sequence b } = Monad4 do {sequence []; sequence b }
Induction Step • Assume: sequence(xs ++ b) = do {sequence xs; sequence b} • Prove: sequence((x:xs) ++ b) = do {sequence (x:xs); sequence b} sequence((x:xs) ++ b) = by def of ++ sequence( x : (xs ++ b)) = by def of sequence do { x; sequence (xs ++ b) } = by IH do { x; do {sequence xs; sequence b} } = by Monad3 do { do {x; sequence xs}; sequence b } = def of sequence do { sequence (x:xs); sequence b }
Back to the main proof • To Prove : drawPic w p = draw2 w p • Prove 3 cases • case 1) p = EmptyPic prove:drawPic w EmptyPic = draw2 w EmptyPic • case 2) p = (Region c r) prove:drawPic w (Region c r) = draw2 w (Region c r) • case 3) p = (p1 `Over` p2) assume: a) drawPic w p1 = draw2 w p1 b) drawPic w p2 = draw2 w p2 prove:drawPic w (p1 `Over` p2) = draw2 w (p1 `Over` p2)
case 1. p = EmptyPic drawPic w EmptyPic = draw2 w EmptyPic drawPic w EmptyPic = by def drawPict return () = by def sequence sequence [] = by def map sequence (map (drawReg w) []) = by def flattenPict sequence (map (drawReg w) (flattenPict EmptyPic)) = by def draw2 draw2 w EmptyPic
case 2. p = (Region c r) drawPic w (Region c r) = draw2 w (Region c r) drawPic w (Region c r) = by def drawPic drawReg w (c,r) = by Lemma1 sequence [ drawReg w (c,r) ] by def of map sequence (map (drawRegion w) [(c,r)]) by def flattenPict sequence (map (drawRegion w) (flattenPict (Region c r))) by def draw2 draw2 w (Region c r)
case 3. p = (a `Over` b) assume: a) drawPic w a = draw2 w a b) drawPic w b = draw2 w b prove: drawPic w (a `Over` b) = draw2 w (a `Over` b) drawPic w (a `Over` b) = by def drawPic do { drawPic w b; drawPic w a } = by IH do { draw2 w b; draw2 w a } = by def draw2 do { sequence (map (drawReg w) (flattenPict b)); sequence (map (drawReg w) (flattenPict a)) } =
Case 3. continued = by Lemma2 sequence ( (map (drawReg w) (flattenPict b)) ++ (map (drawReg w) (flattenPict a)) ) = by Lemma3 sequence (map (drawReg w) (flattenPict b ++ flattenPict a)) = def of flattenPict sequence (map (drawReg w) (flattenPict (a `Over` b))) = by def of draw2 draw2 (a `Over` b)
Ex2. Recall String to Integer • A function which converts a string of digits into an Int. Uses the function which ord which takes a Char as input and returns its ascii code ord ‘1’ --> 49 • Follows the "pipeline" analysis "167" --> ['1','6','7'] --> [49,54,55] --> [1,6,7] --> [(1,100),(6,10),(7,1)] --> [100, 60, 7] --> 167
String to Int (cont) • Then str2int is an easy composition str2int :: String -> Int str2int = sum . map (uncurry (*)) . explist . map (\ z -> z -(ord '0')) . map ord • The Key is the function explist explist [5,3,4] --> [(5,100),(3,10),(4,1)]
Key Function Explist • Useful intermediates reverse [1,10,100] [1,10,100] --> [100,10,1] zip [3,4] [100, 10, 1] --> [(3,100), (4,10)] • Definition explist zs = zip zs [ power 10 x | x <- reverse [0 .. n-1] ] where n = length zs
Another explist (we use this one in the improvement) explist = fst . foldr g ([],1) where z `g` (zs,n) = ((z,n) : zs, n * 10) • Suppose we start with [5,3,2] • Folding g leaves • 5 `g` (3 `g` (2 `g` ([],1))) • 5 `g` (3 `g` ((2,1):[],10)) • 5 `g` (3 `g` ([(2,1)],10)) • 5 `g` ((3,10):[(2,1)],100) • 5 `g` ([(3,10),(2,1)],100) • ([(5,100),(3,10),(2,1)],1000)
Using theory to improve functions Given the definitions: sum: sum = foldr (+) 0 uncurry: uncurry f (x,y) = f x y and the laws: map1: map f . map g = map (f . g) lambda1: (\ x -> e) . f = (\ x -> e[(f x)/x]) lambda2: f . (\ x -> e) = (\ x -> f e) mapfoldr: foldr c e . map f = (let d x y = c (f x) y in foldr d e) Improve the definition of str2int
Improvement 1 str2int = sum . map (uncurry (*)) . explist . map (\ z -> z -(ord '0')) . map ord • by map1 str2int = sum . map (uncurry (*)) . explist . map ((\z -> z -(ord '0')) . ord)
Improvement 2 str2int = sum . map (uncurry (*)) . explist . map ((\z -> z -(ord '0')) . ord) • by lambda1 str2int = sum . map (uncurry (*)) . explist . map (\ z -> ord z -(ord '0'))
Improvement 3 • by definition of sum str2int = foldr (+) 0 . map (uncurry (*)) . explist . map (\ z -> ord z -(ord '0')) • by mapfoldr str2int = (let d x y = (+)(uncurry (*) x) y in foldr d 0) . explist . map (\ z -> ord z -(ord '0'))
Improvement 4 str2int = (let d x y = (+)(uncurry (*) x) y in foldr d 0) . explist . map (\ z -> ord z -(ord '0')) • by operator use : (+) x y = x + y str2int = (let d x y = (uncurry (*) x) + y in foldr d 0) . explist . map (\ z -> ord z -(ord '0'))
Improvement 5 • patterns and definition of uncurry str2int = (let d (a,b) y = a*b + y in foldr d 0) . explist . map (\ z -> ord z -(ord '0')) • rearranging str2int = (foldr d 0) . explist . map f where d (a,b) y = a*b + y f z = ord z -(ord '0')
Improvement 6 • Using def of explist explist = fst . foldr g ([],1) where z `g` (zs,n) = ((z,n) : zs, n * 10)
Improvement 7 str2int = (foldr d 0) . fst . (foldr g ([],1)) . map f where d (a,b) y = a*b + y f z = ord z -(ord '0') z `g` (zs,n) = ((z,n) : zs, n * 10) • Using mapfoldr again str2int = (foldr d 0).fst.(foldr d2 ([],1)) where d (a,b) y = a*b + y d2 z (zs,n) =((ord z - ord ‘0’,n)::zs, n*10)