Pattern Matching
Pattern Matching
ghci> :l sayMe.hs
[1 of 1] Compiling Main ( sayMe.hs,
interpreted )
Ok, one module loaded.
ghci> sayMe 1
PATTERN MATCHING
• Pattern Matching is process of matching specific type of expressions.
• Can be considered as a variant of dynamic polymorphism where at runtime, different
methods can be executed depending on their argument list.
• Example:
fact :: Int -> Int
fact 0 = 1 • The compiler will start searching for a
function called "fact" with an argument.
fact n = n * fact ( n - 1 )
• If the argument is not equal to 0, then the
number will keep on calling the same
main = do function with 1 less than that of the actual
argument.
putStrLn "The factorial of 5 is:"
print (fact 5) • When the pattern of the argument exactly
matches with 0, it will call our pattern
which is "fact 0 = 1".
ghci> main
The factorial of 5 is:
120
A FACTORIAL EXAMPLE
• Order is
--factorial using pattern matching factorialp.hs
important !
factorialp :: (Integral a) => a -> a
• Specify the most
factorialp 0 = 1 specific ones first
and then the
factorialp n = n * factorialp (n - 1) more general
ones later
ghci> :l factorialp.hs
[1 of 1] Compiling Main ( factorialp.hs, interpreted )
Ok, one module loaded.
ghci> factorialp 0
1
ghci> factorialp 5
120
FACTORIAL EXECUTION FOR N=3
factorial 3
factorial 0 = 1
3* factorial 2 pattern is matched
3*(2 * factorial 1) here
3 * (2 * (1 * factorial 0))
3 * (2 * (1 * 1))
3 * (2 * 1)
Had we written the second pattern
3*2 (factorial n) before the first one
6 (factorial 0), it would catch all
numbers, including 0 and our
calculation would never terminate.
PATTERN MATCHING COULD FAIL!!
--charName.hs
charName :: Char -> String
charName 'a' = "Albert"
When making patterns, we should always
charName 'b' = "Broseph" include a catch-all pattern so that our
program doesn't crash if we get some
charName 'c' = "Cecil“ unexpected input.
ghci> :l charName1.hs
[1 of 1] Compiling Main ( charName1.hs, interpreted )
Ok, one module loaded.
ghci> charName1 'b'
"Broseph"
ghci> charName1 'h'
"String not defined"
PATTERN MATCHING ON TUPLES
• To make a function that takes two vectors in a 2D space (that are in the
form of pairs) and adds them together
ghci> :l addVectors.hs
[1 of 1] Compiling Main ( addVectors.hs, interpreted )
Ok, one module loaded.
ghci> addVectors (1,2) (3,4)
(4,6)
ADDVECTORS USING PATTERN MATCHING
--addVectors1.hs - with pattern matching
addVectors1 :: (Num a) => (a, a) -> (a, a) -> (a, a)
addVectors1 (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
ghci> :l addVectors1.hs
[1 of 1] Compiling Main ( addVectors1.hs, interpreted )
Ok, one module loaded.
ghci> addVectors1 (1,2) (3,4) Note that this is already a catch-all
pattern. The type of addVectors
(4,6) (in both cases) is addVectors :: (Num
a) => (a, a) -> (a, a) - > (a, a), so we
are guaranteed to get two pairs as
parameters.
DEFINING OUR OWN FUNCTIONS FOR TRIPLES
first :: (a, b, c) -> a
first (x, _, _) = x
second :: (a, b, c) -> b _ means don’t care
second (_, y, _) = y
third :: (a, b, c) -> c
third (_, _, z) = z
PATTERN MATCH IN LIST COMPREHENSIONS
ghci> let xs = [(1,3), (4,3), (2,4), (5,3), (5,6), (3,1)]
ghci> [a+b | (a,b) <- xs]
[4,7,6,8,11,4]
• Should a pattern match fail, it will just move on to the next element.
LISTS AND PATTERN MATCHING
• Lists themselves can also be used in pattern matching
• You can match with the empty list [ ] or any pattern that involves : and the
empty list
• A pattern like x:xs will bind the head of the list to x and the rest of it to xs,
even if there's only one element so xs ends up being an empty list
• The x:xs pattern is used a lot, especially with recursive functions
• Patterns that have : in them only match against lists of length 1 or more
• If you want to bind, say, the first three elements to variables and the rest
of the list to another variable, you can use something like x:y:z:zs
• It will only match against lists that have three elements or more
OUR OWN IMPLEMENTATION OF THE HEAD FUNCTION
--myhead.hs
myhead :: [a] -> a
myhead [] = error "Can't call head on an empty list, dummy!"
myhead (x:_) = x
ghci> :l myhead.hs
[1 of 1] Compiling Main ( myhead.hs, interpreted )
Ok, one module loaded.
ghci> myhead [4,5,6]
4
ghci> myhead "Hello"
'H'
A SAFE LIST TELLER FUNCTION !
--tell.hs
tell :: (Show a) => [a] -> String
tell [] = "The list is empty"
tell (x:[]) = "The list has one element: " ++ show x
tell (x:y:[]) = "The list has two elements: " ++ show x ++ " and " ++ show y
tell (x:y:_) = "This list is long. The first two elements are: " ++ show x ++ " and " ++ show y
ghci> :l tell.hs
[1 of 1] Compiling Main ( tell.hs, interpreted )
Ok, one module loaded. • This function is safe because it takes care
ghci> tell []
of the empty list, a singleton list, a list with
two elements and a list with more than two
"The list is empty"
elements.
ghci> tell [1]
"The list has one element: 1" • Note that (x: [ ] ) and (x:y: [ ] ) could be
ghci> tell [1,2]
rewriten as [x] and [x,y](because its syntatic
sugar, we don't need the parentheses). We
"The list has two elements: 1 and 2"
can't rewrite (x:y:_) with square brackets
ghci> tell [1,2,3] because it matches any list of length 2 or
"This list is long. The first two elements are: 1 and 2" more.
LENGTH USING PATTERN MATCHING
--mylength.hs
mylength :: (Num b) => [a] -> b
mylength [] = 0 -- length of empty list
mylength (_:xs) = 1 + mylength xs -- recursive call to mylength
ghci> :l mysum.hs
[1 of 1] Compiling Main ( mysum.hs, interpreted )
Ok, one module loaded.
ghci> mysum [1,2,3,4,5]
15
GUARDS
• Patterns are a way of making sure a value conforms to some form and
deconstructing it
• Guards are a way of testing whether some property of a value (or several
of them) are true or false.
• Guards are similar to if statements
• Guards are lot more readable to match several conditions
A BMI EXAMPLE USING GUARDS Reminiscent of a big if
else tree in imperative
--bmiTell.hs languages but more
readable
bmiTell :: (RealFloat a) => a -> String
bmiTell bmi Many times, the last
| bmi <= 18.5 = "You're underweight, you emo, you!" guard is otherwise.
otherwise is defined
| bmi <= 25.0 = "You're supposedly normal. Pffft, I bet you'resimply
ugly!"as otherwise =
True and catches
| bmi <= 30.0 = "You're fat! Lose some weight, fatty!" everything.
| otherwise = "You're a whale, congratulations!“
• Guards are indicated by pipes that follow a
function's name and its parameters.
ghci> :l bmiTell.hs • Usually, they're indented a bit to the right and
lined up.
[1 of 1] Compiling Main ( bmiTell.hs, interpreted )
• A guard is basically a boolean expression.
Ok, one module loaded. • If it evaluates to True, then the corresponding
function body is used.
ghci> bmiTell 24.3 • If it evaluates to False, checking drops through
to the next guard and so on.
"You're supposedly normal. Pffft, I bet you're ugly!"
BMI WITH MORE PARAMETERS There's no = right after
the function name and its
--bmiTell1.hs parameters, before the
first guard.
bmiTell1 :: (RealFloat a) => a -> a -> String
bmiTell1 weight height
| weight / height ^ 2 <= 18.5 = "You're underweight, you emo, you!"
| weight / height ^ 2 <= 25.0 = "You're supposedly normal. Pffft, I bet you're
ugly!"
| weight / height ^ 2 <= 30.0 = "You're fat! Lose some weight, fatty!"
| otherwise = "You're a whale, congratulations!“
ghci> maxI 2 3
3
OUR OWN COMPARE FUNCTION
myCompare :: (Ord a) => a -> a -> Ordering
a `myCompare` b
| a > b = GT
| a == b = EQ
| otherwise = LT
ghci> 3 `myCompare` 2
GT
ghci> 3 `myCompare` 2
Back ticks ( ` `) (check tilde ~ key on your
GT keyboard) are used to call and define
functions as infix to make it easy for
readability ! ! !
WHERE CLAUSE
• Where is a keyword or inbuilt function
roots :: (Float, Float, Float) -> (Float, Float) that can be used at runtime to generate a
roots (a,b,c) = (x1, x2) where desired output.
ghci> :l whereroots.hs
[1 of 1] Compiling Main ( whereroots.hs, interpreted )
Ok, one module loaded.
ghci> main
The roots of our Polynomial equation are:
(7.1622777,0.8377223)
THE BMI EXAMPLE MODIFIED
bmiTell :: (RealFloat a) => a -> a -> String -- Earlier Version
bmiTell weight height
| weight / height ^ 2 <= 18.5 = "You're underweight, you emo, you!"
| weight / height ^ 2 <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
| weight / height ^ 2 <= 30.0 = "You're fat! Lose some weight, fatty!"
| otherwise = "You're a whale, congratulations!"
The where keyword is put after the
guards , indented and several names Same expression
or functions are defined repeated thrice ! ! !
• The in part of the let binding is omitted in list comprehensions because the visibility of the names
is already predefined there.
• However, we could use a let in binding in a predicate and the names defined would only be
visible to that predicate.
VARIANTS OF LET
ghci> let zoot x y z = x * y + z
ghci> zoot 3 9 2
29
ghci> let boot x y z = x * y + z in boot 3 4 2
14 • The in part can also be omitted when
defining functions and constants directly
ghci> boot in GHCi.
<interactive>:1:0: Not in scope: `boot' • If we do that, then the names will be
visible throughout the entire interactive
session.
CASE EXPRESSIONS
• Case expressions are much similar to if else expressions and let
bindings.
• Not only can expressions be evaluated based on the possible cases of
the value of a variable, but also can do pattern matching.
• The syntax for case expressions is pretty simple:
Expression is
case expression of pattern -> result matched against
the patterns.
pattern -> result
• The first pattern that matches the expression is used.
pattern -> result
• If it falls through the whole case expression and no suitable
... pattern is found, a runtime error occurs.
• These two pieces of code do the same thing and are interchangeable:
head' :: [a] -> a
head' :: [a] -> a
head' xs = case xs of [] -> error "No head for empty
head' [] = error "No head for empty lists!"
lists!"
head' (x:_) = x
(x:_) -> x
EXAMPLE USAGE OF CASE EXPRESSIONS
• While Pattern matching on function parameters can only be done when
defining functions, case expressions can be used pretty much anywhere.
• They are useful for pattern matching against something in the middle of
an expression.