snippetMinor
(Ab)using scanl to generate a random number stream, is there a better way?
Viewed 0 times
randomstreamnumberscanlwaybettergenerateusingthere
Problem
Here is my code to do it:
The problem I have with this is that I need to pass a list to it e.g., via:
The values of this list end up being ignored and are just there to drive the
I'm wondering if there is a better way to do this (e.g., is there something like
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) gThe 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?Solution
If you just need to generate a stream of random numbers, you can use standard "randomRs" function from System.Random:
Usage:
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:
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:
randomR takes a range for generation and (implicitly here!) a generator and returns a random value and a new value of the generator. Informally:
That's exactly what a typical unfolding function needs to do! So we just wrap this in Just and we are done.
Usage:
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:
Here we just produce random number one by one in a lazy fashion.
Hope this helps!
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) gHere we just produce random number one by one in a lazy fashion.
Hope this helps!
Code Snippets
randomRs :: (RandomGen g, Random a) => (a, a) -> g -> [a]Prelude System.Random> newStdGen >>= print . take 5 . randomRs (1,10)
[2,10,10,3,5]unfoldr :: (b -> Maybe (a, b)) -> b -> [a]generateRandoms2 :: (RandomGen g, Random a) => a -> a -> g -> [a]
generateRandoms2 min max = unfoldr (Just . randomR (min, max))randomR (1, 2) :: (RandomGen g) => g -> (a, g)Context
StackExchange Code Review Q#9149, answer score: 6
Revisions (0)
No revisions yet.