【问题标题】:Understanding Haskell lazy evaluation [duplicate]了解 Haskell 惰性求值 [重复]
【发布时间】:2014-11-13 07:23:00
【问题描述】:

请原谅我的愚蠢问题,我是 Haskell 的新手。

我在 Haskell 中尝试了以下方法:

sum [fib n| n <- [1..], (even (fib n) && fib n < 4000000)] 

这需要无限的时间。如果我忽略n &lt;- [1..],解决方案马上就来了。

我认为这应该无关紧要,因为 Haskell 正在评估惰性。我是否误解了惰性评估?

【问题讨论】:

    标签: haskell lazy-evaluation fibonacci


    【解决方案1】:

    注意

    sum [ n | n <- [1..], n < 10 ]
    

    也不会终止,因为它会尝试所有可能的ns,以防发现还有一个元素“小于 10”,以便将其包含在总和中。

    相比之下,

    sum $ takeWhile (< 10) [ n | n <- [1..] ]
    

    将终止,因为一旦发现不满足谓词&lt;10 的项目,takeWhile 将丢弃列表的其余部分。

    【讨论】:

      【解决方案2】:

      你对列表的理解

      sum [fib n | n <- [1..], even (fib n) && fib n < 4000000]
      

      等价于表达式

      sum $ map fib $ filter (\n -> even (fib n) && fib n < 4000000) [1..]
      

      filter的定义:

      filter :: (a -> Bool) -> [a] -> [a]
      filter predicate [] = []
      filter predicate (x:xs)
          | predicate x = x : filter predicate xs
          | otherwise   =     filter predicate xs
      

      我们可以看到它总是会检查列表中的每个元素,直到它到达列表的末尾。提供给表达式过滤的列表是[1..],它是无限的。这在 Haskell 中很好,它只是意味着如果你强制评估整个列表,过滤器将永远不会完成。然后你将它传递给map fib,它也可以很好地处理无限列表,但是你将它传递给sum,这需要有限数量的元素相加。

      要解决此问题,正如@chi 所指出的,您可以改用takeWhile

      sum $ map fib $ filter (\n -> even (fib n)) $ takeWhile (\n -> fib n < 4000000) [1..]
      

      虽然我会注意到您在此表达式中应用了 fib 3 个不同的时间。最好是先map fib,然后就不用再申请了:

      sum $ filter even $ takeWhile (< 4000000) $ map fib [1..]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多