I read from Foldr Foldl Foldl' that foldl'
is more efficient for long finite lists because of the strictness property. I am aware that it is not suitable for infinite list.
Thus, I am limiting the comparison only for long finite lists.
concatMap
concatMap
is implemented using foldr
, which gives it laziness. However, using it with long finite lists will build up a long unreduced chain according to the article.
concatMap :: Foldable t => (a -> [b]) -> t a -> [b]
concatMap f xs = build (\c n -> foldr (\x b -> foldr c b (f x)) n xs)
Thus I come up with the following implementation with use of foldl'
.
concatMap' :: Foldable t => (a -> [b]) -> t a -> [b]
concatMap' f = reverse . foldl' (\acc x -> f x ++ acc) []
Test it out
I have build the following two functions to test out the performance.
lastA = last . concatMap (: []) $ [1..10000]
lastB = last . concatMap' (: []) $ [1..10000]
However, I was shocked by the results.
lastA:
(0.23 secs, 184,071,944 bytes)
(0.24 secs, 184,074,376 bytes)
(0.24 secs, 184,071,048 bytes)
(0.24 secs, 184,074,376 bytes)
(0.25 secs, 184,075,216 bytes)
lastB:
(0.81 secs, 224,075,080 bytes)
(0.76 secs, 224,074,504 bytes)
(0.78 secs, 224,072,888 bytes)
(0.84 secs, 224,073,736 bytes)
(0.79 secs, 224,074,064 bytes)
Follow-up Questions
concatMap
outcompetes my concatMap'
in both time and memory. I wonder there are mistakes I made in my concatMap'
implementation.
Thus, I doubt the articles for stating the goodness of foldl'
.
Are there any black magic in
concatMap
to make it so efficient?Is it true that
foldl'
is more efficient for long finite list?Is it true that using
foldr
with long finite lists will build up a long unreduced chain and impact the performance?
++
which runs in O(n) where n is the size of the left list... So you usually want to avoid that (since you are going to define a concat on a concat on a concat...) - Willem Van OnsemconcatMap
, how can I avoid the++
? Moreover,f x
is of size 1 for the test case. - Gavinfoldl'
is good if you are building something in a nice strict data type from a seed using a strict function to update - like anInt
starting with anInt
seed using(+)
to take the simplest example. If you are constructing a list or other lazy structure from a list,foldr
will be much more natural. - Michaelf x
(tryconcatMap (\x -> [-x,x]) [1,2,3]
andconcatMap' (\x -> [-x,x]) [1,2,3]
) - Alec