310 likes | 402 Views
Lecture #6, Oct 13, 2004. Reading Assignments Read chapter 5 of the Text Polymorphic and Higher-Order Functions Read chapter 6 of the Text Shapes III: Perimeters of Shapes Assignment #3 – Now in Assignments directory Assigned: Oct. 13, 2004
E N D
Lecture #6, Oct 13, 2004 • Reading Assignments • Read chapter 5 of the Text Polymorphic and Higher-Order Functions • Read chapter 6 of the Text Shapes III: Perimeters of Shapes • Assignment #3 – Now in Assignments directory • Assigned: Oct. 13, 2004 • Due, in class: Wednesday, Oct. 20, 2004 • Today’s Topics • Polymorphic Functions • Polymorphic datatypes • Type Constructors define polymorphic Constructor functions • Recursive datatypes • Higher Order functions • Perimeters of Shapes
Polymorphic Length len :: [a] -> Int len [] = 0 len (x:xs) = 1 + len xs • Polymorphic functions don’t “look at” their polymorphic arguments. • They use the same code now matter what the type of their polymorphic arguments. “a” is a type variable. It is lowercase to distinguish it from types, which are uppercase.
Polymorphism • Consider: tag1 x = (1,x) ? :type tag1 tag1 :: a -> (Int,a) • Other functions have types like this consider (++) ? :type (++) (++) :: [a] -> [a] -> [a] ? :type ([1,2]++) ([1,2] ++) :: [Int] -> [Int] • What are some other polymorphic functions and their types? • id :: • reverse :: • head :: • tail :: • (:) ::
Polymorphic data structures • Polymorphism originates from data structures that don’t care what kind of data they store. id :: a -> a -- The ultimate -- polymorphic function reverse :: [a] -> [a] -- lists tail :: [a] -> [a] head :: [a] -> a (:) :: a -> [a] -> [a] tag1 :: Num a => b -> (a,b) -- tuples • How do we define new data structures with “holes” that can be polymorphic?
Parametric Type Constructors The type parameter to Option causes it’s constructors to be polymorphic • Polymorphic types data Option a = NONE | SOME a • Note NONE is a constant, SOME is a function • We can have: type example with that type Option Int SOME 3 Option String SOME "x" Option (Int,Bool) SOME (3,True) even Option (Option Int) • Give some examples with the type Option (Option Int)
Why use Option? pos :: Eq a => a -> [a] -> Option Int pos a l = let find a [] n = NONE find a (x:xs) n = if a==x then SOME n else find a xs (n+1) in find a l 0 ? pos 4 [1,2,3,4,5] SOME 3 ? pos 4 [1,5,6,7] NONE • Option is a type constructor since it constructs new types from old ones.
Types Constructors with multiple inputs data Pair a b = Pair(a,b) data Pair2 a b = Pair2 a b • In Haskell sometimes the Type constructor and the value constructor have the same name. ? :t Pair(2,"x") Pair (2,"x") :: Pair Int [Char] ? :t Pair ((),Pair (2,3)) Pair ((),Pair (2,3)) :: Pair () (Pair Int Int) • Domain of constructors - To use tuples or not? That is the question! ? :t Pair Pair :: (a,b) -> Pair a b ? :t Pair2 Pair2 :: a -> b -> Pair2 a b
Recursive types Lists are predefined, but could be defined this way • Well known and loved lists data [a] = [] | a : [a] • You can define your own ! data Mylist a = Nil | Cons (a,Mylist a) • But be warned! The two are not the same type. ? :t Cons Cons :: (a,Mylist a) -> Mylist a ? Cons(3,2:[]) ERROR: Cannot derive instance in expression *** Expression : Cons (3,[ 2 ]) *** Required instance: Monad Mylist
Recursive type constructors with multiple inputs data Twolist a b = Twonil | Acons (a,Twolist a b) | Bcons (b,Twolist a b) append2 Twonil ys = ys append2 (Acons(a,xs)) ys = Acons(a,append2 xs ys) append2 (Bcons(b,xs)) ys = Bcons(b,append2 xs ys) rev2 Twonil = Twonil rev2 (Acons(a,t)) = append2 (rev2 t) (Acons(a,Twonil)) rev2 (Bcons(b,t)) = append2 (rev2 t) (Bcons(b,Twonil)) ? :t rev2 rev2 :: Twolist a b -> Twolist a b ? append2 (Acons(2,Twonil))(Bcons(4,Twonil)) Acons (2,Bcons (4,Twonil))
Recursive type constructors with 0 inputs data Nat = Zero | Succ Nat; • Is there any polymorphism here? Why not? • Write the addition function for Nat? add :: Nat -> Nat -> Nat add = • Write a fun from positive Int to Nat. toNat :: Int -> Nat ? toNat 3 Succ (Succ (Succ Zero)) toNat =
Polymorphism from functions as arguments • Another source of polymorphism comes from functions which take functions as arguments. applyTwice f x = f(f x) Main> :t applyTwice applyTwice :: (a -> a) -> a -> a • What’s the type of z below? z f x y = (f x, f y)
Polymorphism:Functions returned as values • Consider: k x = (\ y -> x) ? (k 3) 5 3 • What’s the type of K? • Another Example: compose f g = \ x -> f (g x) • What’s the type of compose ?
Higher order functions abstract control • Higher order functions capture control patterns transList :: [Vertex] -> [Point] transList [] = [] transList (p:ps) = trans p : transList ps putCharList :: String -> [IO ()] putCharList [] = [] putCharList (c:cs) = putChar c : putChaList cs map f [] = [] map f (x:xs) = (f x):(map f xs)
When do you define a higher order function? • Recognizing patterns is the key mysum [] = 0 mysum (x:xs) = (+)x (mysum xs) myprod [] = 1 myprod (x:xs) = (*)x (myprod xs) myand [] = True myand (x:xs) = (&&)x (myand xs) • Note the similarities in definition and in use ? mysum [1,2,3] 6 ? myprod [2,3,4] 24 ? myand [True, False] False
Abstracting myfoldr ope [] = e myfoldr ope (x:xs) = op x (myfoldr ope xs) ? :t myfoldr myfoldr :: (a -> b -> b) -> b -> [a] -> b ? myfoldr (+) 0 [1,2,3] 6 ?
Using foldr • Once we’ve captured a pattern we should use it! mysum = foldr (+) 0 myprod = foldr (*) 1 myand = foldr (&&) True • Study the definitions below. Define equivalent functions in terms of foldr. map f [] = [] map f (x:xs) = (f x):(map f xs) map f = foldr . . . append [] ys = ys append (x:xs) ys = x : (append xs ys) append xs ys = foldr . . .
foldl • Let: x = [1,2,3,4] foldr :: (a -> b -> b) -> b -> [a] -> b foldr (+) e x = 1 + (2 + (3 + e)) foldl :: (b -> a -> b) -> b -> [a] -> b foldl (+) e x = (((e + 1) + 2) + 3) + 4 • Can you define foldl?
Why foldl and foldr? • Grouping from the left or the right matters in some computations. • Consider the two defintions concat1 :: [[a]] -> [a] concat1 = foldr (++) [] concat2 :: [[a]] -> [a] concat2 = foldl (++) [] • Which is more efficient? concat1 [x,y,z] = x ++ (y ++ z) concat2 [x,y,z] = (x ++ y) ++ z
Reverse rev [] = [] rev (x:xs) = (rev xs) ++ [x] • What’s the cost (in number of (:)’s) for rev [1,2,3,4]? • Can we do better? • Use an accumulating parameter rev2 [] ys = ys rev2 (x:xs) ys = . . . rev x = rev2 x [] • Can we write rev2 as a fold? • Which one do we use foldr or foldl?
Artihmetic Sequences • Special syntax for computing lists with regular properties. [1 .. 6] = [1,2,3,4,5,6] [1,3 .. 9] = [1,3,5,7,9] • Infinite lists too! take 9 [1,3..] = [1,3,5,7,9,11,13,15,17] take 5 [5..] = [5,6,7,8,9]
The perimeter of a Shape • To compute the perimeter we need a function with four equations (1 for each constructor of Shape) • The first three are easy … perimeter :: Shape -> Float perimeter (Rectangle s1 s2) = 2*(s1+s2) perimeter (RtTriangle s1 s2) = s1 + s2 + sqrt(s1^2+s2^2) perimeter (Polygon pts) = foldl (+) 0 (sides pts) • Provided we can compute a list of the length of the sides of a polygon. This shouldn’t be too difficult since we can compute the distance between two points with distBetween s1 s2 s1 s2
Sides is easy recursively sides :: [Vertex] -> [Side] sides [] = [] sides (p:pts) = aux (p:pts) where aux (p1:p2:pts) = (distBetween p1 p2) : (aux (p2:pts)) aux (pn:[]) = distBetween pn p : [] -- aux [pn] = [distBetween pn p] • But can we do it as a seasoned functional programmer might do it?
Visualize what’s happening B • The list of vertex, vs =[A,B,C,D,E] • distance between (A,B) (B,C) (C,D) (D,E) (E,A) • Can we compute these pairs as a list? [(A,B),(B,C),(C,D),(D,E),(E,A)] • Yes by zipping the following two lists: [A,B,C,D,E] [B,C,D,E,A] zip vs ((tail vs)++[head vs]) A C E D
Second version of sides sides2 :: [Vertex] -> [Side] sides2 pts = zipWith distBetween pts (tail pts ++ [head pts]) In class exercise, define: zipWith zipWith f ...
Perimeter of an Ellipse • The perimeter of an ellipse is given by the summation of an infinite series. For an ellipse with radii r1 and r2 p = 2pr1(1 - S si) where s1 = 1/4 e2 si+1 = si (2i-1)(2i-3) e2 for i>= 1 4i2 e = sqrt(r1* r1 - r2 * r2)/r1 Given si its is easy to compute the next value in the series s i+1
Computing the series nextEl:: Float -> Float -> Float -> Float nextEl e s i = s*(2*i-1)*(2*i-3)*(e^2) / (4*i^2) • We want to compute [s1,s2,s3, …] • for fixed e aux s i = nextEl e s i [s1, aux s1 1, aux (aux s1 1) 2, aux (aux (aux s1 1) 2) 3, …] si+1 = si (2i-1)(2i-3) e2 4i2 Can we capture this pattern?
Scanl (scan from the left) [s1, s2 = f s1 1, s3 = f s2 2 = f(f s1 1) 2, s4 = f s3 3 = f(f (f s1 1) 2) 3, …] scanl :: (a -> b -> b) -> b -> [a] -> [b] scanl f seed [] = seed : [] scanl f seed (x:xs) = seed : scanl f newseed xs where newseed = f x seed scanl aux s1 [1,2 ..]
We get the following pattern s1 = 1/4 e2 s1 = 0.25 * ((r1^2 - r2^2) / r1) where r1 and r2 are the radii of the ellipse [s1, s2 = aux s1 1, s3 = aux s2 2, s4 = aux s3 3, …]
e = sqrt (r1^2 - r2^2) / r1 = 0.699854 [s1 = 0.122449, s2 = 0.0112453, s3 = 0.00229496, s4 = 0.000614721, s5 = 0.000189685, …] Note how quickly the series gets real small ... r1 = 2.1 r2 = 1.5
Putting it all together This is s1 perimeter (Ellipse r1 r2) | r1 > r2 = ellipsePerim r1 r2 | otherwise = ellipsePerim r2 r1 where ellipsePerim r1 r2 = let e = sqrt (r1^2 - r2^2) / r1 s = scanl aux (0.25*e^2) (map intToFloat [2..]) aux s i = nextEl e s i test x = x > epsilon sSum = foldl (+) 0 (takeWhile test s) in 2*r1*pi*(1 - sSum)
Assignment #3, Also in the assignments web page Be sure and include a listing (1-2 pages) which shows how you tested your programs in the Hugs system. 1) Write a function "wordsL" such that (wordsL "This is a sentence.") returns ["This", "is","a","sentence."]. It breaks a string into a list of strings. It breaks the strings (and throws away the separators) at blanks ( ' ' ), tabs ( '\t' ), and newlines ( '\n' ). Hint: takeWhile and dropWhile are very useful. e.g. (takeWhile even [2,4,5,6,7]) => [2,4]. There is a function called words in the prelude that does exactly this. Write your own version. 2) Exercise 3.1 page 49 of the text 3) Use the IO monad to write a function when given two strings as input, opens a file with the first strings name, and copys it to another file, with the name of the second string. copy :: String -> String -> IO () . Be sure and test your function in the input you hand in. 4) Write a program "pal" which reads a string from the user, and then prints on the screen if that string is a palindrome. (A palindrome is a string which reads the same both forwards and backwards. For example "hah" and "abCba".) pal :: IO () 5) Using the primitive drawing functions of chapter 4 of the text, open a graphics window and draw a picture of a "house".