2012-06-15 28 views
6

¿Podría alguien describir cómo funcionan el siguiente constructor de tipos y funciones?Haskell Random 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] 

Respuesta

8

Vamos a empezar por el principio:

type Rand a = State StdGen a 

Esta línea le dice que Rand a es un sinónimo de tipo para State, cuyo estado viene dada por StdGen y cuyo valor final es de tipo a. Esto se usará para almacenar el estado del generador de números aleatorios entre cada solicitud de un número aleatorio.

El código para getRandom se puede convertir en notación hacer:

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 función runRand toma una semilla inicial n y un valor r de tipo Rand a (que, recordemos, es sólo un sinónimo de State StdGen a). Crea un generador nuevo con mkStdGen n y lo alimenta a evalState r. La función evalState solo evalúa el valor de retorno de un tipo State s a, ignorando el estado.

Una vez más, podemos convertir en runRandIOdo notación:

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 

Por último, getRandoms toma un número n que representa el número de valores aleatorios que desea generar. Construye una lista [1..n] y aplica getRandom a la lista. Tenga en cuenta que los valores reales en [1..n] no se utilizan (puede saber porque la función lambda comienza con \_ -> ...). La lista está allí para tener algo con la cantidad correcta de elementos. Dado que getRandom devuelve un valor monádico, usamos mapM para mapear sobre la lista, lo que hace que el estado (es decir, StdGen) se enhebre correctamente a través de cada una de las llamadas al getRandom.

5

La idea básica es simple: para crear números pseudoaleatorios, debe mantener algún estado entre las llamadas a funciones. Por lo tanto, el tipo Rand a se define como "a junto con el estado necesario para la aleatoriedad".

El estado se almacena utilizando State monad. Esto proporciona dos acciones principales: get y put, que hacen exactamente lo que parecen. Entonces, getRandom solo busca el estado actual y luego llama a la función random. Esta función devuelve dos valores: el valor aleatorio y el nuevo estado. Luego, acaba de put el nuevo estado y ajusta el valor resultante.

runRand le permite desplegar un valor "aleatorio" dado una semilla. evalState le permite ejecutar un cálculo con estado (es decir, un valor de tipo State s a o, en este caso, Rand a) dado un estado inicial y luego simplemente descarta el estado final, solo le da el resultado. Esto le permite ejecutar un Rand a con una semilla determinada y solo devuelve el valor resultante. El valor solo puede tener el tipo a, en lugar de Rand a porque siempre le dará el mismo resultado para la misma semilla.

runRandomIO simplemente hace lo mismo excepto que obtiene la semilla basada en algún estado global en IO.

getRandoms simplemente se obtiene una lista de valores Rand a llamando getRandom para cada elemento de la lista [1..n] (ignorando el número real).