Show how the list comprehension
[f x | x <- xs, p x]
can be re-expressed using the higher order functionsmap
andfilter
.
applyFilterAndFnToList :: (a -> Bool) -> (a -> b) -> [a] -> [b]
= map f (filter p xs) applyFilterAndFnToList p f xs
Without looking at the definitions from the standard prelude, define the following higher-order library functions on lists:
Decide if all the elements of a list satisfy a predicate:
all :: (a -> Bool) -> [a] -> Bool
Decide if any element of a list satisfies a predicate:
any :: (a -> Bool) -> [a] -> Bool
Select elements from a list while they satisfy a predicate
takeWhile :: (a -> Bool) -> [a] -> [a]
Remove elements from a list while they satisfy a predicate
dropWhile :: (a -> Bool) -> [a] -> [a]
Note: In
Prelude
the first two of these functions are generic functions rather than being specific to the type of lists
-- Part a
all :: (a -> Bool) -> [a] -> Bool
all p = and . map p
-- Part b
any :: (a -> Bool) -> [a] -> Bool
any p = or . map p
-- Part c
takeWhile :: (a -> Bool) -> [a] -> [a]
takeWhile _ [] = []
takeWhile p (x:xs)
| p x = x : takeWhile p xs
| otherwise = []
-- Part d
dropWhile :: (a -> Bool) -> [a] -> [a]
dropWhile _ [] = []
dropWhile p x'@(x:xs)
| p x = dropWhile p xs
| otherwise = x'
Redefine the functions
map f
andfilter p
usingfoldr
.
map' :: (a -> b) -> [a] -> [b]
= foldr (\x xs -> f x : xs) []
map' f
filter' :: (a -> Bool) -> [a] -> [a]
=
filter' p foldr (\x xs ->
if p x then
:xs
xelse
xs) []
Using
foldl
define a functiondec2Int :: [Int] -> Int
that converts a decimal number into an integer. For example:> dec2Int [2,3,4,5] 2345
dec2Int :: [Int] -> Int
= foldl (\xs x -> 10*xs + x) 0 dec2Int
Without looking at the definitions from the standard prelude, define the higher-order library function
curry
that converts a function on pairs into a curried function, and, conversely, the functionuncurry
that converts a curried function with two arguments into a function on pairs. (Hint: first write down the types of the two functions.)
curry :: ((a,b) -> c) -> a -> b -> c
curry f a b = f (a,b)
-- Alternatively:
-- curry f = \a b -> f (a,b)
uncurry :: (a -> b -> c) -> (a,b) -> c
uncurry f (a,b) = f a b
-- Alternatively:
-- uncurry f = \(a,b) -> f a b
A higher-order function
unfold
that encapsulates a simple pattern of recursion for producing a list can be defined as follows:| px = [] unfold p h t x | otherwise = h x : unfold p h t (t x)
That is, the function
unfold p h t
produces the empty list if the predicatep
is true of the argument value, and otherwise produces a non-empty list by applying the functionh
to this value to give the head, and the functiont
to generate another argument that is recursively processed in the same way to produce the tail of the list. For example, the functionint2Bin
can be rewritten more compactly usingunfold
as follows:= unfold (== 0) (`mod` 2) (`div` 2) int2Bin
Redefine the functions
chop8
,map f
anditerate f
usingunfold
.
unfold :: (a -> Bool) -> (a -> b) -> (a -> a) -> a -> [b]
unfold p h t x| p x = []
| otherwise = h x : unfold p h t (t x)
chop8 :: [Int] -> [[Int]]
= unfold null (take 8) (drop 8)
chop8
map'' :: (a -> b) -> [a] -> [b]
= unfold null (f . head) tail
map'' f
iterate' :: (a -> a) -> a -> [a]
= unfold (const False) id iterate'
Modify the binary string transmitter example to detect simple transmission errors using the concept of parity bits. That is, each eight-bit binary number produced during encoding is extended with a parity bit, set to one if the number contains an odd number of ones, and to zero otherwise. In turn, each resulting nine-bit binary number consumed during decoding is checked to ensure that its parity bit is correct, with the parity bit being discarded if this is the case, and a parity error being reported otherwise. (Hint: the library function
error :: String -> a
displays the given string as an error message and terminates the program; the polymorphic result type ensures that error can be used in any context.)
type Bit = Int
bin2Int :: [Bit] -> Int
= foldr (\x y -> x + 2*y) 0
bin2Int
int2Bin :: Int -> [Bit]
0 = []
int2Bin = mod n 2 : int2Bin (div n 2)
int2Bin n
make8 :: [Bit] -> [Bit]
= take 8 (bits ++ repeat 0)
make8 bits
encode :: String -> [Bit]
= concatMap (make8 . int2Bin . ord)
encode
encodeWithParityBit :: String -> [Bit]
= encoded ++ [parityBit]
encodeWithParityBit s where
= encode s
encoded = sum encoded `mod` 2
parityBit
-- Using previously defined chop8 from #6
decode :: [Bit] -> String
= map (chr . bin2Int) . chop8
decode
decodeWithParityBit :: [Bit] -> String
=
decodeWithParityBit list if last list == sum (init list) `mod` 2
then decode $ init list
else error "Parity bit doesn't match input actual string"
Test your new string transmitter program from the previous exercise using a faulty communication channel that forgets the first bit, which can be modeled using the tail function on lists of bits.
channelNoisy :: [Bit] -> [Bit]
= tail
channelNoisy
transmitNoisy :: String -> String
= decodeWithParityBit
transmitNoisy . channelNoisy
. encodeWithParityBit
Define a function
altMap :: (a -> b) -> (a -> b) -> [a] -> [b]
that alternately applies its two argument functions to successive elements in a list, in turn about order. For example:> altMap (+10) (+100) [0,1,2,3,4] 10,101,12,103,14] [
altMap :: (a -> b) -> (a -> b) -> [a] -> [b]
= []
altMap _ _ [] :xs) = f x : altMap g f xs altMap f g (x
Using
altMap
, define a functionluhn :: [Int] -> Bool
that implements the Luhn algorithm from the exercises in Chapter 4 for bank card numbers of any length. Test your new function using your own bank card.
luhnDouble :: Int -> Int
luhnDouble n| n <= 5 = 2 * n
| otherwise = 2 * n - 9
luhn :: [Int] -> Bool
= False
luhn [] = mod (sum $ altMap luhnDouble id ns) 10 == 0 luhn ns