2
\$\begingroup\$

Here is my code to do it:

import Random (randomR, Random, StdGen)

generateRandoms :: (Random a, Num a) => StdGen -> a -> a -> [b] -> [a]
generateRandoms gen min max = map fst . tail . scanl f (0, gen)
    where f (x, g) _ =  randomR (min, max) g

The problem I have with this is that I need to pass a list to it e.g., via:

generateRandoms initialgen min max [1..10]

The values of this list end up being ignored and are just there to drive the scanl function.

I'm wondering if there is a better way to do this (e.g., is there something like scanl that doesn't need a list to make it generate values?

\$\endgroup\$
5
\$\begingroup\$

If you just need to generate a stream of random numbers, you can use standard "randomRs" function from System.Random:

randomRs :: (RandomGen g, Random a) => (a, a) -> g -> [a]

Usage:

Prelude System.Random> newStdGen >>= print . take 5 . randomRs (1,10)
[2,10,10,3,5]

If you want to implement it by yourself, it can be done with unfoldr function instead of scanl. unfoldr allows to (surprise!) unfold a list from the seed value. Exactly what you need! Let's examine its type:

unfoldr :: (b -> Maybe (a, b)) -> b -> [a]

First argument is unfolding function and the second argument is the seed value. Unfolding function takes current seed value and produces Nothing or Just (a, b). Nothing means we should stop generating the list and Just (a, b) produces a new value of type "a" which is added to the genererated list and a new value of seed.

Since we want to produce a stream of random values, we never return Nothing from our unfolding function. Here is the code:

generateRandoms2 :: (RandomGen g, Random a) => a -> a -> g -> [a]
generateRandoms2 min max = unfoldr (Just . randomR (min, max))

randomR takes a range for generation and (implicitly here!) a generator and returns a random value and a new value of the generator. Informally:

randomR (1, 2) :: (RandomGen g) => g -> (a, g)

That's exactly what a typical unfolding function needs to do! So we just wrap this in Just and we are done.

Usage:

*Random Data.List> newStdGen >>= print . take 10 . generateRandoms2 1 10
[10,6,2,7,3,7,5,9,4,5]

In fact, we don't use the full power of unfoldr here (since we do not need to distinguish whether we should finish the generation or not), so we can even write a simpler recursive function:

generateRandoms3 :: (RandomGen g, Random a) => a -> a -> g -> [a]
generateRandoms3 min max g = x : generateRandoms3 min max g'
   where (x, g') = randomR (min, max) g

Here we just produce random number one by one in a lazy fashion.

Hope this helps!

\$\endgroup\$
  • \$\begingroup\$ Thanks for this very detailed answer. Learning about the built in functions and the different options on how to do it from scratch (if I wanted to) is exactly what I was after when I asked the question. \$\endgroup\$ – Thorsten Lorenz Feb 19 '12 at 23:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.