Printing out each element of list of tuples several times

Viewed 146

Given a list of tuples like this [('a',2), ('b', 6), ('c', 1)] I'm trying to print out the char each time in a string.

I'm looking for an output like this "aabbbbbbc" in a string on one line.

I'm trying to do this using replicate, for example replicate (snd x) (fst x) but this didn't work.

4 Answers

If you have a list of items, you can use map :: (a -> b) -> [a] -> [b] to construct a list where the given function is applied to all elements of the given list. We thus here can make use of replicate, like:

map (\x -> replicate (snd x) (fst x))

to obtain a list of items, where for an item ('a', 2), we are thus given "aa". For the sample data, we thus obtain:

Prelude> map (\x -> replicate (snd x) (fst x)) [('a',2), ('b', 6), ('c', 1)]
["aa","bbbbbb","c"]

This is of course a list of list of elements. We can use concat :: Foldable f => f [a] -> [a] to concatenate the elements together, or use concatMap :: Foldable f => (a -> [b]) -> f a -> [b] to combine map and concat:

Prelude> concatMap (\x -> replicate (snd x) (fst x)) [('a',2), ('b', 6), ('c', 1)]
"aabbbbbbc"

We can use pattern matching here to avoid using fst and snd:

concatMap (\(x, y) -> replicate y x)

or we can make use of uncurry :: (a -> b -> c) -> (a, b) -> c and flip :: (a -> b -> c) -> b -> a -> c and write the function as:

concatMap (uncurry (flip replicate))

This could be done using list comprehensions (where xs is your list of tuples):

concat [replicate n x | (x, n) <- xs]

Let's start by just making the definition

foo [('a',2), ('b', 6), ('c', 1)] = "aabbbbbbc"

Is this a correct definition? Yes.

Is there some problem with it? Yes, it is not general enough. It will only accept a very very specific and narrow form of input, and error out on everything else:

> foo [('a',2), ('b', 6), ('c', 1)]
"aabbbbbbc"
it :: [Char]

> foo [('a',2), ('b', 6), ('c', 2)]
"*** Exception: <interactive>:13:5-51: Non-exhaustive patterns in function foo

Anything else wrong with that definition? Well, not wrong per se, but it's lacking a type signature. GHCi can infer the type for us:

> :t foo
foo :: (Num t, Eq t) => [(Char, t)] -> [Char]

but it seems too general for comfort. We can specialize is e.g. as

foo ::                [(Char, Int)] -> [Char]

So, with this type, what can be done about the definition? Starting from the missing case, we add

foo [('a',2), ('b', 6), ('c', 2)] = "aabbbbbbcc"
foo [('a',2), ('b', 6), ('c', 3)] = "aabbbbbbccc"

All correct cases. But what about making them more general?

For that, we try re-writing it and trying to see some patterns in it:

foo [('a',2), ('b', 6), ('c', 4)] = "aabbbbbbcccc"
                   = "aabbbbbb" ++ "cccc"
                   = "aabbbbbb" ++ replicate _ 'c'

What is the value to go into that _ slot?

When you've answered that, you'll be able to also complete this one:

foo [('a',2), ('b', 6), ('c', n)] = "aabbbbbbcccc"
                   = "aabbbbbb" ++ "cccc"
                   = "aabbbbbb" ++ replicate _ 'c'

and this one:

foo [('a',2), ('b', m), ('c', n)] = "aabbbbbbcccc"
                   = "aabbbbbb" ++ "cccc"
                   = "aa" ++ replicate _ _ ++ replicate _ 'c'

and isn't this the same as

foo ( ('a',2) : [('b', m), ('c', n)] ) = 
                   = replicate _ 'a' ++ foo [('b', m), ('c', n)]

or even

foo ( h : t )  
    | h == ('a',2)
    , t == [('b', m), ('c', n)] ) 
                   = replicate _ (fst h) ++ foo t

… right?

Could you continue this line of thought to its conclusion? Do you see how the code practically writes itself when we generalize our concrete cases by replacing concrete terms with variables?

This is basically a folding job.

Prelude> foldr (\(c,n) r -> replicate n c ++ r) "" [('a',2), ('b', 6), ('c', 1)]
"aabbbbbbc"

I use foldr to make concatenations to the head of the accumulator r which is more efficient.

Or as for a monadic approach you may write like

Prelude> let decode = (uncurry (flip replicate) =<<)
Prelude> decode [('a',2), ('b', 6), ('c', 1)]
Prelude> "aabbbbbbc"
Related