-- Functions as outputs of other functions
--
add :: Int -> Int -> Int
add x y = x + y
-- equivalent to
--
-- add :: Int -> (Int -> Int)
-- add = \x -> (\y -> x + y)
--
-- i.e. add is a function that takes x as input and returns a function
-- as output that takes y as input and returns x + y
-- Functions can also take as input a function value
--
twice :: (a -> a) -> a -> a
twice f x = f (f x)
--
twice (*2) 3
twice reverse [1,2,3]
Functions that have other functions as inputs or return a function are called Higher Order Functions
map1 :: (a -> b) -> [a] -> [b]
map1 f xs = [f x | x <- xs]
map1 (+1) [1,2,3,4]
map1 even [1,2,3,4]
map1 reverse ["abc","def","ghi"]
-- map within a map!
--
map1 (map1 (+1)) [[1,2,3],[4,5]]
-- = [map1 (+1) [1,2,3], map1 (+1) [4,5]]
-- = [[2,3,4],[5,6]]
Definition of map
map1 :: (a -> b) -> [a] -> [b]
map1 f [] = []
map1 f (x:xs) = f x : map1 f xs
filter1 :: (a -> Bool) -> [a] -> [a]
filter1 p xs = [x | x <- xs, p x]
filter1 even [1..10]
filter1 (>5) [1..10]
filter1 (/= ' ') "abc def ghi"
Definition of filter
filter1 :: (a -> Bool) -> [a] -> [a]
filter1 p [] = []
filter1 p (x:xs)
| p x = x : filter1 p xs
| otherwise = filter1 p xs
-- Example: sum of squares of even integers in a list
sumsqreven :: [Int] -> Int
sumsqreven ns = sum (map (^2) (filter1 even ns))
sumsqreven [1,2,3,4,5,6]
all even [2,4,6,8]
any odd [2,4,6,8]
takeWhile even [2,4,6,7,8]
dropWhile odd [1,3,5,6,7]
Many functions on lists follow the pattern
f [] = v
f (x:xs) = x # f xs
i.e. f applied to [] is v; f applied to non-empty list equals operator # applied to head of list and result of processing tail of list recursively
sum [] = 0
sum (x:xs) = x + sum xs
product [] = 1
product (x:xs) = x * product xs
or [] = False
or (x:xs) = x || or xs
and [] = True
and (x:xs) = x && and xs
The HOF foldr encapsulates the above pattern
sum :: Num a => [a] -> a
sum = foldr (+) 0
-- sum xs = foldr (+) 0 xs
product :: Num a => [a] -> a
product = foldr (*) 1
or :: [Bool] -> Bool
or = foldr (||) False
and :: [Bool] -> Bool
and = foldr (&&) True
foldr itself could be implemented as follows
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f v [] = v
foldr f v (x:xs) = f x (foldr f v xs)
In practice it is best to think of foldr as an iterative process that processes a list, say
1 : (2 : (3 : []))
by replacing "cons" by f and [] by v. For example
sum = foldr (+) 0
replaces "cons" by + and [] by 0 to get
1 + (2 + (3 + 0))
foldr can be used for much more than the simple pattern seen above. For example
length1 :: [a] -> Int
length1 = foldr (\ _ n -> 1 + n) 0
length1 [1,2,3]
snoc :: a -> [a] -> [a]
snoc x xs = xs ++ [x]
reverse1 :: [a] -> [a]
reverse1 = foldr snoc []
reverse [1,2,3]
It is interesting to note that foldr uses an operator that "associates" to the right; e.g.
foldr (+) 0 [1,2,3] = 1 + (2 + (3 + 0))
or in general
foldr (#) v [x0,x1,...,xn] = x0 # (x1 # (... (xn # v)...))
-- left associative!
sum2 :: Num a => [a] -> a
sum2 = sum' 0
where sum' v [] = v
sum' v (x:xs) = sum' (v+x) xs
sum2 [1,2,3]
sum2 [1,2,3]
= sum' 0 [1,2,3]
= sum' (0+1) [2,3]
= sum' ((0+1)+2) [3]
= sum' (((0+1)+2)+3) []
= sum' ((0+1)+2)+3
= 6
Generalizing the pattern, we get
f v [] = v
f v (x:xs) = f (v # x) xs
This pattern is captured by the foldl operator in Haskell. e.g.
sum :: Num a -> [a] -> a
sum = foldl (+) 0
Similarly
product :: Num a -> [a] -> a
product = foldl (*) 1
or :: [Bool] -> Bool
or = foldl (||) False
and :: [Bool] -> Bool
and = foldl (&&) True
length :: [a] -> Int
length = foldl (\n _ -> n+1) 0
reverse :: [a] -> [a]
reverse = foldl (\xs x -> x:xs) []
Using the above foldl definitions,
length [1,2,3] = ((0 + 1) + 1) + 1 = 3
reverse [1,2,3] = 3 : (2 : (1 : [])) = [3,2,1]
foldl itself is defined using recursion as
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v [] = v
foldl f v (x:xs) = foldl f (f v x) xs
In practice, however, it is best to view foldl non-recursively as
foldl (#) v [x1,x1,...,xn] = (... ((v # x0) # x1) ... ) # xn
(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g = \x -> f (g x)
Composition can be used to simplify nested function applications. e.g.
odd n = not (even n)
twice f x = f (f x)
sumsqreven ns = sum (map (^2) (filter even ns))
can be rewritten as
odd = not . even
twice f = f . f
sumsqreven = sum . map (^2) . filter even
Composition is associative
f . (g . h) = (f . g) . h
Composition has an identity:
id :: a -> a
id = \x -> x
Now composition of a list of functions can be defined as
compose :: [a -> a] -> (a -> a)
compose = foldr (.) id
Consider the problem of transmitting a string of characters encoded as a sequence of binary digits. To make some of the functions we develop easier, we will represent binary numbers in reverse order of bits, i.e., the binary number 1101 which corresponds to the decimal number 13, will be written as 1011.
The library function iterate
works as follows:
iterate f x = [x, f x, f (f x), f (f (f x)), ...]
take 10 (iterate (*2) 1)
import Data.Char
type Bit = Int
bin2int :: [Bit] -> Int
bin2int bits = sum [w*b | (w,b) <- zip weights bits]
where weights = iterate (*2) 1
-- bits are in reverse order for this example
bin2int [1,0,1,1]
Another way to implement bin2Int
[a,b,c,d]
1*a + 2*b + 4*c + 8*d
= a + 2*b + 4*c + 8*d
= a + 2*(b + 2*c + 4*d)
= a + 2*(b + 2*(c + 2*d))
= a + 2*(b + 2*(c + 2*(d + 2*0)))
This can be implemented using foldr as follows
bin2int :: [Bit] -> Int
bin2int = foldr (\x y -> x + 2*y) 0
bin2int :: [Bit] -> Int
bin2int = foldr (\x y -> x + 2*y) 0
bin2int [1,0,1,1]
int2bin :: Int -> [Bit]
int2bin 0 = []
int2bin n = n `mod` 2 : int2bin (n `div` 2)
int2bin 13
8 bits are sufficient to encode ASCII characters.
make8 :: [Bit] -> [Bit]
make8 bits = take 8 (bits ++ repeat 0)
make8 [1,0,1,1]
Here, repeat is a function within the prelude that produces an infinite list of the same value.
repeat :: a -> [a]
encode :: String -> [Bit]
encode = concat . map (make8 . int2bin . ord)
encode "abc"
-- to decode message back to string, first we chop into 8 bits
chop8 :: [Bit] -> [[Bit]]
chop8 [] = []
chop8 bits = take 8 bits : chop8 (drop 8 bits)
chop8 [1,0,0,0,0,1,1,0,0,1,0,0,0,1,1,0,1,1,0,0,0,1,1,0]
decode :: [Bit] -> String
decode = map (chr . bin2int) . chop8
decode [1,0,0,0,0,1,1,0,0,1,0,0,0,1,1,0,1,1,0,0,0,1,1,0]
-- Finally transmit message
transmit :: String -> String
transmit = decode . channel . encode
channel :: [Bit] -> [Bit]
channel = id
transmit "higher-order functions are easy"
Counting votes and determining winner
votes :: [String]
votes = ["Red","Blue","Green","Blue","Blue","Red"]
count :: Eq a => a -> [a] -> Int
count x = length . filter (== x)
count "Red" votes
count "Blue" votes
count "Green" votes
rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x:xs) = x : filter (/= x) (rmdups xs)
rmdups votes
import Data.List
result :: Ord a => [a] -> [(Int,a)]
result vs = sort [(count v vs,v) | v <- rmdups vs]
result votes
winner :: Ord a => [a] -> a
winner = snd . last . result
winner votes
ballots :: [[String]]
ballots = [["Red","Green"],
["Blue"],
["Green","Red","Blue"],
["Blue","Green","Red"],
["Green"]]
Each ballot lists 1st preference, 2nd preference, etc.
Method to determine winner: repeatedly eliminate smallest number of 1st choice votes. e.g.
Since "Red" has smallest number of 1st choices, eliminate it from all ballots:
ballots = [["Green"],
["Blue"],
["Green","Blue"],
["Blue","Green"],
["Green"]]
Now, "Blue" has smallest number of 1st choices; eliminate it from all ballots:
ballots = [["Green"],
[],
["Green"],
["Green"],
["Green"]]
Removing [] and observing that we have only one remaining candidate we can now declare the winner
-- rmempty removes [] from ballot list
rmempty :: Eq a => [[a]] -> [[a]]
rmempty = filter (/= [])
-- elim eliminates x from all ballots
elim :: Eq a => a -> [[a]] -> [[a]]
elim x = map (filter (/= x))
ballots2 = [["Green"],
[],
["Green"],
["Green"],
["Green"]]
rmempty ballots2
elim "Red" ballots
elim "Blue" (elim "Red" ballots)
-- ranks ballots based on 1st preference (from low to high)
-- temp1 = map head ballots
-- temp2 = result temp1
-- return map snd temp2
rank :: Ord a => [[a]] -> [a]
rank = map snd . result . map head
rank ballots
winner' :: Ord a => [[a]] -> a
winner' bs = case rank (rmempty bs) of
[c] -> c -- if only one candidate left declar winner
(c:cs) -> winner' (elim c bs) -- if more than one then eliminate c and recurse
winner' ballots