Random Intro
Note
These functions requireimport System.Random
.
Source: LYaH, Input and Output.
Haskell functions are pure, which means the same input must give the same output. A “random number generator” that just puts out different numbers each time you call it is not pure. Instead, in Haskell, the randomization functions output a random value and a new random number generator that will give different results.
Quick Example
A random function in Haskell should put out a random number generator in addition to the number or numbers it makes.
import System.Random
rand1to6 :: StdGen -> (Int, StdGen)
rand1to6 g = randomR (1,6) g
main = do print "Setup"
g <- newStdGen
print "Random roll"
let (n,gNew) = rand1to6 g
print n
The example illustrates:
- Making a “standard generator” - the original source of randomness.
NOTE: this requires the
<-
operator inmain
and cannot be done in elsewhere. (Slight lie.) - The random function gives a new generator state
gNew
, which should be used for later random choices.
Getting Random Numbers
The most common method is randomR
, which gives random numbers in a range.
-
(
randomR
) TherandomR
function gives a random numberr
in a rangelow <= r <= high
. Notice that both ends of the range are included. Use a helper function with a type signature.rand20 :: StdGen -> (Int, StdGen) rand20 gen = randomR (1,20) gen main = do gen <- newStdGen let (n, gen') = rand20 gen print n
Multiple Random Numbers
This is tricker than I would like it to be. The most straightforward way is write a recursive function and manually manage the count.
kRolls :: Int -> StdGen -> [Int]
kRolls 0 _ = []
kRolls k g = n : krand (k-1) g'
where (n,g') = randomR g (1,6)
If you want to make more random numbers after this, you need get the final state of the random number generator out of the function. That will be an exercise.
Exercises:
-
Modify the code above to write a function that produces a list of integers and also a random generator that you can continue to use later.
kRollsG :: Int -> StdGen -> ([Int], StdGen)`
-
What is incorrect with the following code? If you can’t tell, print out the results and look for a pattern.
kRollOne :: StdGen -> Int
kRollOne g = n
where (n,_) = randomR g (1,6)
kRollsBad :: Int -> StdGen -> [Int]
kRollsBad n g = [kRollOne g | _ <- [1..n]]
Making a StdGen
The best default choice is to use a newStdGen
. You could also use
mkStdGen
if you want to specify the initial “seed” (so the random
numbers will all follow the same pattern every time you run the program).
Notice the use of let
and <-
in the examples.
-
newStdGen
: An IO function that uses the system’s random number state to create a random number generator that gives different results every time you run it. -
mkStdGen
: Provide a seed number, like 109211, and get a random number generator that will produce the same results on every run. Good for debugging.
The code below creates two standard generators. This is just an example. Normally you would only use one.
main = do rng1 <- mkStdGen 109211
rng2 <- newStdGen
putStrLn "OK"
Common Problems
- Copy and pasting examples can mess up indentation. All of the items
in a
do
-block need to line up vertically - Variables: regular variables like
let x = 5
use a different syntax from “IO variables” likey <- newStdGen
.
Notes
- Avoid
getStdGen
. CallinggetStdGen
repeatedly will give the exact same random number generator each time. (It does not “advance the state”.) If you ever have more than one standard generator at a time, this will not be what you want.