[Haskell-cafe] Help with IO and randomR
Tillmann Rendel
rendel at rbg.informatik.tu-darmstadt.de
Mon Jul 16 08:50:22 EDT 2007
Niko Korhonen wrote:
> So, in short, how do I do this without getting into an infinite loop:
>
> tpdfs :: (Int, Int) -> IO [Int]
> tpdfs (low, high) = do
> first <- getStdRandom (randomR (low, high))
> second <- getStdRandom (randomR (low, high))
> let r = (first + second) `div` 2
> rest <- tpdfs (low, high) -- (A)
> return (r : rest) -- (B)
(A) will be executed before (B) because of the IO monad. But you want r
to be returned before rest is computed. I would split tpdfs in two
functions: a pure function converting a infinite list of random numbers
to another infinite list of random numbers, and an IO-function creating
the original infinite list of random numbers:
tpdfs' :: [Int] -> [Int]
tpdfs' (x:y:rest) = (x + y) `div` 2 : tpdfs' rest
tpdfs :: (Int, Int) -> IO [Int]
tpdfs range = do
gen <- newStdGen
return (tpdfs' (randomRs range gen))
The only IO action (newStdGen) is executed when tpdfs is called, but the
infinite result list is lazily created when needed. This is possible
because newStdGen uses split to create a new source of randomness
exclusively for the tpdfs' function wich is not accessed anywhere else.
tpdfs can be written more concisely as one of these
tpdfs range = liftM (tpdfs' . randomRs range) newStdGen
tpdfs range = return (tpdfs' . randomRs range) `ap` newStdGen
tpdfs range = newStdGen >>= (randomRs range >>> tpdfs' >>> return)
using either Control.Monad or Control.Arrow.
I'm not sure your aproach is numerically correct. Let's assume range =
(0, 1). The resulting number could be
(0 + 0) `div` 2 = 0
(0 + 1) `div` 2 = 0
(1 + 0) `div` 2 = 0
(1 + 1) `div` 2 = 1
with equal probability. Is this what you want?
Tillmann Rendel
More information about the Haskell-Cafe
mailing list