以有效的方式从列表中获取最后一个元素

时间:2013-01-11 18:27:21

标签: haskell

在Haskell中获取列表的最后一个元素的最有效方法是什么?

示例:getLastElement [1,2,3,4]应该返回4

据我所知,last [1,2,3,4]效率不高,因为Haskell挖掘列表会导致效率为O(n),其中n是列表的长度。

4 个答案:

答案 0 :(得分:11)

Okasaki的书教给我们一个很好的技巧,可以将有效的API添加到现有的数据结构中:将结构包装在一个缓存API调用结果的新结构中。如果想要将last添加到高效调用集中,这是执行此操作的简单方法:

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)

现在last操作是O(1),简单易懂。这是非常有限的:你不能有效地修改列表的末尾,或者有效地获得倒数第二个元素,或者有效地从列表中构建其中一个(所以你必须通过整个代码替换所有基于列表的函数以查看好处),或者类似的东西,但是你被要求高效的那个叫做。

答案 1 :(得分:9)

那就是列表数据结构对吗?如果您想要最后一个元素,则会有O(n)复杂度。没有解决这个问题。

您必须考虑不同的数据结构,例如Data.Sequence

答案 2 :(得分:9)

首先请注意last在其他方面并不是特别好,因为它是head之类的部分功能。如果您经常使用列表的尾端,请将其废弃至Data.SequenceData.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'

如果您使用的是未装箱的类型,例如IntDoubleChar等,那么使用Data.Vector.Unboxed您将获得更好的结果,请参阅相关文档Data.Vector.Unboxed.headData.Vector.Unboxed.last http://hackage.haskell.org/packages/archive/vector/0.10.0.1/doc/html/Data-Vector-Unboxed.html#g:4

的效率

在您使用Char使用Data.Text的列表的特殊情况下,这是强调'惯用'的事情。再次参阅headlast http://hackage.haskell.org/packages/archive/text/0.11.2.3/doc/html/Data-Text.html

的文档

如果你想象“Haskell”列表和其他语言中的“列表”之间的对比,那么一方面可能是'Haskell'列表与另一方面的矢量和数组之间的对比;后者和列表中的'Haskelly'一样。

答案 3 :(得分:1)

使用内置列表无法解决O(n)复杂性问题(您只能获取第一个元素,每次都可以获取列表的其余部分,因此无需遍历所有n个元素就无法完成)。