Search code examples
functional-programmingpurescript

Which is a more efficient version of "takeEnd" function


I am new to PureScript so I am re-creating some basic functions, and I wanted to recreate "takeEnd" function which takes specified number of elements from the end of the list.

Here is solution I wrote:

takeEnd :: forall a. Int -> List a -> List a
takeEnd _ Nil = Nil
takeEnd n l = go n Nil $ reverse l where
    go _ new Nil = new
    go 0 new _ = new
    go n new  (x : xs) = go (n - 1) (x : new) xs

And here is a solution I found in a book:

takeEnd :: forall a. Int -> List a -> List a
takeEnd _ Nil = Nil
takeEnd n  = go >>> snd where
    go Nil = Tuple 0 Nil
    go (x : xs) = go xs
        # \(Tuple c nl) -> Tuple (c + 1) $ if c < n then x : nl else nl

I am interested in which version is more efficient? If I am not mistaken I believe also that second version is not tail optimized


Solution

  • The second solution does a single traversal of the list but is not tail recursive, so creates stack frames.

    The first solution looks incorrect and gives the elements backwards. So it should do one traversal of the whole list and then a traversal of n, then possibly another reverse - so it is less efficient in terms of time.

    PureScript often makes you decide between these efficiencies, due to strict evaluation. Could you blow the stack with the size of your list? If so, you have to stick with the theoretically slower, first solution.