2012-06-15 13 views
6

Qualcuno potrebbe descrivere come il seguente tipo costruttore e funzioni lavorano?Haskell a caso Generation

type Rand a = State StdGen a 

getRandom :: (Random a) => Rand a 
getRandom = get >>= (\r -> let (a,g) = random r in (put g) >> (return a)) 

runRand :: Int -> Rand a -> a 
runRand n r = evalState r $ mkStdGen n 

runRandIO :: Rand a -> IO a 
runRandIO r = randomIO >>= (\rnd -> return $ runRand rnd r) 

getRandoms :: (Random a) => Int -> Rand [a] 
getRandoms n = mapM (\_ -> getRandom) [1..n] 

risposta

8

Cominciamo dall'inizio:

type Rand a = State StdGen a 

Questa linea vi dice che Rand a è un tipo sinonimo di un tipo State, il cui stato è data da StdGen e il cui valore finale è di tipo a. Questo sarà usato per memorizzare lo stato del generatore di numeri casuali tra ogni richiesta di un numero casuale.

Il codice per getRandom può essere convertito in notazione fare:

getRandom :: (Random a) => Rand a 
getRandom = do 
    r <- get     -- get the current state of the generator 
    let (a,g) = random r in do -- call the function random :: StdGen -> (a, StdGen) 
    put g     -- store the new state of the generator 
    return a     -- return the random number that was generated 

La funzione runRand prende un seme iniziale n e un valore r di tipo Rand a (che, ricordiamo, è solo un sinonimo di State StdGen a). Crea un nuovo generatore con mkStdGen n e lo invia a evalState r. La funzione evalState valuta solo il valore restituito di un tipo State s a, ignorando lo stato.

Anche in questo caso, siamo in grado di convertire in runRandIOdo notazione:

runRandIO :: Rand a -> IO a 
runRandIO r = do 
    rnd <- randomIO  -- generate a new random number using randomIO 
    return (runRand rnd r) -- use that number as the initial seed for runRand 

Infine, getRandoms prende un numero n che rappresenta il numero di valori casuali che si desidera generare. Costruisce un elenco [1..n] e applica getRandom all'elenco. Si noti che i valori effettivi in ​​[1..n] non vengono utilizzati (lo si può sapere perché la funzione lambda inizia con \_ -> ...). L'elenco è solo lì per avere qualcosa con il numero corretto di elementi. Poiché getRandom restituisce un valore monadico, usiamo mapM mappare sulla lista, che causa lo stato (cioè StdGen) da filettare correttamente attraverso ciascuna delle chiamate getRandom.

5

L'idea di base è semplice - per creare numeri pseudocasuali, è necessario mantenere uno stato tra chiamate di funzione. Quindi il tipo Rand a è definito come "a insieme allo stato necessario per la casualità".

Lo stato viene memorizzato utilizzando il State monad. Questo fornisce due azioni principali - get e put, che fanno esattamente quello che sembrano. Pertanto, getRandom cerca solo lo stato corrente e quindi chiama la funzione random. Questa funzione restituisce due valori: il valore casuale e il nuovo stato. Quindi devi solo il put il nuovo stato e racchiudere il valore risultante.

runRand consente di scartare un valore "random" dato un seme. evalState permette di eseguire un calcolo stateful (cioè un valore di tipo State s a o, in questo caso, Rand a) proposta uno stato iniziale e poi semplicemente scarta stato finale, solo si dà il risultato. In questo modo è possibile eseguire uno Rand a con un seme specificato e restituisce solo il valore risultante. Il valore può avere solo il tipo a, anziché lo Rand a perché darà sempre lo stesso risultato per lo stesso seme.

runRandomIO fa esattamente la stessa cosa tranne che ottiene il seme basato su uno stato globale in IO.

getRandoms solo si ottiene un elenco di valori Rand a chiamando getRandom per ogni elemento della lista [1..n] (ignorando il numero effettivo).