ove*_*lii 5 random haskell state-monad
作为我的Haskell之旅的一部分,我正在实现一个光线跟踪器,我需要能够在代码中的几个位置绘制随机数序列.通常我希望能够为每个像素获得64个样本,并且并行计算像素.
我正在寻找状态monad来实现这一点,我在这个答案的指导下在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
Run Code Online (Sandbox Code Playgroud)
uniform01通过无限数量的计算来处理你的状态,这意味着尽管它会懒散地产生结果,但是没有希望在最后检索最终状态以用于下一次采样.liftM (take n)仅影响最终值,而不是用于计算它的状态效果.因此,如上所述,您只能使用uniform01/ sampleUniform一次.
相反,你可以通过只rand使用你所使用的动作来处理状态,例如
sampleUniform n = mapM (\_ -> rand) $ replicate n ()
Run Code Online (Sandbox Code Playgroud)
或者更简单
sampleUniform n = sequence $ replicate n rand
Run Code Online (Sandbox Code Playgroud)