I'm just learning Haskell and I wanted to know if I'm going in the right direction with my solving of the Haskell 99 problems. The file of interest is here which I've also reproduced below.
module OneToTen where
{-- Problem 1: Find the last element of a list. --}
myLast :: [a] -> a
myLast [] = error "Empty list."
myLast [x] = x
myLast (x:xs) = myLast xs
{-- Problem 2: Find the last but one element of a list. --}
myButLast :: [a] -> a
myButLast [] = error "Empty list."
myButLast [x] = error "One element list."
myButLast [x,y] = x
myButLast (x:xs) = myButLast xs
{-- Problem 3: Find the Kth element of a list. The first element in the list
-- is number 1 --}
elementAt :: [a] -> Int -> a
elementAt [] _ = error "Empty list."
elementAt (x:_) 1 = x
elementAt (_:xs) n = elementAt xs (n-1)
{-- Problem 4: Find the number of elements of a list. --}
myLength :: [a] -> Int
myLength xs = myLengthAux xs 0
myLengthAux :: [a] -> Int -> Int
myLengthAux [] acc = acc
myLengthAux (_:xs) acc = myLengthAux xs (acc+1)
{-- Problem 5: Reverse a list. --}
myReverse :: [a] -> [a]
myReverse xs = myReverseAux xs []
myReverseAux :: [a] -> [a] -> [a]
myReverseAux [] acc = acc
myReverseAux (x:xs) acc = myReverseAux xs (x:acc)
{-- Problem 7: Flatten a nested list structure. --}
data NestedList a = Elem a | List [NestedList a]
flatten :: NestedList a -> [a]
flatten xs = flattenAux xs []
flattenAux :: NestedList a -> [a] -> [a]
flattenAux (List []) acc = acc
flattenAux (Elem x) acc = x:acc
flattenAux (List (x:xs)) acc = flattenAux (List xs) (acc ++ (flattenAux x []))
{-- Problem 8: Eliminate consecutive duplicated of list elements. --}
compress :: (Eq a) => [a] -> [a]
compress [] = []
compress (x:xs) = compressAux xs x [x]
compressAux :: (Eq a) => [a] -> a -> [a] -> [a]
compressAux [] y acc = acc
compressAux (x:xs) y acc | x == y = compressAux xs y acc
| x /= y = compressAux xs x (acc ++ [x])
{-- Problem 9: Pack consecutive duplicates of list elements into sublists. If a
-- list contains repeated elements they should be placed in separate sublists.
--}
pack :: (Eq a) => [a] -> [[a]]
pack [] = error "Empty list."
pack (x:xs) = packAux xs x [x] []
packAux :: (Eq a) => [a] -> a -> [a] -> [[a]] -> [[a]]
packAux [] _ xs acc = acc ++ [xs]
packAux (x:xs) y ys acc | x == y = packAux xs y (y:ys) acc
| x /= y = packAux xs x [x] (acc ++ [ys])
{-- Problem 10: Run-length encoding of a list. Use the result of problem P09
-- to implement the so-called run0length encoding data compression method. --}
encode :: (Eq a) => [a] -> [(Int,a)]
encode [] = error "Empty list."
encode xs = map (\ys -> (length ys, head ys)) (pack xs)
I'm most curious if implementing all of these auxiliary methods is standard practice? It's easier for me to think of a recursive problem using an accumulator, consequently, I write most of them this way. Is this correct? Should I be writing them differently?