-- HW 3 Suggested Solutions

-- #1

map' :: (a -> b) -> [a] -> [b]
map' f xs = [f x | x <- xs]

filter' :: (a -> Bool) -> [a] -> [a]
filter' p xs = [x | x <- xs, p x]

-- #2a

h' f g xs = map (f . g) xs

-- #2b

r' p q xs = filter pnq xs
   where pnq x = p x && q x

-- #3

map2 :: (a -> b) -> [[a]] -> [[b]]
map2 f = map (map f)

upall = map2 toUpper

-- #4a

map'' :: (a -> b) -> [a] -> [b]
map'' f = foldr g []
    where g x ys = f x : ys

-- #4b

filter'' :: (a -> Bool) -> [a] -> [a]
filter'' p = foldr g []
    where g x ys | p x = x : ys
                 | otherwise = ys 


-- #4c 

(+++) :: [a] -> [a] -> [a]
xs +++ ys = foldr (:) ys xs


-- #4d

average :: [Int] -> Float
average xs = (fromInt sum) / (fromInt count)
    where (sum,count) = foldr g (0,0) xs
          g x (s,c) = (s+x,c+1)


-- #5

-- The result type of map is always a list, whereas the result type of foldr can be
-- anything.


-- #6

-- The direct version is probably simplest:
makeChange :: Int -> [Int] -> [Int]
makeChange amount [] = []
makeChange amount (coin:coins) = 
          (amount `div` coin):(makeChange (amount `mod` coin) coins)

-- with a foldl, the accumulating value is a pair
makeChange' :: Int -> [Int] -> [Int]
makeChange' amount coins = counts
    where (_,counts) = foldl f (amount,[]) coins
	  f (amount,counts) coin = (amount `mod` coin,counts ++ [amount `div` coin])


-- or, if you prefer foldr's, the accumulating value is a function! :
makeChange'' :: Int -> [Int] -> [Int]
makeChange'' amount coins = foldr f (const []) coins amount
  where f coin g = \ amount -> (amount `div` coin):(g (amount `mod` coin))

