I have this code:
lado :: [([Char],Int)] -> [[Char]]
lado xs = [a | (a,b) <- xs]
I need to output this:
> lado [("A
I really like Aplet123's approach, but I think it can be made a bit more efficient. What's wrong with it as is? nonzero
is used twice, which means the program will realize its result list rather than fusing it with the consumers of that list. So each time we go through the recursion, we'll allocate three new lists. Can we fix it? Let's start by assuming that the argument has no zeros in it.
-- Assumes all the Ints are positive
lado' :: [(a, Int)] -> [a]
lado' [] = []
lado' xns = map fst xns ++ rest
where
rest = lado' [(x, n - 1) | (x, n) <- xns, n /= 1]
This is already much better. It only allocates two lists each time. But map fst
will allocate a bunch of selector thunks to put in the list. We can fix that with another list comprehension:
-- Assumes all the Ints are positive
lado' :: [(a, Int)] -> [a]
lado' [] = []
lado' xns = start ++ rest
where
start = [x | (x, _) <- xns]
rest = lado' [(x, n - 1) | (x, n) <- xns, n /= 1]
Nice and clean! But what if the original list has zeros? We can filter it up front:
lado :: [(a, Int)] -> [a]
lado = lado' . filter (\(_, n) -> n > 0)
There's still one little inefficiency in the case where there are a good number of non-tiny numbers: the representation of the list of pairs. A small improvement:
data IPair a = IPair a !Int
A bigger improvement would change the implementation of lists:
data IPList a
= Cons a !Int (IPList a)
| Nil
The downside of IPList
is that you'd have to ditch list comprehensions.
I see you're not allowed to use recursion, which is ... quite a silly requirement. Can you see how to work around that with iterate
, takeWhile
, and concat
?