99-haskell-problems / 99_problems.hs

@ c0a7e37a7070086d18744b5dd75843bac914a658 | history


-- 1. Implement `last`
myLast :: [a] -> a
myLast [] = error "Empty list"
myLast [x] = x
myLast (x:xs) = myLast xs

myLast' :: [a] -> a
myLast' = head . reverse


-- 2. Write a function that returns the penultimate item in a list
myButLast :: [a] -> a
myButLast list
    | len < 2 = error "Too few elements!"
    | otherwise = list !! (len - 2)
    where len = length list


-- 3. Implement `!!`
elementAt :: [a] -> Int -> a
elementAt list index
    | index < 1 = error "Index must be a positive number!"
    | length list < index = error "List index out of range!"
    | otherwise = myLast $ take index list
    -- But this doesn't work for infinite lists!

elementAt' :: [a] -> Int -> a
elementAt' list index 
    | index < 1 = error "Index must be a positive number!"
    | null elementAtHead = error "List index out of range!"
    | otherwise = head elementAtHead
    where elementAtHead = drop (index - 1) list
    -- This works fine for infinite lists


-- 4. Implement `length`
myLength :: [a] -> Int
myLength [] = 0
myLength (x:xs) = 1 + myLength xs


-- 5. Implement `reverse`
myReverse :: [a] -> [a]
myReverse [] = []
myReverse [x] = [x]
myReverse list = (myLast list):(myReverse $ init list)


-- 6. Write a function to determine whether a list is a palindrome
isPalindrome :: Eq a => [a] -> Bool
isPalindrome list = myReverse list == list

isPalindrome' :: Eq a => [a] -> Bool
isPalindrome' [] = True
isPalindrome' [x] = True
isPalindrome' list = ((head list) == (last list))
                  && (isPalindrome' (init $ tail list))


-- 7. Flatten an arbitrarily-nested list
data NestedList a = Elem a | List [NestedList a]

flatten :: NestedList a -> [a]
flatten (Elem x) = [x]
flatten (List []) = [] 
flatten (List (x:xs)) = (flatten x) ++ (flatten $ List xs)


-- 8. Collapse adjacent identical list elements to a single element
compress :: Eq a => [a] -> [a]
compress [] = []
compress [x] = [x]
compress (x:xs) = if x == head xs
                  then compress xs 
                  else x:(compress xs)


-- 9. Separate runs of identical elements into sublists
pack :: Eq a => [a] -> [[a]]
pack [] = []
pack (x:xs) = (x : (takeWhile (== x) xs)) : pack (dropWhile (== x) xs)

pack' :: Eq a => [a] -> [[a]]
pack' [] = []
pack' (x:xs) = (x : fst run) : (pack $ snd run)
    where run = span (== x) xs


-- 10. Encode runs of identical elements into tuples: 
--     (<number of elements in run>, <element>)
encode :: Eq a => [a] -> [(Int, a)]
encode = map (\l -> (length l, head l)) . pack


-- 11. Encode runs of identical elements into custom data type
data OneOrMany a = Multiple Int a | Single a deriving Show

encodeModified :: Eq a => [a] -> [OneOrMany a]
encodeModified = map (\l -> if length l == 1
                            then Single $ head l
                            else Multiple (length l) (head l)) . pack


-- 12. Decode a run-length encoded list
decodeModified :: Eq a => [OneOrMany a] -> [a]
decodeModified [] = []
decodeModified [Single x] = [x]
decodeModified [Multiple n x] = take n $ repeat x
decodeModified (x:xs) = (decodeModified [x]) ++ (decodeModified xs)


-- 13. Directly run-length encode a list without creating sublists
encodeDirect :: Eq a => [a] -> [OneOrMany a]
encodeDirect [] = []
encodeDirect (x:xs) = 
    let run = span (== x) xs
        pkg = \l -> if length l == 1 
                    then Single $ head l 
                    else Multiple (length l) (head l)
    in pkg (x : fst run) : (encodeDirect $ snd run)


-- 14. Duplicate the elements of a list; e.g. "abc" -> "aabbcc"
dupli :: [a] -> [a]
dupli [] = []
dupli (x:xs) = x:x:(dupli xs)


-- 15. As above, but replicate the elements of a list a given number of times
repli :: [a] -> Int -> [a]
repli [] _ = []
repli l n = concat [take n $ repeat x | x <- l]


-- 16. Drop every n'th element from a list
dropEvery :: [a] -> Int -> [a]
dropEvery list n = map fst [x | x <- zip list [1..], snd x `mod` n /= 0]