You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
haskell/05-syntax-in-functions.hs

125 lines
3.5 KiB

-- 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