作家单声道的记忆

哈斯凯琳

注意我只是想了解下面这段特定代码中发生的事情。我知道这可能不是解决问题的最佳方法。

我正在尝试将懒惰的Writermonad与已记忆的fibonacci函数一起使用,以计算该函数被调用的次数。该函数快速返回正确的值,但是Writer环境从不返回并且不使用任何CPU或内存。

import Control.Monad.Writer.Lazy as W

fib :: Int -> Writer (Sum Int) Int
fib = let fibs = mapM fib' [0..]
          fib' 0 = return 0
          fib' 1 = return 1
          fib' n = liftM2 (+) (fib $ n-1) (fib $ n-2)
      in \n -> tell (Sum 1) >> fibs >>= return . (!!n)

Prelude W> runWriter $ fib 51
(20365011074,Sum {getSum = Interrupted.

有人可以解释发生了什么吗?环境为什么不返回值?

编辑

无限列表[0..]在这里不是问题。我尝试用诸如[0..10]的有限列表替换它,[0..n]但问题仍然存在。如果无限列表是问题,那将是非常占用内存的计算,这就是为什么我在上面提到它不消耗任何CPU或内存的原因,这令我感到困惑。

我认为,由于懒惰,在评估fib函数的节点时会在某种程度上发生死锁

彼得

问题是mapM fib' [0..]这是一种有效的计算,可在monad中计算无限列表,为此,在这种情况下,还需要组合无限数量的效果tell (Sum 1)由于Writer的懒惰,您可以访问结果,但是monoid部分中的计数永远不会结束。

更新:即使您使列表有限,它仍然无法正常工作。问题在于它mapM fib' [0..10]代表“斐波纳契数的列表以及计算它们所需的总呼叫数”。因此,在表达式中,tell (Sum1) >> fibs >>= ...您总是将所有呼叫总数加到计数器上,这显然是您不想要的。

而且,它创建了一个无限循环:对fibinvokes的任何调用都会fib'计算其所有元素的调用次数,因此会调用(在其他调用中)再次fib' 2调用fib仅当您将列表限制为时,无限递归才会停止[0..1]同样,问题在于mapM将给定单子计算的所有效果结合在一起。

相反,您需要这样的东西:

import Control.Monad.Writer.Lazy as W

fib :: Int -> Writer (Sum Int) Int
fib = let fibs = map fib' [0..] -- <<<<
          fib' 0 = return 0
          fib' 1 = return 1
          fib' n = liftM2 (+) (fib $ n-1) (fib $ n-2)
      in \n -> tell (Sum 1) >> fibs !! n -- <<<<

在此fibs :: [Writer (Sum Int) Int],因此对于每个元素,它既包含结果,又包含其计算所需的调用次数。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章