Can you create more than one element of a list at a time with a list comprehension in haskell?
-
05-09-2019 - |
Question
So, for example, say I had a list of numbers and I wanted to create a list that contained each number multiplied by 2 and 3. Is there any way to do something like the following, but get back a single list of numbers instead of a list of lists of numbers?
mult_nums = [ [(n*2),(n*3)] | n <- [1..5]]
-- this returns [[2,3],[4,6],[6,9],[8,12],[10,15]]
-- but we want [2,3,4,6,6,9,8,12,10,15]
Solution
you could use concat.
concat [ [(n*2),(n*3)] | n <- [1..5]]
output: [2,3,4,6,6,9,8,12,10,15]
OTHER TIPS
I find that extending the list comprehension makes this easier to read:
[ m | n <- [1..5], m <- [2*n,3*n] ]
It might be helpful to examine exactly what this does, and how it relates to other solutions. Let's define it as a function:
mult lst = [ m | n <- lst, m <- [2*n,3*n] ]
After a fashion, this desugars to
mult' lst =
concatMap (\n -> concatMap (\m -> [m]) [2*n,3*n]) lst
The expression concatMap (\m -> [m])
is wrapping m
up in a list in order to immediately flatten it—it is equivalent to map id
.
Compare this to @FunctorSalad's answer:
mult1 lst = concatMap (\n -> [n*2,n*3]) lst
We've optimized away concatMap (\m -> [m])
.
Now @vili's answer:
mult2 lst = concat [ [(n*2),(n*3)] | n <- lst]
This desugars to:
mult2' lst = concat (concatMap (\n -> [[2*n,3*n]]) lst)
As in the first solution above, we are unnecessarily creating a list of lists that we have to concat
away.
I don't think there is a solution that uses list comprehensions, but desugars to mult1
. My intuition is that Haskell compilers are generally clever enough that this wouldn't matter (or, alternatively, that unnecessary concat
s are cheap due to lazy evaluation (whereas they're lethal in eager languages)).
In some similar cases concatMap can also be convenient, though here it doesn't change much:
concatMap (\n -> [n*2,n*3]) [1..5]