【问题标题】:Implement reverse in Haskell that runs in linear time在以线性时间运行的 Haskell 中实现反向
【发布时间】:2010-08-22 21:06:44
【问题描述】:

我只是在学习 Haskell,如果我的问题很愚蠢,很抱歉。我正在阅读 learnyouahaskell.com,现在我在第 5 章“递归”。有一个标准“反向”功能的实现示例:

reverse' :: [a] -> [a]  
reverse' [] = []  
reverse' (x:xs) = reverse' xs ++ [x]  

但它似乎在 O(N^2) 时间内运行,而标准反向运行在 O(N) 时间(我希望如此)。以下代码说明了这一点:

sum (reverse [1,2..1000000]) -- runs pretty fast
sum (reverse' [1,2..1000000]) -- never finishes

所以,我开始思考如何更快地实现我自己的反向。用命令式语言很容易做到。也许我需要后续章节中的一些更高级的材料来做到这一点?欢迎任何提示。

【问题讨论】:

    标签: haskell performance complexity-theory


    【解决方案1】:

    可以使用额外的累加器参数高效实现,例如本例中fac 的第二个参数:

    factorial n = fac n 1
      where
        fac 0 r = r
        fac n r = fac (n-1) (r*n)
    

    如果你只是想知道标准库是怎么做的,也可以look at the source code

    【讨论】:

      【解决方案2】:

      reversePrelude 中定义。

      您可以将其实现为:

      reverse = foldl (flip (:)) []
      

      【讨论】:

      • 这不会受到foldl 的经典堆栈问题的影响吗?
      • Kru 提供的版本直接来自源代码。注意列表的内容不只评估它们的位置。此外,如果您不使用整个反向列表,则永远不会创建列表。
      • @3noch 我认为foldr 是这种情况,但foldl 不是这种情况
      • 我会评论说,这实际上是 OP 推测的“来自后续捕获者的更高级材料”的情况。特别是在“高阶函数”一章,learnyouahaskell.com/higher-order-functions,“仅折叠和马”小节中讨论了此实现
      【解决方案3】:
      reverse l =  rev l []
        where
          rev []     a = a
          rev (x:xs) a = rev xs (x:a)
      

      【讨论】:

      【解决方案4】:
      foldl (\acc x -> x:acc) [] xs
      

      这在 O(n) 中运行。这个想法很简单——你拿一个空列表(累加器)并将元素从上到下传递给它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-05-29
        • 1970-01-01
        • 2018-06-03
        • 2013-12-15
        • 2012-01-09
        • 1970-01-01
        • 2012-11-19
        • 1970-01-01
        相关资源
        最近更新 更多