注意我只是想了解下面这段特定代码中发生的事情。我知道这可能不是解决问题的最佳方法。
我正在尝试将懒惰的Writer
monad与已记忆的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 >>= ...
您总是将所有呼叫的总数加到计数器上,这显然是您不想要的。
而且,它创建了一个无限循环:对fib
invokes的任何调用都会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] 删除。
我来说两句