{-  Exploring Languages with Interprters and Functional Programming
    Chapter 13: List Programming
    Copyright (C) 2018, H. Conrad Cunningham

1234567890123456789012345678901234567890123456789012345678901234567890

2018-07-11: Revised for 2018 textbook Chapter 13

-}

module ListProg
  ( len, sum', product', length', remdups, drop', take') 
where

len :: String -> Int
len s  =  if s == [] then 0 else 1 + len (tail s)

-- Backward recursive sum
sum' :: [Int] -> Int
sum' []     = 0           -- nil list
sum' (x:xs) = x + sum' xs -- non-nil list

-- Backward recursive product
product' :: [Int] -> Int
product' []     = 1 
product' (0:_)  = 0
product' (x:xs) = x * product' xs 
  
-- Similar to standard prelude function length
length' :: [a] -> Int
length' []     = 0              -- nil list
length' (_:xs) = 1 + length' xs -- non-nil list

-- 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

-- Drop from head of list
-- Uses data sharing
drop' :: Int -> [a] -> [a]
drop' n xs | n <= 0 = xs
drop' _ []          = []
drop' n (_:xs)      = drop' (n-1) xs

-- Take from head of list
-- No data sharing
take' :: Int -> [a] -> [a]
take' n _ | n <= 0 = []
take' _ []         = []
take' n (x:xs)     = x : take' (n-1) xs

