在Haskell中通过getStdGen使用全局随机生成器

时间:2014-02-02 07:55:11

标签: haskell functional-programming

我刚刚开始学习Haskell并阅读“让你学习哈斯克尔以获得美好!”。书中有一个例子对我没有意义。它表示以下代码将输出相同的随机字符串两次:

main = do
    gen <- getStdGen
    putStrLn $ take 20 (randomRs ('a','z') gen)
    gen2 <- getStdGen
    putStrLn $ take 20 (randomRs ('a','z') gen2)

另一方面,如果同一个程序被调用两次,那么毫无疑问会产生不同的输出。此外,如果我将其与下面的代码进行比较,这会产生不同的s1s2值,那么这似乎不一致:

main = do
    s1 <- getLine
    s2 <- getLine
    putStrLn s1
    putStrLn s2

我想知道以上两个例子是如何不同的。

2 个答案:

答案 0 :(得分:6)

这只是getStdGen的一个特定属性:与getLine不同,它不是一个“有效”的IO操作,而是每次评估它时只访问相同的默认随机生成器状态。因此它几乎是一个纯粹的函数,但由于它在不同的程序运行之间会有所不同(实际上在同一个运行中,如果你用setStdGen明确修改),它们仍然把它放在{{1 monad。

也许这是一个很好的类比:

IO

答案 1 :(得分:3)

查看source

-- |Gets the global random number generator.
getStdGen :: IO StdGen
getStdGen  = readIORef theStdGen

theStdGen :: IORef StdGen
theStdGen  = unsafePerformIO $ do
   rng <- mkStdRNG 0
   newIORef rng

mkStdRNG :: Integer -> IO StdGen
mkStdRNG o = do
    ct          <- getCPUTime
    (sec, psec) <- getTime
    return (createStdGen (sec * 12345 + psec + ct + o))

所以从实际的角度来看,它只是与getStdGen的实现有关:它读取一个IORef,它在给定的程序调用中首次使用时初始化一次。