
RECURSIVE LIST PROCESSING CONTINUED Concatenating sublists: ➜ Lists can be nested, e.g., [[5, 6, 2], [], [4, 2]] :: [[Int]] ➜ concat merges sublists into one list, e.g., concat [[5, 6, 2], [], [4, 2]] ⇒ [5, 6, 2, 4, 2] ➜ We may specify the functionality of concat as concat [xs1, xs2, ... , xsn] = xs1 ++ xs2 ++ ··· ++ xsn ✧ Full definition: concat :: [[a]] -> [a] concat [] = [] concat (xs:xss) = xs ++ concat xss RECURSIVE LIST PROCESSING CONTINUED 1 RECURSIVE LIST PROCESSING CONTINUED Concatenating sublists: ➜ Lists can be nested, e.g., [[5, 6, 2], [], [4, 2]] :: [[Int]] ➜ concat merges sublists into one list, e.g., concat [[5, 6, 2], [], [4, 2]] ⇒ [5, 6, 2, 4, 2] ➜ We may specify the functionality of concat as concat [xs1, xs2, ... , xsn] = xs1 ++ xs2 ++ ··· ++ xsn ✧ Full definition: concat :: [[a]] -> [a] concat [] = [] concat (xs:xss) = xs ++ concat xss RECURSIVE LIST PROCESSING CONTINUED 1-A Appending a list: ➜ The function ++ is an infix operator (Functions with alphanumeric names are used prefix) (++) :: [a] -> [a] -> [a] ➜ ++ consumes and simultaneously produces a list ➜ Let us look at [5, 6, 2] ++ [4, 2]: (5:(6:(2:[]))) ++ (4:(2:[])) ⇒ 5:(6:(2:(4:(2:[])))) ➜ What can we observe? Essentially, ++ replaces the [] of the first list with the second list. ✧ Full definition: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys) RECURSIVE LIST PROCESSING CONTINUED 2 Appending a list: ➜ The function ++ is an infix operator (Functions with alphanumeric names are used prefix) (++) :: [a] -> [a] -> [a] ➜ ++ consumes and simultaneously produces a list ➜ Let us look at [5, 6, 2] ++ [4, 2]: (5:(6:(2:[]))) ++ (4:(2:[])) ⇒ 5:(6:(2:(4:(2:[])))) ➜ What can we observe? Essentially, ++ replaces the [] of the first list with the second list. ✧ Full definition: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys) RECURSIVE LIST PROCESSING CONTINUED 2-A Appending a list: ➜ The function ++ is an infix operator (Functions with alphanumeric names are used prefix) (++) :: [a] -> [a] -> [a] ➜ ++ consumes and simultaneously produces a list ➜ Let us look at [5, 6, 2] ++ [4, 2]: (5:(6:(2:[]))) ++ (4:(2:[])) ⇒ 5:(6:(2:(4:(2:[])))) ➜ What can we observe? Essentially, ++ replaces the [] of the first list with the second list. ✧ Full definition: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys) RECURSIVE LIST PROCESSING CONTINUED 2-B Appending a list: ➜ The function ++ is an infix operator (Functions with alphanumeric names are used prefix) (++) :: [a] -> [a] -> [a] ➜ ++ consumes and simultaneously produces a list ➜ Let us look at [5, 6, 2] ++ [4, 2]: (5:(6:(2:[]))) ++ (4:(2:[])) ⇒ 5:(6:(2:(4:(2:[])))) ➜ What can we observe? Essentially, ++ replaces the [] of the first list with the second list. ✧ Full definition: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys) RECURSIVE LIST PROCESSING CONTINUED 2-C Stepwise execution of ++: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys) Let us look at [5, 6, 2] ++ [4, 2]: (5:(6:(2:[]))) ++ (4:(2:[])) (1) ⇒ 5:( (6:(2:[])) ++ (4:(2:[])) ) (2) ⇒ 5:(6:( (2:[]) ++ (4:(2:[])) )) (3) ⇒ 5:(6:(2:( [] ++ (4:(2:[])) ))) (4) ⇒ 5:(6:(2:( 4:(2:[]) ))) (5) RECURSIVE LIST PROCESSING CONTINUED 3 Stepwise execution of ++: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys) Let us look at [5, 6, 2] ++ [4, 2]: (5:(6:(2:[]))) ++ (4:(2:[])) (1) ⇒ 5:( (6:(2:[])) ++ (4:(2:[])) ) (2) ⇒ 5:(6:( (2:[]) ++ (4:(2:[])) )) (3) ⇒ 5:(6:(2:( [] ++ (4:(2:[])) ))) (4) ⇒ 5:(6:(2:( 4:(2:[]) ))) (5) RECURSIVE LIST PROCESSING CONTINUED 3-A Stepwise execution of ++: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys) Let us look at [5, 6, 2] ++ [4, 2]: (5:(6:(2:[]))) ++ (4:(2:[])) (1) ⇒ 5:( (6:(2:[])) ++ (4:(2:[])) ) (2) ⇒ 5:(6:( (2:[]) ++ (4:(2:[])) )) (3) ⇒ 5:(6:(2:( [] ++ (4:(2:[])) ))) (4) ⇒ 5:(6:(2:( 4:(2:[]) ))) (5) RECURSIVE LIST PROCESSING CONTINUED 3-B Stepwise execution of ++: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys) Let us look at [5, 6, 2] ++ [4, 2]: (5:(6:(2:[]))) ++ (4:(2:[])) (1) ⇒ 5:( (6:(2:[])) ++ (4:(2:[])) ) (2) ⇒ 5:(6:( (2:[]) ++ (4:(2:[])) )) (3) ⇒ 5:(6:(2:( [] ++ (4:(2:[])) ))) (4) ⇒ 5:(6:(2:( 4:(2:[]) ))) (5) RECURSIVE LIST PROCESSING CONTINUED 3-C Stepwise execution of ++: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys) Let us look at [5, 6, 2] ++ [4, 2]: (5:(6:(2:[]))) ++ (4:(2:[])) (1) ⇒ 5:( (6:(2:[])) ++ (4:(2:[])) ) (2) ⇒ 5:(6:( (2:[]) ++ (4:(2:[])) )) (3) ⇒ 5:(6:(2:( [] ++ (4:(2:[])) ))) (4) ⇒ 5:(6:(2:( 4:(2:[]) ))) (5) RECURSIVE LIST PROCESSING CONTINUED 3-D POLYMORPHISM ➜ We used head, tail, null, concat, and ++ only on lists containing integers ➜ But they are not restricted to integer lists head ["a string", "another string"] ⇒ "a string" null [2.3, 4.1, 9.52] ⇒ False [False, True, True] ++ [False] ⇒ [False, True, True, False] Thus, we call them polymorphic functions. POLYMORPHISM 4 Types of polymorphic functions: head :: [a] -> a tail :: [a] -> [a] null :: [a] -> Bool concat :: [[a]] -> [a] (++) :: [a] -> [a] -> [a] fst :: (a, b) -> a snd :: (a, b) -> b ➜ We call the a in [a] a type variable ➜ A type variable is a placeholder for a concrete type (just as an argument variable is placeholder for a concrete argument value) ✧ POLYMORPHISM 5 Types of polymorphic functions: head :: [a] -> a tail :: [a] -> [a] null :: [a] -> Bool concat :: [[a]] -> [a] (++) :: [a] -> [a] -> [a] fst :: (a, b) -> a snd :: (a, b) -> b ➜ We call the a in [a] a type variable ➜ A type variable is a placeholder for a concrete type (just as an argument variable is placeholder for a concrete argument value) ✧ POLYMORPHISM 5-A (++) :: [a] -> [a] -> [a] -- general type ➜ When using a polymorphic function, we can instantiate the type variables to any concrete type: (++) :: [Int] -> [Int] -> [Int] -- These are (++) :: [Float] -> [Float] -> [Float] -- instances of (++) :: [Bool] -> [Bool] -> [Bool] -- (++) ➜ Consistency: All occurrences of a type variable have to be replaced by the same concrete type (++) :: [Int] -> [Bool] -> [Int] -- WRONG! ➜ Important: Type variables must start with a lowercase letter Good names: a, ty, elem, elem1, elementType Incorrect names: A, Elem, 1elem ➜ Conversely, names of concrete types always start with an uppercase letter POLYMORPHISM 6 (++) :: [a] -> [a] -> [a] -- general type ➜ When using a polymorphic function, we can instantiate the type variables to any concrete type: (++) :: [Int] -> [Int] -> [Int] -- These are (++) :: [Float] -> [Float] -> [Float] -- instances of (++) :: [Bool] -> [Bool] -> [Bool] -- (++) ➜ Consistency: All occurrences of a type variable have to be replaced by the same concrete type (++) :: [Int] -> [Bool] -> [Int] -- WRONG! ➜ Important: Type variables must start with a lowercase letter Good names: a, ty, elem, elem1, elementType Incorrect names: A, Elem, 1elem ➜ Conversely, names of concrete types always start with an uppercase letter POLYMORPHISM 6-A (++) :: [a] -> [a] -> [a] -- general type ➜ When using a polymorphic function, we can instantiate the type variables to any concrete type: (++) :: [Int] -> [Int] -> [Int] -- These are (++) :: [Float] -> [Float] -> [Float] -- instances of (++) :: [Bool] -> [Bool] -> [Bool] -- (++) ➜ Consistency: All occurrences of a type variable have to be replaced by the same concrete type (++) :: [Int] -> [Bool] -> [Int] -- WRONG! ➜ Important: Type variables must start with a lowercase letter Good names: a, ty, elem, elem1, elementType Incorrect names: A, Elem, 1elem ➜ Conversely, names of concrete types always start with an uppercase letter POLYMORPHISM 6-B (++) :: [a] -> [a] -> [a] -- general type ➜ When using a polymorphic function, we can instantiate the type variables to any concrete type: (++) :: [Int] -> [Int] -> [Int] -- These are (++) :: [Float] -> [Float] -> [Float] -- instances of (++) :: [Bool] -> [Bool] -> [Bool] -- (++) ➜ Consistency: All occurrences of a type variable have to be replaced by the same concrete type (++) :: [Int] -> [Bool] -> [Int] -- WRONG! ➜ Important: Type variables must start with a lowercase letter Good names: a, ty, elem, elem1, elementType Incorrect names: A, Elem, 1elem ➜ Conversely, names of concrete types always start with an uppercase letter POLYMORPHISM 6-C (++) :: [a] -> [a] -> [a] -- general type ➜ When using a polymorphic function, we can instantiate the type variables to any concrete type: (++) :: [Int] -> [Int] -> [Int] -- These are (++) :: [Float] -> [Float] -> [Float] -- instances of (++) :: [Bool] -> [Bool] -> [Bool] -- (++) ➜ Consistency: All occurrences of a type variable have to be replaced by the same concrete type (++) :: [Int] -> [Bool] -> [Int] -- WRONG! ➜ Important: Type variables must start with a lowercase letter Good names: a, ty, elem, elem1, elementType Incorrect names: A, Elem, 1elem ➜ Conversely, names of concrete types always start with an uppercase letter POLYMORPHISM 6-D (++) :: [a] -> [a] -> [a] -- general type ➜ When using a polymorphic function, we can instantiate the type variables to any concrete type: (++) :: [Int] -> [Int] -> [Int] -- These are (++) :: [Float] -> [Float] -> [Float] -- instances of (++) :: [Bool] -> [Bool] -> [Bool] -- (++) ➜ Consistency: All occurrences of a type variable have to be replaced by the same concrete type (++) :: [Int] -> [Bool] -> [Int] -- WRONG! ➜ Important: Type variables must start with a lowercase letter Good names: a, ty, elem, elem1, elementType Incorrect names: A, Elem, 1elem ➜ Conversely, names of concrete types always start with an uppercase letter POLYMORPHISM 6-E (++) :: [a] -> [a] -> [a] --
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages30 Page
-
File Size-