有人可以描述以下类型的构造函数和函数如何工作吗?

type Rand a = State StdGen a

getRandom :: (Random a) => Rand a
getRandom = get >>= (\r -> let (a,g) = random r in (put g) >> (return a))

runRand :: Int -> Rand a -> a
runRand n r = evalState r $ mkStdGen n

runRandIO :: Rand a -> IO a
runRandIO r = randomIO >>= (\rnd -> return $ runRand rnd r)

getRandoms :: (Random a) => Int -> Rand [a]
getRandoms n = mapM (\_ -> getRandom) [1..n]

最佳答案

让我们从头开始:

type Rand a = State StdGen a

此行告诉您Rand aState类型的类型同义词,其状态由StdGen给出,其最终值是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返回一个monadic值,因此我们使用mapM映射列表,这会导致状态(即StdGen)通过对getRandom的每次调用正确地进行线程化。

10-06 05:06