2015-08-08 9 views
5

W ramach mojej podróży Haskell, wdrażam raytracer i muszę mieć możliwość rysowania sekwencji liczb losowych w kilku miejscach kodu. Zazwyczaj chciałbym móc powiedzieć, że 64 próbki dla każdego piksela i pikseli są obliczane równolegle.Losowa sekwencja numerów un Haskell i State Monad, co robię źle?

Patrzyłem na monadę państwową, aby to osiągnąć i kierowałem się tą odpowiedzią Sampling sequences of random numbers in Haskell, ale napisany przeze mnie kod nie kończy się, a zużycie pamięci eksploduje.

Oto wydobywane część kodu: I był cudny, aby móc nazwać sampleUniform kilka razy w kodzie, aby uzyskać nowe listy liczb losowych, ale jeśli to zrobię runhaskell test.hs, wyprowadza pierwszy znak Lis [ a następnie utknęła w pozornie nieskończonej pętli.

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 
+0

Zobacz również [Dlaczego funkcja sekwencja Haskell nie może być leniwy albo dlaczego rekurencyjne funkcje monadycznego nie może być leniwy] (http://stackoverflow.com/q/31892418/1333025) .. –

Odpowiedz

5

uniform01 wątki twój stan poprzez nieskończonej liczby obliczeń, co oznacza, że ​​chociaż produkuje wynik leniwie, nie ma nadziei na zdobycie ostatecznej stan na końcu użyć do następne pobieranie próbek. liftM (take n) wpływa tylko na ostateczną wartość, a nie na efekty stanu użyte do jej obliczenia. Dlatego, jak napisano, możesz użyć tylko jeden raz: uniform01/sampleUniform.

Zamiast tego można łączyć stan tylko z tak wieloma działaniami, jak rand, których używasz, np. z

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

lub prostsze

sampleUniform n = sequence $ replicate n rand 
+1

Lub nawet 'replicateM n rand' – chi

+0

Świetnie, dziękuję za wyjaśnienie i prawidłowe rozwiązanie! – overlii

+0

Teraz mogę zrobić antyaliazing :) Następne pytanie, czy mogę użyć tej Monady stanu równolegle? A może byłoby lepiej mieć stan na wątek? Może być na kolejny tydzień koniec :) – overlii

Powiązane problemy