I am learning Haskell programming, and I am trying to understand how lists work, hence I attempted writing two possible length
functions:
myLength :: [a] -> Integer
myLength = foldr (\x -> (+) 1) 0
myLength1 :: [a] -> Integer
myLength1 [] = 0
myLength1 (x:xs) = (+1) (myLength1 xs)
Which one is better?
From my point of view, myLength1
is much easier to understand, and looks natural for operating on lists.
On the other hand, myLength
is shorter and it does not use recursion; does this imply myLength
runs faster than myLength1
?
Take in mind this "pseudo implementation" of foldr
:
foldr :: function -> initializer -> [a] -> b
foldr _ i [] = i
foldr f i (x:xs) = x `f` (foldr f i xs)
Now we have your code
myLength :: [a] -> Integer
myLength = foldr (\x -> (+) 1) 0
myLength1 :: [a] -> Integer
myLength1 [] = 0
myLength1 (x:xs) = (+1) (myLength1 xs)
Since foldr
is also recursive itself, your myLength1 and myLength will be almost the same but in the first case the recursive call is done by foldr instead of explicitly by yourself. They should run around the same time.
Both functions do the same thing : foldr use recursion and will end up executing similarly to your directly recursive function. It could be argued that the foldr version is cleaner (once you're accustomed to them, higher order function are often more readable than direct recursion).
But those two functions are pretty bad : they'll both end up building a big thunk (an unevaluated value) 1 + (1 + (1 + ... + 0)..))
which will take a lot of memory ( O(n) space ) and will slow evaluation. To avoid that you should start adding the 1s from the beginning of the list, like so :
betterLength xs = go 0 xs
where
go n [] = n
go n (_:xs) = n `seq` go (n+1) xs
The seq
ensures that n is evaluated before the go function is called recursively and thus there is no accumulation of +1
. With the BangPatterns
extension, you can write this :
betterLength xs = go 0 xs
where
go n [] = n
go !n (_:xs) = go (n+1) xs
It is also possible to do this version with a fold :
betterLength = foldl' (\n _ -> n + 1) 0
where the foldl'
start from the left and is strict (').
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With