You can do this without any numbers. Let's work our way up to it. We'll use the accumulator approach, but instead of keeping a number in the accumulator, we'll keep a function that repeats its argument a certain number of times.
test0 :: [a] -> [a]
test0 xs = go rep1 xs
where
rep1 :: a -> [a]
rep1 a = [a]
go :: (a -> [a]) -> [a] -> [a]
go _rep [] = []
go rep (a : as) = rep a ++ go (oneMore rep) as
oneMore :: (a -> [a]) -> (a -> [a])
oneMore rep a = a : rep a
We start off calling go
with rep1
, a really simple function that turns its argument into a singleton list. Then on each recursive call, we modify the repeater function by making it repeat its argument one more time.
test0
works just fine, but it uses the ++
function, and you're not supposed to be using any predefined functions. Using ++
here also means that you have to build up the small lists and put them together, an inefficiency we can remove pretty easily.
Note that every time go
calls rep
, it immediately appends something else to the result. This suggests the solution: instead of having rep
take an element and produce a list, let's have it take an element and a list, and produce a list consisting of the element repeated a certain number of times followed by the given list! So we'll have
rep1 "a" ["b", "c"] = ["a", "b", "c"]
rep2 "a" ["b", "c"] = ["a", "a", "b", "c"]
where rep1
and rep2
are the first two rep
functions. Just a few adjustments are needed.
test :: [a] -> [a]
test = go rep1
where
rep1 :: a -> [a] -> [a]
rep1 a as = a : as -- Note: rep1 can be written as just (:)
go :: (a -> [a] -> [a]) -> [a] -> [a]
go _ [] = []
go rep (a : as) = rep a (go (oneMore rep) as)
oneMore :: (a -> [a] -> [a])
-> a -> [a] -> [a]
oneMore f a as = a : f a as
This really isn't an efficient way to solve the problem, but it's a rather minimalist approach.
zip
too. – Swen