
module Chap05
  ( sumlist, length', remdups, rev, reverse', f, g, 
    fib, fib', fibU, take', drop', zip' ) 
where

{- 5.2.1 -}
sumlist :: [Int] -> Int
sumlist []     = 0              -- nil list
sumlist (x:xs) = x + sumlist xs -- non-nil list

{- 5.2.2 -}
-- similar to standard prelude function length
length' :: [a] -> Int
length' []     = 0              -- nil list
length' (_:xs) = 1 + length' xs -- non-nil list

{- 5.2.3 -}
-- remdups :: [Int] -> [Int]
-- Generalize: restrict polymorphism to class Eq
remdups :: Eq a => [a] -> [a]
remdups (x:y:xs)
  | x == y = remdups (y:xs)
  | x /= y = x : remdups (y:xs)
remdups xs = xs

{- 5.4.2 and 5.4.4 -}
rev :: [a] -> [a]
rev []     = []            -- nil argument
rev (x:xs) = rev xs ++ [x] -- non-nil argument

reverse' :: [a] -> [a]
reverse' xs = rev xs []
              where rev []     ys = ys
                    rev (x:xs) ys = rev xs (x:ys)

{- 5.4.5 -}
f :: [Int] -> [Int]
f [] = []
f xs = let square a = a * a
           one      = 1

           (y:ys)   = xs
       in (square y + one) : f ys

g :: Int -> Int
g n | (n `mod` 3) == x = x
    | (n `mod` 3) == y = y
    | (n `mod` 3) == z = z
                       where x = 0
                             y = 1
                             z = 2

{- 5.4.6 -}
fib :: Int -> Int
fib 0         = 0
fib 1         = 1
fib n | n > 1 = fib (n-2) + fib (n-1)

fib' :: Int -> Int
fib' n = fib'' n 0 1
         where fib'' 0 p q         = p
               fib'' n p q | n > 0 = fib'' (n-1) q (p+q)

fibU :: Integer -> Integer
fibU n = fib'' n 0 1
         where fib'' 0 p q         = p
               fib'' n p q | n > 0 = fib'' (n-1) q (p+q)

infixl 9 !!!  -- list selection really !!

(!!!) :: [a] -> Int -> a
xs     !!! n | n < 0 = error "!!! negative index"
[]     !!! _         = error "!!! index too large"
(x:_)  !!! 0         = x
(_:xs) !!! n         = xs !!! (n-1)

take' :: Int -> [a] -> [a]
take' n _ | n <= 0 = []
take' _ []         = []
take' n (x:xs)     = x : take' (n-1) xs

drop' :: Int -> [a] -> [a]
drop' n xs | n <= 0 = xs
drop' _ []          = []
drop' n (_:xs)      = drop' (n-1) xs

zip' :: [a] -> [b] -> [(a,b)]
zip' (x:xs) (y:ys) = (x,y) : zip' xs ys     -- zip.1
zip' _      _      = []                     -- zip.1
