Haskell random generation

Can someone describe how the following constructors and type functions work?

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 nr = 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] 
+6
source share
2 answers

Let it start from the beginning:

 type Rand a = State StdGen a 

This line tells you that Rand a is a type synonym for State , whose state is specified by StdGen and whose final value is of type a . This will be used to store the state of the random number generator between each request for a random number.

The getRandom code can be converted to a notation:

 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 

The runRand function takes an initial seed n and a value r type Rand a (which, remember, is simply a synonym for State StdGen a ). It creates a new generator with mkStdGen n and passes it to evalState r . The evalState function simply evaluates the return value of type State sa , ignoring the state.

Again, we can convert runRandIO to do :

 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 

Finally, getRandoms takes a number n representing the number of random values ​​that you want to generate. It creates a list [1..n] and applies getRandom to the list. Please note that the actual values ​​in [1..n] not used (you can tell because the lambda function starts with \_ -> ... ). There is only something in the list with the correct number of items. Since getRandom returns a monadic value, we use mapM to match on a list that causes the state (i.e., StdGen ) to draw correctly through each of the getRandom calls.

+8
source

The basic idea is simple - to create pseudo-random numbers you need to maintain some state between function calls. Thus, the type of Rand a is defined as " a along with the state necessary for randomness."

State is saved using the State monad. This provides two main actions - get and put , which do exactly what they sound. Therefore, getRandom just looks at the current state and then calls the random function. This function returns two values: a random value and a new state. Then you just put new state and complete the resulting value.

runRand allows runRand to expand a "random" value based on the seed. evalState allows evalState to perform a state-based calculation (i.e., a value of type State sa or, in this case, Rand a ), a given initial state, and then simply discards the final state, giving the result. Thus, this allows you to run Rand a with the given seed and returns only the return value. The value may be of type a rather than Rand a , because it will always give you the same result for the same semester.

runRandomIO does the same, except that the seed is based on some global state in IO.

getRandoms simply gets a list of Rand a values, calling getRandom for each element of the list [1..n] (ignoring the actual number).

+5
source

Source: https://habr.com/ru/post/918157/


All Articles