125 lines
3.5 KiB
Haskell
Executable File
125 lines
3.5 KiB
Haskell
Executable File
-- Pattern matching - different function bodies for different destructured type matches
|
|
|
|
lucky :: (Integral a) => a -> String
|
|
lucky 7 = "Lucky number seven!"
|
|
lucky _ = "Sorry, out of luck..."
|
|
|
|
-- This also makes recursion pretty nice
|
|
|
|
factorial :: (Integral a) => a -> a
|
|
factorial 0 = 1
|
|
factorial x => x * (factorial (x-1))
|
|
|
|
-- When making functions, always include a catch-all pattern match
|
|
|
|
-- We can use pattern patching to do destructuring. e.g.
|
|
|
|
addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)
|
|
addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
|
|
|
|
-- By the way, can also pattern match in list comprehensions
|
|
|
|
listComprehensionTest1 = [ a+b | (a,b) <- [(1,3),(2,4)] ]
|
|
|
|
-- Here, we note that if a pattern match fails, it will be skipped and move on to the next element!
|
|
|
|
-- Implement head ourselves:
|
|
head :: [a] -> a
|
|
head [] = error "Empty list has no head"
|
|
head (x:_) = x
|
|
|
|
-- We can use cons to destructure multiple elements:
|
|
firstSum :: (Num a) -> [a] -> a
|
|
firstSum [] = 0
|
|
firstSum (x:[]) = x
|
|
firstSum (x:y:[]) = x + y
|
|
firstSum (x:y:z:_) = x + y + z
|
|
|
|
-- What about length?
|
|
length :: (Num b) => [a] -> b
|
|
length [] = 0
|
|
length (x:xs) = 1 + (length xs)
|
|
|
|
-- Now, sum:
|
|
sum :: (Num a) => [a] -> a
|
|
sum [] = 0
|
|
sum (x:xs) = 1 + (sum xs)
|
|
|
|
-- We can also name patterns:
|
|
emptyUnless2OrMore :: [a] -> [a]
|
|
emptyUnless2OrMore [] = []
|
|
emptyUnless2OrMore (x:[]) = []
|
|
emptyUnless2OrMore list@(x:y:_) = list
|
|
|
|
-- Guards are a flow-through boolean alternation
|
|
bmiTell :: (Floating a) => a -> String
|
|
bmiTell bmi
|
|
| bmi <= 18.5 = "underweight"
|
|
| bmi <= 25.0 = "normal"
|
|
| bmi <= 30.0 = "overweight"
|
|
| otherwise = "obese"
|
|
|
|
-- Interesting: implementation of max
|
|
max :: (Ord a) => a -> a -> a
|
|
max a b
|
|
| a > b = a
|
|
| otherwise = b
|
|
|
|
-- Implementation of compare
|
|
myCompare :: (Ord a) -> a -> a -> Ordering
|
|
a `myCompare` b -- functions can also be defined using infix-syntax
|
|
| a < b = LT
|
|
| a == b = EQ
|
|
| otherwise = GT
|
|
|
|
-- We can use `where` to define local variables for a function block:
|
|
bmiTell :: (Floating a) => a -> a -> String
|
|
bmiTell weight height
|
|
| bmi <= skinny = "underweight"
|
|
| bmi <= normal = "normal"
|
|
| bmi <= fat = "overweight"
|
|
| otherwise = "obese"
|
|
where bmi = weight / height ^ 2
|
|
skinny = 18.5
|
|
normal = 25.0
|
|
fat = 30.0
|
|
|
|
-- the variables in `where` need to be indented at the same level so Haskell knows how to scope them correctly
|
|
-- we can also pattern-match in `where`:
|
|
|
|
initials :: String -> String -> String
|
|
initials first last = [f] ++ ". " ++ [l] ++ "."
|
|
where (f:_) = first
|
|
(l:_) = last
|
|
|
|
-- We can also define local functions in the where-body
|
|
calcBmis :: (Floating a) => [(a,a)] -> [a]
|
|
calcBmis xs = [bmi w h | (w,h) <- xs]
|
|
where bmi weight height = weight / height ^ 2
|
|
|
|
-- We can also use `let`, which is an expression, not a syntactic construct:
|
|
cylinder :: (Floating a) => a -> a -> a
|
|
cylinder r h = let sideArea = 2 * pi * r * h in
|
|
let topArea = pi * r ^ 2 in
|
|
sideArea + 2 * topArea
|
|
|
|
-- Because it's an expression, it can be used to shorten inline code:
|
|
squaresTest1 = [let square x = x * x in (square 5, square 3, square 2)]
|
|
|
|
-- let is good for quickly destructuring a type structure inline:
|
|
tupleTest1 = (let (a,b,c) = (1,2,3) in a+b+c) * 100 -- == 600
|
|
|
|
-- Case statements
|
|
-- Turns out, pattern matching function bodies is just syntactic sugar for case statements!
|
|
|
|
head :: [a] -> a
|
|
head [] = error "No head of empty list"
|
|
head (x:_) = x
|
|
|
|
-- Is equivalent to:
|
|
head :: [a] -> a
|
|
head xs = case xs of
|
|
[] -> error "No head of empty list"
|
|
(x:_) -> x
|
|
|