【问题标题】:Memoization in the Writer monadWriter monad 中的记忆
【发布时间】:2014-04-03 11:37:27
【问题描述】:

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

我正在尝试将惰性 Writer monad 与 memozied 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 函数的节点时,某处会出现死锁。

【问题讨论】:

  • 你的猜测是正确的。对fib 的每次调用都会尝试执行单子操作fibs,它最终会尝试评估自身,因此它会卡住。而且因为这是使用图形缩减完成的,所以没有实际的计算发生,只是一个节点被自己阻塞了。由于纯度,编译器不需要继续遍历图中的这个无限循环,所以它不需要,这解释了 cpu/memory 使用情况。

标签: haskell monads memoization writer-monad


【解决方案1】:

问题是mapM fib' [0..]。这是一个计算 monad 中无限列表的有效计算,为此它还需要组合无限数量的效果,在本例中为 tell (Sum 1)。感谢Writer 的懒惰,您可以访问结果,但幺半群部分内的计数永远不会结束。

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

此外,它创建了一个无限循环:对fib 的任何调用都会调用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],因此对于每个元素,它都包含结果和计算所需的调用次数。

【讨论】:

  • 不,这不是问题,如果您将[1..] 替换为[1..10] 或任何有限列表,问题仍然存在。
  • @haskelline 我更新了答案。问题还是mapM,不适合这个问题。它不计算你想要的,在这种情况下也会导致无限循环。
猜你喜欢
  • 2016-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多