我刚刚开始学习Haskell
并阅读“让你学习哈斯克尔以获得美好!”。书中有一个例子对我没有意义。它表示以下代码将输出相同的随机字符串两次:
main = do
gen <- getStdGen
putStrLn $ take 20 (randomRs ('a','z') gen)
gen2 <- getStdGen
putStrLn $ take 20 (randomRs ('a','z') gen2)
另一方面,如果同一个程序被调用两次,那么毫无疑问会产生不同的输出。此外,如果我将其与下面的代码进行比较,这会产生不同的s1
和s2
值,那么这似乎不一致:
main = do
s1 <- getLine
s2 <- getLine
putStrLn s1
putStrLn s2
我想知道以上两个例子是如何不同的。
答案 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
,它在给定的程序调用中首次使用时初始化一次。