【问题标题】:Is this Haskell code efficient?这个 Haskell 代码高效吗?
【发布时间】:2011-05-18 22:32:19
【问题描述】:

作为初学者练习,我实现了以下函数来查找列表中的第 n 个元素:

elem_at (h:_) 0 = h  
elem_at (_:t) n = elem_at t (n-1)  
elem_at _ _     = error "Index out of bounds"

但是,如果我调用:elem_at [1,2,3,4] 5,它是否正确,只有在遍历整个列表后才会返回“索引超出范围”,以便最后一行与模式匹配_ _ 与 [] 1?更一般地说,如果列表很大,那不是性能问题吗? ghc 能以某种方式优化这种情况吗?

【问题讨论】:

    标签: list data-structures haskell performance


    【解决方案1】:

    事实上,这几乎是索引列表的规范方法。您需要添加检查负数

    elem_at xs n | n < 0 =  error "elem_at: negative index"
    

    您可以为空列表添加特定匹配项:

    elem_at [] _         =  error "elem_at: index too large"
    

    以及基础和归纳案例:

    elem_at (x:_)  0         =  x
    elem_at (_:xs) n         =  elem_at xs (n-1)
    

    你已经得到了列表索引的 Prelude 定义,(!!) 运算符。

    可以通过worker wrapper产生一个更高效的版本,将索引测试浮动到顶层。

    【讨论】:

    • 谢谢!这就说得通了。但是等等……到数学[]_,从[1,2,3,4]和5开始,我必须使用归纳案例4次,对吧?
    • 如果 n 太大,你会遇到 elem_at [] _ 的基本情况。这很好,因为如果将 n 与长度进行比较,最终会遍历列表一次以获取长度,然后再次查找元素。
    • @Tim 你是对的。补充问题:Haskell 是否在内部存储列表的长度以便查询它可以真正快速?
    • 不。您可以编写自己的列表,但是您不能使用对列表进行操作的内置函数。已经有一些关于将列表类型变成类型类的讨论,以便可以对其进行概括,但我认为这方面没有做任何事情。此外,@Don Stewart 会更好地继续这种思路,所以我在此不再赘述。
    • @Frank:不,列表是简单的递归类型,data [a] = [] | a : [a]。就是这样。如果您的算法依赖于索引或取长,请考虑使用手指树或向量(Data.SequenceData.Vector)。
    【解决方案2】:

    我相信,haskell 的实现与在任何语言中遍历单链表一样有效。如果数据存储在不同的结构中,那么您可以在不遍历列表的情况下回答问题。

    【讨论】:

    • 我的“问题”是我非常喜欢上面的代码,因为它非常简洁。如果我什么都不写,并且在运行时仍然有非常高效的代码,那将是理想的。我已经编写了另一个版本,可以更快地触发越界错误(我认为),但它并不那么优雅。
    • 如果您有一个静态列表并且只想快速查找,则可以通过将列表转换为数组来提高效率。 haskell.org/haskellwiki/Modern_array_libraries
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多