How can I generate random array using Data.Array
?
I have function, that gives me a random number:
randomNumber :: (Random r) => r -> r -> IO r
randomNumber a b = getStdRandom (randomR (a,b))
And then I'm trying to use function from Data.Array
to generate list
assocs $ array (1,100) [(i,i) | i <- (randomNumber 1 10)]
I know, that the type of randomNumber
is IO
, is there any method to convert IO Int -> Int
? Or I need to use other methods to get random list? Should I do these functions with bind
operator in do
block?
You should use functions to generate random list from a generator, that are pure, and then use getStdRandom
:
randomList :: Int -> Int -> IO [Int]
randomList a b = getStdGen >>= return . randomRs (a,b)
The function that you need is randomRs. Then you set the generator to stdGen
with getStdGen
and you have your generator.
The function randomList
first gets the standard generator with getStdGen
and then passes it to randomRs
. Note that randomList
can be rewritten without hiding the generator parameter:
randomList a b = getStdGen >>= \gen -> return (randomRs (a,b) gen)
I'll continue as long as @mariop's answer tells about lists, not arrays, and try to explain a nature of Haskell randomness a little more.
(if you're not interested in theory, skip to the (tl;dr) section)
At first, let's choose a signature for our presumed function. I'll consider that you need a plain array (as in C or Java), indexed by consecutive natural numbers (if my guessing is wrong, please correct).
As you may know, all Haskell functions are pure and deterministic, so each function must always return same results for the same arguments. That's not the case of random, of course. The solution is to use pseudorandom values, where we have a generator. A generator itself is a complicated function that have an internal hidden state called seed, and can produce a value and a new generator with a new seed (which then can produce a new (value, generator) pair and so on). A good generator is built in way that the next value could not be predicted from the previous value (when the we don't know the seed), so they appear as random to the user.
In fact, all major random implementations in most languages are pseudorandom because the "true" random (which gets its values from the sources of "natural" randomness, called entropy, such as CPU temperature) is computatively expensive.
All so-called random functions in Haskell are dealing with the generator in some way. If you look at methods from the Random
typeclass, they are divided in two groups:
Those which get the random generator explicitly: randomR
, random
and so on. You can build an explicit generator, initialized with a seed, with mkStdRandom (or even make your own).
Those which work in the IO monad: randomIO
, randomRIO
. They actually get the generator from the environment "carried" within the IO monad (with getStdRandom
), and give it to function from the first group.
So, we can organize our function in either way:
--Arguments are generator, array size, min and max bound
generateArray :: (RangomGen g, Random r) => g -> Int -> r -> r -> Array Int r
or
--Arguments are array size, min and max bound
generateArray :: Random r => Int -> r -> r -> IO (Array Int r)
Because Haskell is lazy, there is no need to make a fixed set of random values — we can make an infinite one and take as many values as we need. The infinite list of random bounded values is produced by the randomRs
function.
(tl;dr)
If the array is consecutive, the easier way is to build it from a plain values list rather than assocs (key, value) list:
generateArray gen size min max =
listArray (0, size - 1) $ randomRs (min, max) gen
or
generateArray size min max =
getStdGen >>= return . listArray (0, size - 1) . randomRs (min, max)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With