Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell Length function implementation

Tags:

haskell

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?

like image 820
anru Avatar asked May 12 '17 07:05

anru


2 Answers

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.

like image 87
Netwave Avatar answered Nov 15 '22 06:11

Netwave


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 (').

like image 41
Jedai Avatar answered Nov 15 '22 06:11

Jedai