小编His*_*ess的帖子

Haskell中的懒惰和尾部递归,为什么会崩溃?

我有这个相当简单的函数来计算大列表元素的平均值,使用两个累加器来保存到目前为止的总和以及到目前为止的计数:

mean = go 0 0
    where
      go s l []     = s / fromIntegral l
      go s l (x:xs) = go (s+x) (l+1) xs

main = do
  putStrLn (show (mean [0..10000000]))
Run Code Online (Sandbox Code Playgroud)

现在,用严格的语言,这将是尾递归,并且没有问题.然而,由于Haskell很懒,我的谷歌搜索让我明白(s + x)和(l + 1)将作为thunk传递递归.所以整件事崩溃和烧伤:

Stack space overflow: current size 8388608 bytes.
Run Code Online (Sandbox Code Playgroud)

进一步的谷歌搜索后,我发现seq$!.这似乎我不理解,因为我在这种情况下使用它们的所有尝试都证明是徒劳的,错误信息说的是关于无限类型的东西.

最后我发现-XBangPatterns,它通过改变递归调用来解决所有问题:

go !s !l (x:xs) = go (s+x) (l+1) xs
Run Code Online (Sandbox Code Playgroud)

但我对此并不满意,因为-XBangPatterns目前这是一个扩展.我想知道如何在不使用的情况下严格评估-XBangPatterns.(也许还可以学到一些东西!)

只是让你理解我缺乏理解,这就是我尝试过的(编译的唯一尝试,即):

go s l (x:xs) = go (seq s (s+x)) (seq l (l+1)) xs …
Run Code Online (Sandbox Code Playgroud)

optimization performance haskell lazy-evaluation ghc

18
推荐指数
3
解决办法
3057
查看次数