I have this code:
lado :: [([Char],Int)] -> [[Char]]
lado xs = [a | (a,b) <- xs]
I need to output this:
> lado [("A
Will Ness came up with a nice expression in terms of rotations:
lado :: [(a,Int)] -> [a]
lado [] = []
lado ((a,1):b) = a : lado b
lado ((a,n):b) = a : lado (b ++ [(a,n-1))])
Unfortunately, this will be quite inefficient because using ++ to add an element to the end of a list takes linear time. This can be fixed by using a queue instead of a list. The queue is used in an ephemeral manner, so it can be a very simple one:
-- Invariant: The Int is at least 1.
data IStream a
= ISCons a !Int (IStream a)
| ISNil
-- Invariant: The Int is at least 1.
data IList a
= ICons a !Int !(IList a)
| INil
data IQueue a = IQueue !(IStream a) !(IList a)
-- Note: the list may be infinite
listToIStream :: [(a, Int)] -> IStream a
listToIStream = foldr go ISNil
where
go (a, n) r
| n <= 0 = r
| otherwise = ISCons a n r
listToIQueue :: [(a, Int)] -> IQueue a
listToIQueue xs = IQueue (listToIStream xs) INil
dequeue :: IQueue a -> Maybe (Dequeued a)
dequeue (IQueue (ISCons a i more) rear) = Just (Dequeued a i (IQueue more rear))
dequeue (IQueue ISNil INil) = Nothing
dequeue (IQueue ISNil (ICons a i more)) = Just $! (rotate ISNil a i more)
data Dequeued a = Dequeued a !Int !(IQueue a)
rotate :: IStream a -> a -> Int -> IList a -> Dequeued a
rotate str a0 i0 INil = Dequeued a0 i0 (IQueue str INil)
rotate str a0 i0 (ICons a i more) = rotate (ISCons a0 i0 str) a i more
enqueue :: IQueue a -> a -> Int -> IQueue a
enqueue (IQueue front rear) a i = IQueue front (ICons a i rear)
Now we can define
lado :: [(a, Int)] -> [a]
lado = lado' . listToIQueue
lado' :: IQueue a -> [a]
lado' q = case dequeue q of
Nothing -> []
Just (Dequeued a 1 b) -> a : lado' b
Just (Dequeued a n b) -> a : lado' (enqueue b a (n - 1))