【问题标题】:Infinite recursion with the Rand monad使用 Rand monad 进行无限递归
【发布时间】:2018-04-01 10:31:58
【问题描述】:

我有这个工作程序:

import Control.Monad.Random

data Tree = Node Tree Tree Tree Tree
          | Leaf Bool
          deriving (Show)

randomTree' :: (RandomGen a) => Int -> Rand a Tree
randomTree' 0 = do
  r <- getRandom
  return $ Leaf r

randomTree' depth = do
  let d = depth-1
  a <- randomTree' d
  b <- randomTree' d
  c <- randomTree' d
  d <- randomTree' d
  r <- getRandom
  if r
    then return $ Node a b c d
    else randomTree' 0

randomTree1 :: Int -> Tree
randomTree1 seed = evalRand (randomTree' 4) (mkStdGen seed)

main = print $ randomTree1 1

但我不完全理解为什么这个其他版本会失败并填满我所有的 RAM(带有 thunk ?):

randomTree'' :: (RandomGen a) => Int -> Rand a Tree
randomTree'' depth = do
  let d = depth-1
  a <- randomTree'' d
  b <- randomTree'' d
  c <- randomTree'' d
  d <- randomTree'' d

  r1 <- getRandom
  r2 <- getRandom
  if depth > 0 && r1
    then return $ Node a b c d
    else return $ Leaf r2

由于懒惰,a b cd 不应该只在需要时才评估吗?

谢谢!

【问题讨论】:

  • 如果您对不同的变量使用相同的名称,则有问题...
  • 我会将递归调用移到then 分支下,以便您只在需要时进行调用。

标签: haskell monads lazy-evaluation


【解决方案1】:

不幸的是,不,懒惰在这里帮不了你:通过计算线程化的随机种子添加了一个数据依赖关系,它否定了任何可能的懒惰。

具体来说:您检查通过调用getRandom 计算得出的r1 是否为True。要知道这一点,必须在调用getRandom 时发现使用哪个种子;并且要知道使用哪个种子,必须首先对randomTree'' 的递归调用所需的种子进行修改。由于递归调用在基本情况下永远不会触底,它们只会消耗越来越多的 RAM,直到您的计算机阻塞。无赖!

【讨论】:

  • 但是 随机生成器支持split,它允许你创建一个惰性随机“monad”(monad 法则不会完全成立,但如果你关心的只是值的分布然后他们做)
  • @luqui 这是Control.Monad.Random.Lazy 吗?
  • @TimothéeJourde 不,该模块似乎仍然在其Monad(&gt;&gt;=) 实现中保持对种子的数据依赖。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-05
  • 2020-12-22
  • 2021-11-15
  • 2017-02-02
  • 1970-01-01
  • 2015-04-07
  • 1970-01-01
相关资源
最近更新 更多