Reputation: 3762
Consider the following function for finding the second-to-last element of a list:
myButLast (x:xs) = if length xs > 1 then myButLast xs else x
This is an O(n^2) algorithm, because length xs
is O(n) and is called O(n) times. What is the most elegant way to write this in Haskell such that length
stops once it gets past 1, so that the algorithm is O(n)?
Upvotes: 4
Views: 3927
Reputation: 83
Here is my solution using the tail function from prelude
myButLast :: [Int] -> Maybe Int
myButLast (x:xs) | ((null xs) || null (tail xs)) = Just x
| otherwise = myButLast(xs)
myButLast [] = Nothing
Upvotes: 1
Reputation: 71119
Another zip-based solution, without exlicitly zipping. Throw in some safety for good measure.
import Data.Maybe
g :: Int -> [a] -> Maybe a
g i = (head <$>) . listToMaybe . reverse -- quick'n'dirty safe-last
. getZipList . traverse ZipList
. sequence [id, drop i]
g 1 [1..10] => Just 9
g 3 [1..10] => Just 7
g 13 [1..10] => Nothing
Upvotes: 1
Reputation: 11238
Another solution:
myButLast [] = error "List is empty"
myButLast [x] = error "List is a singleton"
myButLast xs = last $ init xs
Upvotes: 4
Reputation: 116174
Exploiting zip
:
\l -> fst . last $ zip l (tail l)
Also available in a pointless, obfuscated style:
fst . last . (zip <*> tail)
or even without parentheses (thanks to @melpomene):
fst . last . ap zip tail
Other variants:
last . ap (zipWith const) tail
Upvotes: 5
Reputation: 85887
The easiest way is to avoid length
:
myButLast (x : _ : []) = x -- base case
myButLast (_ : xs) = myButLast xs
The definitive reference on patterns in Haskell is the language report: https://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-580003.17
GHC implements a few extensions described at https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/syntax-extns.html#pattern-guards.
Upvotes: 9
Reputation: 52290
what about
myButLast [] = error "oho"
myButLast [x,_] = x
myButLast (_:xs) = myButLast xs
Upvotes: 6