【问题标题】:Haskell: `reverse` or right `cons`, which is more efficientHaskell:`reverse` 或正确的`cons`,效率更高
【发布时间】:2014-06-21 17:31:54
【问题描述】:

自从我前段时间学习 Haskell 以来,我一直看到人们 reverse 列表,只是稍后将它们反过来。下面是一个用分隔符分割字符串的函数:

splitOn ::Eq a => a -> [a] -> [[a]]
splitOn sep str = s_word str []
   where s_word []     w = [reverse w]
         s_word (c:cs) w = if (c == sep) then reverse w : s_word cs []
                           else s_word cs (c:w)

我认为原因是“缺乏”右/反向 cons 运算符,例如:

rcons xs x = xs ++ [x]

当然,rcons 的效率远低于 cons 运算符 (:)。

但上面的代码似乎通过使用reverse 引入了它自己的低效率。我想知道它是否比以下变体更有效或效率更低:

splitOn' ::Eq a => a -> [a] -> [[a]]
splitOn' sep str = s_word str []
   where s_word []     w = [w]
         s_word (c:cs) w = if (c == sep) then w : s_word cs []
                           else s_word cs (rcons w c)

这两个函数似乎实现了同样的事情。我认为第二个版本似乎更直观(尽管可能不那么聪明)。像这样使用rcons 有什么陷阱吗? (无限列表,懒惰等)

谢谢。

附:输出:

*Main> splitOn' ',' "a,b,"
["a","b",""]

【问题讨论】:

  • 如果我遇到了一个我认为确实需要在两端高效consing的问题,我会使用Data.Sequence from the containers package
  • 还请注意,您可以高效地编写splitOn 而无需反转 rconsing。考虑使用break (== x)
  • 希望reverse 的内部实现比天真的Haskell 实现更理智一点。它可以是一个常数时间的实现。但话又说回来,我不知道 ghc 的任何实现细节。
  • @bitmask,内置的 Haskell 列表是所有传统实现(包括 GHC)中真正的单链表。基本上,这是因为任何更高级的东西都会减慢至少一些适当使用它们的代码。如果您需要更高级的东西,可以从一个或另一个包裹中获得。

标签: list haskell


【解决方案1】:

如果使用“双反转”只添加单个元素,rcons 可能会更有效,因为它只会遍历输入列表一次,而每个reverse 将总共遍历两次。

但是,在您给出的splitOn' 示例中,reverse 在每个输出字中仅使用一次,因为该值一开始就以相反的形式累积。更重要的是,(:) 在调用reverse 之前会被多次使用。

在您使用rcons 的替代方案中,每个新元素都会对列表进行线性遍历,而使用reverse 在结果准备好后将只有一个线性遍历。

【讨论】:

猜你喜欢
  • 2018-12-29
  • 1970-01-01
  • 1970-01-01
  • 2010-10-28
  • 1970-01-01
  • 2020-11-16
  • 2017-06-17
  • 2021-11-06
  • 1970-01-01
相关资源
最近更新 更多