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/07-higher-order-functions.hs

121 lines
3.8 KiB

-- Partial application is awesome, since haskell functions are curried:
compareWithHundred :: (Num a, Ord a) => a -> Ordering
compareWithHundred x = compare 100 x
-- this is equivalent to:
compareWithHundred :: (Num a, Ord a) => a -> Ordering
compareWithHundred = compare 100
-- Infix functions can also be partially-applied:
divideByTen :: (Floating a) => a -> a
divideByTen = (/10)
-- (/10) 200 == 200 / 10
isUpperAlphanum :: Char -> Bool
isUpperAlphanum = (`elem` ['A'..'Z'])
-- Functions can take functions as params:
applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)
-- Example implementation of zipWith:
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith _ [] _ = []
zipWith _ _ [] = []
zipWith f (x:xs) (y:ys) = (f x y):(zipWith f xs ys)
flip :: (a -> b -> c) -> b -> a -> c
flip f x y = f y x
-- map built-in:
map (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = (f x):(map f xs)
-- filter built-in:
filter (a -> Bool) -> [a] -> [a]
filter _ [] = []
filter f (x:xs)
| f x = x:(filter xs)
| otherwise = filter xs
-- Largest # under 100000 divisible by 3829:
-- (this stops after finding the first matching value, because of lazy evaluation)
largestDivisible :: (Integral a) => a
largestDivisible = head (filter p [100000,99999,..])
where p x = x `mod` 3829 == 0
takeWhile :: (a -> Bool) -> [a] -> [a]
takeWhile _ [] = []
takeWhile p (x:xs)
| p x = x:(takeWhile p xs)
| otherwise = []
-- Collatz sequence builder
chain :: (Integral a) => a -> [a]
chain 1 = [1]
chain n
| even n = n:(chain (n `div` 2))
| odd n = n:(chain (n*3 + 1))
numLongChains :: Int
numLongChains = length (filter isLong (map chain [1..100]))
where isLong xs = length xs > 15
-- Instead, with lambdas!
numLongChains :: Int
numLongChains = length (filter (\xs -> length xs > 15) (map chain [1..100]))
-- You can pattern-match in lambdas, but only on one case. If a value doesn't match the pattern,
-- you get a runtime error. So, make sure your pattern is exhaustive.
-- Folds - foldl - left fold
sum :: (Num a) => [a] -> a
sum xs = foldl (\acc x -> acc + x) 0 xs
-- Or, more succinctly with currying:
sum xs = foldl (+) 0 xs
-- Generally, because of currying, a function `foo a = bar b a` can be written as `foo = bar b`
-- Another fold example:
elem :: (Eq a) => a -> [a] -> Bool
elem y ys = foldl (\acc x -> if x == y then True else acc) False ys
-- Right folds `foldr` work similarly, but from the right side, and the acc x are reversed. e.g.
map :: (a -> b) -> [a] -> [b]
map f xs = foldr (\x acc -> (f x):acc) [] xs
-- We could have done this with `foldl` and `++`, but cons is much cheaper than `++`, so we generally
-- use `foldr` when building lists from lists.
-- `foldl1` and `foldr1` work the same as `foldl` and `foldr`, but they use the starting element in
-- the list as the initial accumulator (the left-most or right-most, respectively)
-- `scanl` and `scanr` are like their fold* counterparts, but return an array of all the states of the accumulator:
scanl1Test = scanl1 (\acc x -> if x > acc then x else acc) [3,4,5,3] -- == [3,4,5,5]
-- These have a `scanl1` and `scanr1` equivalent. The final result of `scanl*` is the last element, and the head
-- element for `scanr1`
-- Function application
-- Take:
($) :: (a -> b) -> a -> b
f $ x = f x
-- Right-associative function application!
sum (map sqrt (1 + 2 + 3)) == sum $ map $ sqrt $ 1 + 2 + 3
-- This also means we can use function application... as a function:
mapTest1 = map ($ 3) [(4+), (^2)] -- == [4 + 3, 3 ^ 2]
-- Also, function composition:
(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)
-- This means more concise code! TFAE
mapTest2 = map (\x -> negate $ abs x) [1..3]
mapTest3 = map (negate . abs) [1..3]
-- If we want to compose functions with multiple parameters, we need to partially apply them first.