Reputation: 3111
What is the most efficient way to get the last element of a list in Haskell?
Example: getLastElement [1,2,3,4]
should return 4
.
As far as I know, last [1,2,3,4]
is not very efficient as Haskell digs through the list which results in an efficiency of O(n)
where n
is the length of the list.
Upvotes: 3
Views: 2143
Reputation: 8199
Note first that last
is not particularly great on other grounds, since it is a partial function like head
. If you are constantly using the tail end of the list, scrap it for Data.Sequence
or Data.Vector.Unboxed
:
import Data.Sequence
firstThing seq = case viewl seq of
EmptyL -> error "no beginning"
a :< as -> a
lastThing seq = case viewr seq of
EmptyR -> error "no end"
as :> a -> a
-- > lastThing (fromList "abc")
-- 'c'
-- > firstThing (fromList "abc")
-- 'a'
If you are using an unboxed type like Int
, Double
, Char
etc. then you will get still better results with Data.Vector.Unboxed
, see the documentation for remarks about the efficiency of Data.Vector.Unboxed.head
and Data.Vector.Unboxed.last
http://hackage.haskell.org/packages/archive/vector/0.10.0.1/doc/html/Data-Vector-Unboxed.html#g:4
In the special case where you are using lists of Char
s use Data.Text
wherever possible, this is emphatically the 'idiomatic' thing. Again see the documentation for head
and last
http://hackage.haskell.org/packages/archive/text/0.11.2.3/doc/html/Data-Text.html
If you are imagining a contrast between 'Haskell' lists and 'lists' in other languages, it is probably the contrast between 'Haskell' lists on the one hand, and vectors and arrays on the other; the latter are just as 'Haskelly' as lists.
Upvotes: 9
Reputation: 152737
Okasaki's book teaches us a nice trick for adding efficient APIs to existing data structures: wrap up the structure in a new one that caches the result of the API calls. If you only want to add last
to the set of efficient calls, here's the simple way to do that:
import Control.Monad -- mplus is a convenient spell
data LastList a = LastList
{ forget :: [a]
, last :: Maybe a
}
nil = LastList [] Nothing
cons x l = LastList (x : forget l) (last l `mplus` Just x)
Now the last
operation is O(1), easy-cheesy. This is very limited: you can't modify the end of the list efficiently, or get the second-to-last element efficiently, or build one of these out of a list efficiently (so you have to replace all list-based functions with these guys through your whole code to see the benefit), or anything like that, but the one call you asked to be efficient is.
Upvotes: 11
Reputation: 8511
With the built in list there is no way around O(n) complexity (you can only get the first element, and the rest of the list every time so you can't get to the end without going through all n elements).
Upvotes: 1
Reputation: 301147
That is it with the list data structure right? If you want the last element, you will have O(n)
complexity. There is no going around that.
You will have to consider a different data structure like Data.Sequence
Upvotes: 9