让我们从头开始:
type Rand a = State StdGen a
这一行告诉您这Rand a
是一个类型的类型同义词State
,其状态由 给出StdGen
,其最终值为 type a
。这将用于在每个随机数请求之间存储随机数生成器的状态。
的代码getRandom
可以转换为 do 表示法:
getRandom :: (Random a) => Rand a
getRandom = do
r <- get -- get the current state of the generator
let (a,g) = random r in do -- call the function random :: StdGen -> (a, StdGen)
put g -- store the new state of the generator
return a -- return the random number that was generated
该runRand
函数采用初始种子n
和r
类型值Rand a
(请记住,它只是 的同义词State StdGen a
)。它创建一个新的生成器mkStdGen n
并将其提供给evalState r
. 该函数evalState
只计算State s a
类型的返回值,忽略状态。
同样,我们可以转换runRandIO
为do
符号:
runRandIO :: Rand a -> IO a
runRandIO r = do
rnd <- randomIO -- generate a new random number using randomIO
return (runRand rnd r) -- use that number as the initial seed for runRand
最后,getRandoms
取一个数字n
,表示您要生成的随机值的数量。它构建一个列表[1..n]
并应用于getRandom
该列表。请注意,[1..n]
未使用实际值(您可以知道,因为 lambda 函数以 开头\_ -> ...
)。该列表只是为了包含正确数量的元素。由于getRandom
返回一个单子值,我们使用mapM
对列表进行映射,这会导致状态(即StdGen
)通过对 的每个调用正确线程化getRandom
。