Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pick the second last element of the list

Tags:

haskell

After some time I've decied to back to learning some functional programming. I've decided to pick Haskell this time, because of its features and .. syntax.

At the moment I am doing some exercises and I am stuck. I want to write a function that picks second last element from the list, i.e. given [1,2,3,4] it would 3.

Here's my function.

lastButOne xs 
    | null xs = xs 
    | length xs == 1 = xs 
    | length xs == 2 = lastButOne head xs
    | otherwise = lastButOne tail xs

Unfortunately it yields some errors.

Couldn't match expected type `[a]' with actual type `[a1] -> [a1]'
Relevant bindings include
  xs :: [a] (bound at lastButOne.hs:1:12)
  lastButOne :: [a] -> [a] (bound at lastButOne.hs:1:1)
Probable cause: `tail' is applied to too few arguments
In the first argument of `lastButOne', namely `tail'
In the expression: lastButOne tail xs

I've tried with some parthness, like (head xs) and (tail xs), but it doesn't help.

   Occurs check: cannot construct the infinite type: a ~ [a]
   Expected type: [[a]]
     Actual type: [a]
   Relevant bindings include
     xs :: [a] (bound at lastButOne.hs:1:12)
     lastButOne :: [a] -> [a] (bound at lastButOne.hs:1:1)
   In the first argument of `head', namely `xs'
   In the first argument of `lastButOne', namely `(head xs)'

Follow-up: Or should I write Follow-up's. Well, my orginal idea was to write a function that yields head element if list length is one. So given the Lee's explanation it's easy to come up with the following:

lastPattern :: [a] -> Maybe a
lastPattern [] = Nothing
lastPattern [x] = Just x
lastPattern [x,_] = Just x
lastPattern (_:xs) = lastPattern xs

Ane here's the first question. What is the pattern for both conditions [x] and [x,_]?

Next thing I wanted to do is to write the same function with the reverse (as pointed out by Paul Johnson). I quickly came up with head (tail (reverse [1,2,3,4])) which seemed to work fine in the REPL. But when I started some coding I end up with

 lastRev :: [a] -> a
lastRev xs 
    | null xs = error "empty list"
    | length xs == 1 = error "too short"
    | otherwise = head (tail (reverse xs))

since the head function is head :: [a] -> a . The above function is a little mess for my taste, so to say. Is there any way I could make it to be :: [a] -> Maybe a ? That's the second question.

Last but not least - the third question. Which function is better in terms of performance? How can I measure it in Haskell?

like image 233
freefall Avatar asked Oct 27 '25 05:10

freefall


1 Answers

(head xs) returns an a and you're trying to pass it to lastButOne which requires a [a] argument. You could just return head xs directly in this case. You also have a problem with the first two cases since they return a list, while an element is required. Since there is no such element in this cases you could return an error:

lastButOne :: [a] -> a
lastButOne xs 
    | null xs = error "empty list"
    | length xs == 1 = error "list too short"
    | length xs == 2 = head xs
    | otherwise = lastButOne (tail xs)

are more functional solution would be to encode the partiality in the function type and return a Maybe a so you can return Nothing if the input list is too short:

lastButOne :: [a] -> Maybe a
lastButOne xs 
    | null xs = Nothing
    | length xs == 1 = Nothing
    | length xs == 2 = Just (head xs)
    | otherwise = lastButOne (tail xs)

finally, a better solution would be to use pattern matching instead of guarding on the length:

lastButOne :: [a] -> Maybe a
lastButOne [] = Nothing
lastButOne [_] = Nothing
lastButOne [x,_] = Just x
lastButOne (_:xs) = lastButOne xs
like image 51
Lee Avatar answered Oct 30 '25 08:10

Lee



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!