2015-08-08 14 views
5

Come parte del mio viaggio Haskell, sto implementando un raytracer e ho bisogno di essere in grado di disegnare sequenze di numeri casuali in diversi punti del codice. In genere mi piacerebbe essere in grado di dire 64 campioni per ogni pixel e i pixel sono calcolati in parallelo.Sequenza di numeri casuali di Haskell e State Monad, cosa sto facendo male?

Stavo osservando la monade di stato per ottenere ciò ed ero guidato da questa risposta Sampling sequences of random numbers in Haskell ma il codice che ho scritto non termina e il suo consumo di memoria esplode.

Qui è la parte astratta del codice: mi saltellava per essere in grado di chiamare sampleUniform più volte nel codice per ottenere nuovi elenchi di numeri casuali, ma se lo faccio runhaskell test.hs, si emette il primo carattere della LIS [ e poi è bloccato in un ciclo apparentemente infinito.

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

Vedi anche [Perché la funzione di sequenza Haskell non può essere pigri o perché funzioni ricorsive monadici non può essere pigro] (http://stackoverflow.com/q/31892418/1333025) .. –

risposta

5

uniform01 fili suo stato attraverso un infinita numero di calcoli, il che significa che anche se produce risultato pigramente, non c'è speranza di recuperare una finale stato alla fine da utilizzare per la prossimo campionamento. liftM (take n) riguarda solo il valore finale, non gli effetti di stato utilizzati per calcolarlo. Pertanto, come scritto, è possibile utilizzare solo uniform01/sampleUniform una volta.

Invece è possibile passare lo stato solo attraverso quante più azioni rand si utilizzano, ad es. con

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

o semplice

sampleUniform n = sequence $ replicate n rand 
+1

O anche 'replicateM n rand' – chi

+0

Grande, grazie per la spiegazione e la soluzione corretta! – overlii

+0

Ora posso fare antialiasing :) La prossima domanda è, posso usare questa Monad di stato in parallelo? O sarebbe meglio avere uno stato per thread? Forse per un altro week end :) – overlii

Problemi correlati