12.07.2015 Views

CSCE 314 Programming Languages - TAMU Computer Science ...

CSCE 314 Programming Languages - TAMU Computer Science ...

CSCE 314 Programming Languages - TAMU Computer Science ...

SHOW MORE
SHOW LESS
  • No tags were found...

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

<strong>CSCE</strong> <strong>314</strong><strong>Programming</strong> <strong>Languages</strong>Haskell: More on Functions and ListComprehensionsDr. Hyunyoung LeeBased on slides by Graham Hutton and notes by Jaakko Järvi1


Outline Defining Functions List Comprehensions Recursion2


Conditional ExpressionsAs in most programming languages, functions canbe defined using conditional expressions:if cond then e1 else e2• e1 and e2 must be of the same type• else branch is always presentabs :: Int -> Intabs n = if n >= 0 then n else –nmax :: Int -> Int -> Intmax x y = if x [a] -> [a]take n xs = if n


Guarded EquationsAs an alternative to conditionals, functions canalso be defined using guarded equations.abs n | n >= 0 = n| otherwise = -nPrelude:otherwise = TrueGuarded equations can be used to make definitionsinvolving multiple conditions easier to read:signum n | n < 0 = -1| n == 0 = 0| otherwise = 1compare with …signum n = if n < 0 then -1 elseif n == 0 then 0 else 14


Guarded Equations (Cont.)Guards and patterns can be freely mixed, thefirst equation whose pattern matches and guardis satisfied is chosen.take :: Int -> [a] -> [a]take n xs | n


Pattern Matching• Many functions are defined using patternmatching on their arguments.not :: Bool -> Boolnot False = Truenot True = Falsenot maps Falseto True, andTrue to False.• Pattern can be a constant value, or include oneor more variables.6


Functions can often be defined in many different waysusing pattern matching. For example(&&):: Bool → Bool → BoolTrue && True = TrueTrue && False = FalseFalse && True = FalseFalse && False = Falsecan be defined more compactly byTrue && True = True_ && _ = FalseHowever, the following definition is more efficient, becauseit avoids evaluating the second argument if the firstargument is False:False && _ = FalseTrue && b = b• The underscore symbol _ is a wildcard pattern that matches anyargument value.7


Patterns are matched in order. For example,the following definition always returns False:_ && _ = FalseTrue && True = True Patterns may not repeat variables. Forexample, the following definition gives an error:b && b = b_ && _ = False8


List PatternsInternally, every non-empty list is constructedby repeated use of an operator (:) called consthat adds an element to the start of a list.[1,2,3,4] Means 1:(2:(3:(4:[]))).Functions on lists can be defined using x:xspatterns.head :: [a] → ahead (x:_) = xtail :: [a] → [a]tail (_:xs) = xshead and tail map any nonemptylist to its first andremaining elements.is this definitioncomplete?9


Note: x:xs patterns only match non-empty lists:> head []Error x:xs patterns must be parenthesised, becauseapplication has priority over (:). For example,the following definition gives an error:head x:_ = x Patterns can contain arbitrarily deep structure:f (_: (_, x):_) = xg [[_]] = True10


Totality of Functions (Total) function maps every element in the function’sdomain to an element in its codomain. Partial function maps zero of more elements in thefunction’s domain to an element in its codomain, andcan leave some elements undefined. Haskell functions can be partial. For example:head (x:_) = x> head []*** Exception: Prelude.head: empty list> "10elements" !! 10*** Exception: Prelude.(!!): index too large11


Lambda ExpressionsFunctions can be constructed without naming thefunctions by using lambda expressions.λx → x+xThis nameless function takes a numberx and returns the result x+x. The symbol λ is the Greek letter lambda, and is typedat the keyboard as a backslash \. In mathematics, nameless functions are usuallydenoted using the symbol, as in x x+x. In Haskell, the use of the λ symbol for namelessfunctions comes from the lambda calculus, the theoryof functions on which Haskell is based.12


Why Are Lambda's Useful?1. Lambda expressions can be used to give aformal meaning to functions defined usingcurrying. For example:meansadd x y = x + ysquare x = x * xadd = \x -> (\y -> x + y)square = \x -> x * x13


2. Lambda expressions can be used to avoid namingfunctions that are only referenced once. For example:odds n = map f [0..n-1]wheref x = x * 2 + 1can be simplified toodds n = map (\x -> x * 2 + 1) [0..n-1]3. Lambda expressions can be bound to a name (functionargument)incrementer = \x -> x + 1add (incrementer 5) 614


Case ExpressionsPattern matching need not be tied to function definitions;they also work with case expressions. For example:(1)take m ys = case (m, ys) of(n, _) | n [](_, []) -> [](n, x:xs) -> x : take (m-1) xs(2)length [] = 0length (_:xs) = 1 + length xsusing a case expression and a lambda:length = \ls -> case ls of[] -> 0(_:xs) -> 1 + length xs15


(1)Let and WhereThe let and where clauses are used to create a localscope within a function. For example:reserved s = -- using letlet keywords = words “if then else for while”relops = words “== != < > =“elemInAny w [] = FalseelemInAny w (l:ls) = w `elem` l || elemInAny w lsin elemInAny s [keywords, relops]reserved s = -- using whereelemInAny s [keywords, relops]where keywords = words “if then else for while”. . .elemInAny w (l:ls) = w `elem` l || elemInAny w ls(2)unzip :: [(a, b)] -> ([a], [b])unzip [] = ([], [])unzip ((a, b):rest) =let (as, bs) = unzip restin (a:as, b:bs)16


Let vs. WhereThe let … in … is an expression, whereas where blocksare declarations bound to the context. For example:f x -- using where block| cond1 x = a| cond2 x = g a| otherwise = f (h x a)where a = w xf x -- using let-in expression= let a = w xin case () of_ | cond1 x -> a| cond2 x -> g a| otherwise -> f (h x a)17


SectionsAn operator written between its two arguments can beconverted into a curried function written before its twoarguments by using parentheses. For example:> 1 + 23> (+) 1 23This convention also allows one of the arguments of theoperator to be included in the parentheses. For example:> (1+) 2 > (+2) 13 3In general, if ⊕ is an operator then functions of the form(⊕), (x⊕) and (⊕y) are called sections.18


Why Are Sections Useful?Useful functions can sometimes be constructed ina simple way using sections. For example:(1+)(1/)(*2)(/2)- successor function (\y->1+y)- reciprocation function- doubling function- halving functionSometimes it is convenient or necessary to pass anoperator as a parameter or when stating its type19


Exercises(1)Consider a function safetail that behaves inthe same way as tail, except that safetailmaps the empty list to the empty list, whereastail gives an error in this case. Define safetailusing:(a) "a conditional expression;(b) "guarded equations;(c) "pattern matching.Hint: the library function null :: [a] → Bool canbe used to test if a list is empty.20


(2)(3)Give three possible definitions for the logicalor operator (||) using pattern matching.Redefine the following version of (&&) usingconditionals rather than patterns:True && True = True_ && _ = False(4)Do the same for the following version:True && b = bFalse && _ = False21


Outline Defining Functions List Comprehensions Recursion22


List Comprehensions A convenient syntax for defining lists Set comprehension - In mathematics, thecomprehension notation can be used to constructnew sets from old sets. E.g.,{(x 2 ,y 2 )|x {1,2,...,10}, y {1,2,...,10}, x 2 +y 2 ≤101} Same in Haskell: new lists from old lists[(x^2, y^2) | x


List Comprehensions: Generators The expression x take 3 [x | x [(x,y) | x


For example:> [(x,y) | y


Dependant GeneratorsLater generators can depend on the variablesthat are introduced by earlier generators.[(x,y) | x


GuardsList comprehensions can use guards to restrictthe values produced by earlier generators.[x | x [Int]factors n = [x | x factors 15[1,3,5,15]27


A positive integer is prime if its only factors are 1 anditself. Hence, using factors we can define a function thatdecides if a number is prime:prime :: Int -> Boolprime n = factors n == [1,n]> prime 15False> prime 7TrueUsing a guard we can now define a function that returnsthe list of all primes up to a given limit:primes :: Int -> [Int]primes n = [x | x primes 40[2,3,5,7,11,13,17,19,23,29,31,37]28


The Zip FunctionA useful library function is zip, which maps two lists to alist of pairs of their corresponding elements.zip :: [a] -> [b] -> [(a,b)]> zip [a,b,c] [1,2,3,4][(a,1),(b,2),(c,3)]Using zip we can define a function that returns the list ofall positions of a value in a list:positions :: Eq a => a -> [a] -> [Int]positions x xs = [i | (x,i) positions 0 [1,0,0,1,0,1,1,0][1,2,4,7]29


Using zip we can define a function that returns the listof all pairs of adjacent elements from a list:pairs :: [a] -> [(a,a)]pairs xs = zip xs (tail xs)> pairs [1,2,3,4][(1,2),(2,3),(3,4)]Using pairs we can define a function that decides if theelements in a list are sorted:sorted :: Ord a => [a] -> Boolsorted xs = and [x ≤ y | (x,y) sorted [1,2,3,4]True> sorted [1,3,2,4]False30


Exercises(1)A triple (x,y,z) of positive integers is calledpythagorean if x 2 + y 2 = z 2 . Using a listcomprehension, define a functionpyths :: Int -> [(Int,Int,Int)]that maps an integer n to all such tripleswith components in [1..n]. For example:> pyths 5[(3,4,5),(4,3,5)]pyths n = [(x,y,z)| x


(2)A positive integer is perfect if it equals thesum of all of its factors, excluding the numberitself. Using a list comprehension, define afunctionperfects :: Int → [Int]that returns the list of all perfect numbersup to a given limit. For example:> perfects 500[6,28,496]32


(3)The scalar product of two lists of integers xsand ys of length n is given by the sum of theproducts of the corresponding integers:n-1∑i = 0(xs i * ys i )Using a list comprehension, define a functionthat returns the scalar product of two lists.scalarProduct :: [Int] -> [Int] -> IntscalarProduct xs ys =sum[x*y|(x,y)


Outline Defining Functions List Comprehensions Recursion34


A Function without RecursionMany functions can naturally be defined in terms of otherfunctions.factorial :: Int → Intfactorial n = product [1..n]factorial maps any integer nto the product of theintegers between 1 and nExpressions are evaluated by a stepwise process of applyingfunctions to their arguments. For example:factorial 4====product [1..4]product [1,2,3,4]1*2*3*42435


Recursive FunctionsFunctions can also be defined in terms ofthemselves. Such functions are called recursive.factorial 0 = 1factorial n = n * factorial (n-1)factorial 3 = 3 * factorial 2= 3 * (2 * factorial 1)====3 * (2 * (1 * factorial 0))3 * (2 * (1 * 1))3 * (2 * 1)3 * 2factorial maps 0 to 1,and any otherpositive integer tothe product of itselfand the factorial ofits predecessor.= 636


Note: The base case factorial 0 = 1 is appropriatebecause 1 is the identity for multiplication: 1*x= x = x*1. The recursive definition diverges on integers factorial (-1)Error: Control stack overflow37


Why is Recursion Useful? Some functions, such as factorial, are simplerto define in terms of other functions. As we shall see, however, many functions cannaturally be defined in terms of themselves. Properties of functions defined using recursioncan be proved using the simple but powerfulmathematical technique of induction.38


Recursion on ListsLists have naturally a recursive structure. Consequently,recursion is used to define functions on lists.product :: [Int] → Intproduct [] = 1product (n:ns) = n * product nsproduct [2,3,4] = 2 * product [3,4]product maps theempty list to 1, and anynon-empty list to itshead multiplied by theproduct of its tail.====2 * (3 * product [4])2 * (3 * (4 * product []))2 * (3 * (4 * 1))2439


Using the same pattern of recursion as in product we candefine the length function on lists.length :: [a] → Intlength [] = 0length (_:xs) = 1 + length xslength maps the empty listto 0, and any non-empty listto the successor of thelength of its tail.=====length [1,2,3]1 + length [2,3]1 + (1 + length [3])1 + (1 + (1 + length []))1 + (1 + (1 + 0))340


Using a similar pattern of recursion we can define thereverse function on lists.reverse :: [a] → [a]reverse [] = []reverse (x:xs) = reverse xs ++ [x]=====reverse [1,2,3]reverse [2,3] ++ [1](reverse [3] ++ [2]) ++ [1]reverse maps theempty list to theempty list, andany non-emptylist to thereverse of its tailappended to itshead.((reverse [] ++ [3]) ++ [2]) ++ [1](([] ++ [3]) ++ [2]) ++ [1][3,2,1]41


Multiple ArgumentsFunctions with more than one argument can also bedefined using recursion. For example: Zipping theelements oftwo lists:zip:: [a] → [b] → [(a,b)]zip [] _ = []zip _ [] = []zip (x:xs) (y:ys) = (x,y) : zip xs ys Remove the firstn elements froma list:drop:: Int → [a] → [a]drop n xs | n


Laziness RevisitedLaziness interacts with recursion in interestingways. For example, what does the followingfunction do?numberList xs = zip [0..] xs> numberList “abcd”[(0,’a’),(1,’b’),(2,’c’),(3,’d’)]43


Laziness with RecursionRecursion combined with lazy evaluation can be tricky;stack overflows may result in the following example:expensiveLen [] = 0expensiveLen (_:xs) = 1 + expensiveLen xsstillExpensiveLen ls = len 0 lswhere len z [] = zcheapLen ls = len 0 lslen z (_:xs) = len (z+1) xswhere len z [] = zlen z (_:xs) = let z’ = z+1in z’ `seq` len z’ xs> expensiveLen [1..10000000] -- takes quite long> stillExpensiveLen [1..10000000] -- also takes long> cheapLen [1..10000000] -- less memory and time44


QuicksortThe quicksort algorithm for sorting a list ofintegers can be specified by the following tworules: The empty list is already sorted; Non-empty lists can be sorted by sorting thetail values ≤ the head, sorting the tail values> the head, and then appending the resultinglists on either side of the head value.45


Using recursion, this specification can betranslated directly into an implementation:Note:qsort :: [Int] -> [Int]qsort [] = []qsort (x:xs) =qsort smaller ++ [x] ++ qsort largerwheresmaller = [a | a


For example (abbreviating qsort as q):q [3,2,4,1,5]q [2,1] ++ [3] ++ q [4,5]q [1] ++ [2] ++ q [] q [] ++ [4] ++ q [5][1] [] [] [5]47


Exercises(1) Without looking at the standard prelude,define the following library functions usingrecursion: Decide if all logical values in a list aretrue:and :: [Bool] → Booland [] = Trueand (b:bs) = b && and bs Concatenate a list of lists:concat :: [[a]] → [a]concat [] = []concat (xs:xss) = xs ++ concat xss48


Produce a list with n identical elements:replicate :: Int → a → [a]replicate 0 _ = []replicate n x = x : replicate (n-1) x Select the nth element of a list:(!!) :: [a] → Int → a(!!) (x:_) 0 = x(!!) (_:xs) n = (!!) xs (n-1) Decide if a value is an element of a list:elem :: Eq a ⇒ a → [a] → Boolelem x [] = Falseelem x (y:ys) | x==y = True| otherwise = elem x ys49


(2) Define a recursive functionmerge :: [Int] → [Int] → [Int]merge [] ys = ysmerge xs [] = xsmerge (x:xs) (y:ys) = if x merge [2,5,6] [1,3,4][1,2,3,4,5,6]50


(3) Define a recursive functionmsort :: [Int] → [Int]that implements merge sort, which can bespecified by the following two rules:1. Lists of length ≤ 1 are already sorted;2. Other lists can be sorted by sorting the two halvesand merging the resulting lists.halves xs = splitAt (length xs `div` 2) xsmsort [] = []msort [x] = [x]msort xs = merge (msort ys) (msort zs)where (ys,zs) = halves xs51

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!