Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ways to get the middle of a list in Haskell?

I've just started learning about Functional Programming, using Haskel.

I'm slowly getting through Erik Meijer's lectures on Channel 9 (I've watched the first 4 so far) and in the 4th video Erik explains how tail works, and it fascinated me.

I've tried to write a function that returns the middle of a list (2 items for even lengths, 1 for odd) and I'd like to hear how others would implement it in

  • The least amount of Haskell code
  • The fastest Haskell code

If you could explain your choices I'd be very grateful.

My beginners code looks like this:

middle as | length as > 2   = middle (drop 2 (reverse as))
          | otherwise       = as
like image 695
Matt Ellen Avatar asked Nov 14 '09 18:11

Matt Ellen


People also ask

How do you get the first N elements of a list in Haskell?

There is a function in Haskell that takes first n elements of user-supplied list, named take . The syntax is: function-name arg1 arg2 . So, take takes first 1000 elements from an infinite list of numbers from 0 to infinity.

How do you remove a specific element from a list in Haskell?

Line 1: The type annotation remove_one :: [Int] -> Int -> [Int] shows that our function takes a list of integers and an integer element (element to be removed) and returns an integer list (the remaining list).


2 Answers

I don't make any performance claims, though it only processes the elements of the list once (my assumption is that computing length t is an O(N) operation, so I avoid it), but here's my solution:

mid [] = []                      -- Base case: the list is empty ==> no midpt
mid t = m t t                    -- The 1st t is the slow ptr, the 2nd is fast
  where m (x:_) [_] = [x]        -- Base case: list tracked by the fast ptr has
                                    -- exactly one item left ==> the first item
                                    -- pointed to by the slow ptr is the midpt.
        m (x:y:_) [_,_] = [x,y]  -- Base case: list tracked by the fast ptr has
                                    -- exactly two items left ==> the first two
                                    -- items pointed to by the slow ptr are the 
                                    -- midpts
        m (_:t) (_:_:u) = m t u  -- Recursive step: advance slow ptr by 1, and
                                    -- advance fast ptr by 2.

The idea is to have two "pointers" into the list, one that increments one step at each point in the recursion, and one that increments by two.

(which is essentially what Carl Smotricz suggested)

like image 34
Suppressingfire Avatar answered Sep 17 '22 01:09

Suppressingfire


Just for your amusement, a solution from someone who doesn't speak Haskell:

Write a recursive function that takes two arguments, a1 and a2, and pass your list in as both of them. At each recursion, drop 2 from a2 and 1 from a1. If you're out of elements for a2, you'll be at the middle of a1. You can handle the case of just 1 element remaining in a2 to answer whether you need 1 or 2 elements for your "middle".

like image 194
Carl Smotricz Avatar answered Sep 20 '22 01:09

Carl Smotricz