Rand monad的无限递归

时间:2018-04-01 02:17:54

标签: haskell monads lazy-evaluation

我有这个工作计划:

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

但我不完全理解为什么这个版本失败并填满我的所有内存(用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吗?

谢谢!

1 个答案:

答案 0 :(得分:4)

不幸的是,不,懒惰在这里无法帮助你:通过计算线程化的随机种子会增加数据依赖性,从而消除任何可能的懒惰。

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