随机数序列 un Haskell 和 State Monad,我做错了什么?

Random number sequence un Haskell and State Monad, what am I doing wrong?

作为我 Haskell 旅程的一部分,我正在实现一个光线追踪器,我需要能够在代码中的多个位置绘制随机数序列。通常我希望能够为每个像素和像素并行计算 64 个样本。

我正在寻找状态 monad 来实现这一点,我被这个答案引导 Sampling sequences of random numbers in Haskell 但是我写的代码没有终止,它的内存消耗激增。

这里是代码的抽象部分: 我希望能够在代码中多次调用 sampleUniform 以获得新的随机数列表,但如果我这样做 runhaskell test.hs,它会输出 lis [ 的第一个字符,然后它陷入了一个明显的无限循环。

module Main (main
            , computeArray) where

import Control.Monad
import Control.Monad.State (State, evalState, get, put)
import System.Random (StdGen, mkStdGen, random)
import Control.Applicative ((<$>))

type Rnd a = State StdGen a

runRandom :: Rnd a -> Int -> a
runRandom action seed = evalState action $ mkStdGen seed

rand :: Rnd Double
rand = do
  gen <- get
  let (r, gen') = random gen
  put gen'
  return r

{- Uniform distributions -}
uniform01 :: Rnd [Double]
uniform01 = mapM (\_ -> rand) $ repeat ()

{- Get n samples uniformly distributed between 0 and 1 -}
sampleUniform :: Int -> Rnd [Double]
sampleUniform n = liftM (take n) uniform01

computeArray :: Rnd [Bool]
computeArray = do
  samples1 <- sampleUniform 10
  samples2 <- sampleUniform 10
  let dat = zip samples1 samples2
  return $ uncurry (<) <$> dat

main :: IO ()
main = do
  let seed = 48
  let res = runRandom computeArray seed
  putStrLn $ show res

uniform01 通过 无限 次计算对您的状态进行线程化处理,这意味着尽管它会延迟生成 result,没有希望在最后检索最终的 state 以用于下一次采样。 liftM (take n) 只影响最终值,不影响用于计算它的状态效应。因此,正如所写,您只能使用 uniform01/sampleUniform 一次。

相反,您可以仅通过您使用的 rand 个操作来线程化状态,例如与

sampleUniform n = mapM (\_ -> rand) $ replicate n ()

或更简单

sampleUniform n = sequence $ replicate n rand