Online Book Reader

Home Category

Learn You a Haskell for Great Good! - Miran Lipovaca [86]

By Root 449 0
from the current generator, and then make a list that has the value as its head and random numbers based on the new generator as its tail. Because we need to be able to potentially generate an infinite amount of numbers, we can’t give the new random generator back.

We could make a function that generates a finite stream of numbers and a new generator like this:

finiteRandoms :: (RandomGen g, Random a, Num n) => n -> g -> ([a], g)

finiteRandoms 0 gen = ([], gen)

finiteRandoms n gen =

let (value, newGen) = random gen

(restOfList, finalGen) = finiteRandoms (n-1) newGen

in (value:restOfList, finalGen)

Again, this is a recursive definition. We say that if we want zero numbers, we just return an empty list and the generator that was given to us. For any other number of random values, we first get one random number and a new generator. That will be the head. Then we say that the tail will be n - 1 numbers generated with the new generator. Then we return the head and the rest of the list joined and the final generator that we got from getting the n - 1 random numbers.

What if we want a random value in some sort of range? All the random integers so far were outrageously big or small. What if we want to throw a die? Well, we use randomR for that purpose. It has this type:

randomR :: (RandomGen g, Random a) :: (a, a) -> g -> (a, g)

This means that it’s kind of like random, but it takes as its first parameter a pair of values that set the lower and upper bounds, and the final value produced will be within those bounds.

ghci> randomR (1,6) (mkStdGen 359353)

(6,1494289578 40692)

ghci> randomR (1,6) (mkStdGen 35935335)

(3,1250031057 40692)

There’s also randomRs, which produces a stream of random values within our defined ranges. Check this out:

ghci> take 10 $ randomRs ('a','z') (mkStdGen 3) :: [Char]

"ndkxbvmomg"

It looks like a super secret password, doesn’t it?

Randomness and I/O


You may be wondering what this section has to do with I/O. We haven’t done anything concerning I/O so far. We’ve always made our random number generator manually by creating it with some arbitrary integer. The problem is that if we do that in our real programs, they will always return the same random numbers, which is no good for us. That’s why System.Random offers the getStdGen I/O action, which has a type of IO StdGen. It asks the system for some initial data and uses it to jump-start the global generator. getStdGen fetches that global random generator when you bind it to something.

Here’s a simple program that generates a random string:

import System.Random

main = do

gen <- getStdGen

putStrLn $ take 20 (randomRs ('a','z') gen)

Now let’s test it:

$ ./random_string

pybphhzzhuepknbykxhe

$ ./random_string

eiqgcxykivpudlsvvjpg

$ ./random_string

nzdceoconysdgcyqjruo

$ ./random_string

bakzhnnuzrkgvesqplrx

But you need to be careful. Just performing getStdGen twice will ask the system for the same global generator twice. Suppose we do this:

import System.Random

main = do

gen <- getStdGen

putStrLn $ take 20 (randomRs ('a','z') gen)

gen2 <- getStdGen

putStr $ take 20 (randomRs ('a','z') gen2)

We will get the same string printed out twice!

The best way to get two different strings is to use the newStdGen action, which splits our current random generator into two generators. It updates the global random generator with one of them and yields the other as its result.

import System.Random

main = do

gen <- getStdGen

putStrLn $ take 20 (randomRs ('a','z') gen)

gen' <- newStdGen

putStr $ take 20 (randomRs ('a','z') gen')

Not only do we get a new random generator when we bind newStdGen to something, but the global one gets updated as well. This means that if we do getStdGen again and bind it to something, we’ll get a generator that’s not the same as gen.

Here’s a little program that will make the user guess which number it’s thinking of:

import System.Random

import Control.Monad(when)

main = do

gen <- getStdGen

askForNumber gen

askForNumber :: StdGen -> IO ()

askForNumber gen = do

let (randNumber, newGen)

Return Main Page Previous Page Next Page

®Online Book Reader