Basically the naive algorithm which uses appending
revList [] = []
revList (x:xs) = revList xs ++ [x]
is inefficient since appending is an O(n)
operation where n
is the length of the first (left) parameter of the ++
operator. So the revList
function above turns out to be O(n(n-1)/2) ~ O(n^2).
So for such append heavy tasks there are the Difference List data type.
A difference list is just a list expressed as a function. What i mean is, a list like [1,2,3]
when expressed in DList type would be \xs -> [1,2,3] ++ xs
or in short ([1,2,3] ++)
type DList a = [a] -> [a]
toDList :: [a] -> DList a
toDList xs = (xs ++ )
fromDList :: DList a -> [a]
fromDList f = f []
This is sort of cool because since DLists are functions we can append them by composition (.) operator and get a new DList. In other words toDList (xs ++ ys) == (toDList xs) . (toDList ys)
.
So how is this useful? By using nested function compositions we can reverse our list in a similar fashion to revList
function but it will cost us much less. Only O(n) since every function composition is O(1).
revList' :: [a] -> DList a
revList' [] = toDList []
revList' (x:xs) = revList' xs . toDList [x]
Now that we have the reversed [a]
in DList a
type all we need to apply fromDList
fastReverse :: [a] -> [a]
fastReverse = fromDList . revList'
The Difference List data type is not as simple as i have shown above. It can have Monoid, Functor and MonadT instances. For more on this useful data type check Data.DList