-- | Working with List -- Examples to illustrate pattern matching, recursion and testing for lists -- Functional Programming course 2017. -- Thomas Hallgren {- This started out as a skeleton, the definitions were be filled in during the lecture. -} import Prelude hiding ((++),null,length,sum,reverse,take,drop,splitAt,zip,unzip) import qualified Prelude import Test.QuickCheck -------------------------------------------------------------------------------- null :: [a] -> Bool null [] = True null _ = False length :: [a] -> Int length [] = 0 length (x:xs) = length xs + 1 sum :: Num a => [a] -> a sum [] = 0 sum (n:ns) = n + sum ns (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs++ys) cons :: a -> [a] -> [a] cons x xs = x:xs -- O(1) snoc :: [a] -> a -> [a] snoc xs x = xs ++ [x] -- O(n) reverse :: [a] -> [a] reverse [] = [] reverse (x:xs) = reverse xs ++ [x] -- O(n^2) -- complexity? how to make it more efficient? -- | Take the first n elements of a list take :: Int -> [a] -> [a] take 0 xs = [] take _ [] = [] take n (x:xs) = x:take (n-1) xs prop_take n xs = length (take n' xs) == min n' (length xs) where n' = abs n -- | Discard the first n elements of a list drop :: Int -> [a] -> [a] drop 0 xs = xs drop _ [] = [] drop n (_:xs) = drop (n-1) xs prop_take_drop :: Int -> [Int] -> Bool prop_take_drop n xs = take n xs ++ drop n xs == xs nonprop_take_drop :: Int -> [Int] -> Bool nonprop_take_drop n xs = drop n xs ++ take n xs == xs -- | "Quicksort" qsort :: Ord a => [a] -> [a] qsort [] = [] qsort (x:xs) = qsort smaller ++ [x] ++ qsort bigger where smaller = [y | y<-xs, y<=x] bigger = [y | y<-xs, y>x] -- | insert a new element at the right position in a sorted list insert :: Ord a => a -> [a] -> [a] insert x [] = [x] insert x (x':xs) | x [Int] -> Property prop_insert x xs = isSorted xs ==> isSorted (insert x xs) -- quickCheck says "Gave up!" because most of the random lists it generates -- are not sorted. We will return to this issue in a future lecture. -- | Insertion sort (sort a list by using insert) isort :: Ord a => [a] -> [a] isort [] = [] isort (x:xs) = insert x (isort xs) prop_qsort :: [Int] -> Bool prop_qsort xs = qsort xs == isort xs -------------------------------------------------------------------------------- -- * Filled in after the lecture -- | splitAt n xs = (take n xs,drop n xs) splitAt :: Int -> [a] -> ([a],[a]) splitAt 0 xs = ([],xs) splitAt n [] = ([],[]) splitAt n (x:xs) = (x:ys,zs) where (ys,zs) = splitAt (n-1) xs -- When calling a function that returns a pair, it is often useful to use -- pattern matching to access the two parts of the pair -- | Combine a pair of list into a list of pairs zip :: [a] -> [b] -> [(a,b)] zip (x:xs) (y:ys) = (x,y):zip xs ys zip _ _ = [] -- We have two lists that both can be empty or non-empty, so there are -- 4 combinations to consider. By putting the case when both lists are -- non-empty first, all the other cases (one or both lists are empty) can -- be handled with one equation (since zip stops and returns the empty list -- when it reaches the end of the shorter of the two lists). prop_length_zip xs ys = length (zip xs ys) == min (length xs) (length ys) -- | Split a list of pairs into a pair of lists unzip :: [(a,b)] -> ([a],[b]) unzip [] = ([],[]) unzip ((x,y):xys) = (x:xs,y:ys) where (xs,ys) = unzip xys prop_zip_unzip :: [(Int,Int)] -> Bool prop_zip_unzip xys = zip xs ys == xys where (xs,ys) = unzip xys prop_unzip_zip :: [Int] -> [Int] -> Bool prop_unzip_zip xs ys = unzip (zip xs ys) == (take n xs,take n ys) where n = min (length xs) (length ys)