简体   繁体   中英

Haskell Length function implementation

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 l eft and is strict ( ' ).

Using foldr , it can be implemented as:

length' xs = foldr (\_ n -> 1 + n) 0 xs

Explanation:

The lambda function (\\_ x -> n + 1) will increment the accumulator by one every time there is an element. For instance:

lenght' [1..4]

will be applied as: 1 + ( 1 + ( 1 + ( 1 + 0)))

Recall that foldr is defined like this:

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f v [] = v
foldr f v (x:xs) = f x (foldr f v xs)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM