I'm learning haskell, and one of the exercises required that I write a function equivalent to enumFromTo
.
I came up with the following two implementations:
eft' :: Enum a => a -> a -> [a]
eft' x y = go x y []
where go a b sequence
| fromEnum b < fromEnum a = sequence
| otherwise = go (succ a) b (sequence ++ [a])
eft :: Enum a => a -> a -> [a]
eft x y = go x y []
where go a b sequence
| fromEnum b < fromEnum a = sequence
| otherwise = go a (pred b) (b : sequence)
I had a hunch that the first version does more work, as it puts each element into a list and concatenates to the existing sequence
, while the second version prepends a single element to a list. Is this the main reason for the performance difference or are there other significant factors or is my hunch slightly off the mark?
Testing in ghci with :set +s
reveals on my machine (Windows 10, GHC 8.2.2, intel i7-4770HQ):
*Lists> take 10 (eft 1 10000000)
[1,2,3,4,5,6,7,8,9,10]
(9.77 secs, 3,761,292,096 bytes)
*Lists> take 10 (eft' 1 10000000)
[1,2,3,4,5,6,7,8,9,10]
(27.97 secs, 12,928,385,280 bytes)
*Lists> take 10 (enumFromTo 1 10000000)
[1,2,3,4,5,6,7,8,9,10]
(0.00 secs, 1,287,664 bytes)
My second hunch was that the take 10 (eft 1 10000000)
should perform better than take 10 (eft' 10000000)
because the latter has to build the list up all the way from 10000000 to 10 before it can return any useful values that we take
. Clearly this hunch was wrong, and I'm hoping someone can explain why.
Finally, the ghc implementation is incredibly more efficient than my naive implementations. I am curious to understand other optimizations have been applied to speed it up. The answer to this similarly titled SO question shares some code that seems to be from the ghc implementation, but doesn't explain how the "nastiness" gains efficiency.
-O2
(note that can be done within GHCi with the help of the-fobject-code
option). – duplodexs++[x]
needs to scan the whole listxs
, making a copy of it, so to addx
at the end. Instead,x:xs
does not scan or copy anything, and only requires O(1) time instead of O(N). – chi