160 likes | 343 Views
This document delves into the concepts of guards and pattern matching in functional programming, particularly within Haskell. It illustrates the implementation of functions using guards and contrasts this with pattern matching through practical examples. The material covers sequential application of equations, case constructions, and the relevance of parentheses in function definitions. Moreover, it addresses recursive definitions for operations like summation, list manipulation, and text processing, showcasing the flexibility and power of recursion in handling various data types.
E N D
Programming Paradigms CPSC 449 Week 4 Prepared by : Mona Hosseinkhani, ArashAfshar Winter 2014 Department of Computer Science, University of Calgary
Pattern matching vs. guards in functions • An example function using guards mystery :: Integer -> Integer -> Integer mystery x y | x==0 = y | otherwise = x • Same function using pattern matching(by two equations) mystery' :: Integer -> Integer -> Integer mystery' 0 y = y mystery' x _ = x • Equations are applied sequentially
Patterns and parentheses Always parenthesized patterns and constructors • example: f x:xs = (f x):xs and not as f (x:xs) • function applications bind more tightly than any other operations
The case construction • So far: pattern match over the arguments of functions • Case: pattern match over other values • example: Return the first digit in a string. firstDigit :: String -> Char firstDigitst = case (digits st) of [] -> '\0' (x:_) -> x where digits :: String -> Stringdigits st = [ ch | ch<-st , isDigitch] • case e of p1 -> e1 p2 -> e2 … pk -> ek
Testing re-implemented functions using QuickCheck • Suppose we re-implement a function like sum which is implemented in the Prelude • Requirements: • Hide the “sum definition in Prelude” when importing Prelude import Prelude hiding (…,sum,…) • Test our re-implemented sum against the original sum by: import qualified Prelude
Testing re-implemented functions using QuickCheck • Putting it together: module Chapter7 where import Prelude hiding (…,sum,…) import qualified Prelude import Test.QuickCheck sum = … our definition … prop_sum :: [Integer] -> Bool prop_sumxs = sum xs == Prelude.sumxs • This also can be used when we have two different implementations of a particular function.
Finding primitive recursive definitions • Given the value of fun xs, how could we define fun (x:xs) from it? • example: • Suppose you have the function “:” a->[a]->[a] (add a single element to the front of a list) • [2,3,4] ++ [9,8] = [2,3,4,9,8] • 2 : ([3,4] ++ [9,8]) • How we can define “++” [a]->[a]->[a] from “:”?(Join two list together) (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x:(xs++ys)
Finding primitive recursive definitions • More examples: • Testingwhethersomething is a member of a list elem' :: Integer -> [Integer] -> Bool elem' x [] = False elem' x (y:ys) = (x==y) || (elem' x ys) • To double every element of an integer list doubleAll :: [Integer] -> [Integer] doubleAllxs = [ 2*x | x<-xs] doubleAll' [] = [] doubleAll' (x:xs) = 2*x : doubleAll' xs
Finding primitive recursive definitions • To select the even elements from an integer list selectEven :: [Integer] -> [Integer] selectEvenxs = [ x | x<-xs , isEven x ] selectEven' [] = [] selectEven' (x:xs) | isEven x = x : selectEven' xs | otherwise = selectEven' xs
General recursions over lists • A general recursive function . . . • may have more than one base case • may have different arguments for different recursive calls • arguments to recursive calls may not be strictly smaller than arguments to the function itself • may recurs on multiple arguments
General recursions over lists • More examples: • Zipping together two lists. zip :: [a] -> [b] -> [(a,b)] zip (x:xs) (y:ys) = (x,y) : zip xsys zip (x:xs) [] = [] zip [] zs = [] • Recurring on both arguments • Taking a given number of elements from a list take :: Int -> [a] -> [a] take 0 _ = [] take _ [] = [] take n (x:xs) | n>0 = x : take (n-1) xs take _ _ = error "PreludeList.take: negative argument” • Recurring on an integer and a list
Example: Text Processing • How to justify a paragraph • Bottom-up vs. Top-down approach • [Char] Words Lines • An input file in haskell can be treated as a string of characters. • The `whitespace' characters. whitespace = ['\n','\t',’ ‘] • Get a word from the front of a string. getWord :: String -> String getWord [] = [] getWord (x:xs) | elem x whitespace = [] | otherwise = x : getWordxs
Example: Text Processing • In a similar way, the first word of a string can be dropped. dropWord :: String -> String dropWord [] = [] dropWord (x:xs) | elem x whitespace = (x:xs) | otherwise = dropWordxs • To remove the whitespace character(s) from the front of a string. dropSpace :: String -> String dropSpace [] = [] dropSpace (x:xs) | elem x whitespace = dropSpacexs | otherwise = (x:xs)
Example: Text Processing • Splitting a string into words. type Word = String splitWords :: String -> [Word] splitWordsst = split (dropSpacest) split :: String -> [String] split [] = [] split st = (getWordst) : split (dropSpace (dropWordst)) • Getting a line from a list of words. type Line = [Word] getLine :: Int -> [Word] -> Line getLinelen [] = [] getLinelen (w:ws) | length w <= len = w : restOfLine | otherwise = [] where newlen = len - (length w + 1) restOfLine = getLinenewlenws
Example: Text Processing • Dropping the first line from a list of words. • dropLine :: Int -> [Word] -> Line • Splitting into lines splitLines :: [Word] -> [Line] splitLines [] = [] splitLinesws = getLinelineLenws: splitLines (dropLinelineLenws) • To fill a text string into lines, we write fill :: String -> [Line] fill = splitLines . splitWords
Question hossem<at>ucalgary.ca