在运行时,genRandTupe我不断获得相同的随机数,但是当作为参数运行genrandList时gen,我每次都会获得一组新的数字.我该如何解决这个问题?为什么不g = newStdGen生成新的随机数?
import System.Random
import System.IO.Unsafe
type RandTupe = (Int,Int)
genRandTupe :: IO RandTupe
genRandTupe = let [a,b] = genrandList g in return (a,b) where g = newStdGen
genrandList gen = let g = unsafePerformIO gen in take 2 (randomRs (1, 20) g)
gen = newStdGen
Run Code Online (Sandbox Code Playgroud)
genRandTupe是一种不变的应用形式.这意味着它let或where块中的任何局部变量都被记忆.通常非常方便!
在您的情况下,这意味着列表[a,b]仅在整个程序中计算一次.它的计算方式实际上是非法的(不要使用unsafePerformIO!),但它并不重要,因为它只会发生一次.将这个常量元组IO包含return在内并实际上完全是流畅的,你也可以写完
genRandTupe' :: RandTupe
genRandTupe' = let [a,b] = genrandList g in (a,b)
where g = newStdGen
Run Code Online (Sandbox Code Playgroud)
OTOH,当您genrandList gen在多个不同的地方评估(而不是CAF)时,结果不一定会被存储.相反,该功能被重新计算,使用unsafePerformIO到不安全修改全局状态(或者也许不是...编译器实际上是免费的优化送人了,因为,你知道,genRandList是所谓纯函数......),因此产生不同的结果每一次.
当然,正确的做法是远离它unsafePerformIO.实际上根本不需要进行IO genRandList,因为它已经接受了一个随机生成器......只需在传递之前将该生成器从IO绑定:
genRandTupe'' :: IO RandTupe
genRandTupe'' = do
g <- newStdGen
let [a,b] = genrandList g
return (a,b)
randListFrom :: RandomGen g => g -> [Int]
randListFrom g = take 2 (randomRs (1, 20) g)
Run Code Online (Sandbox Code Playgroud)
请注意,因为let [a,b] = ...now现在在一个do块中,它现在在IOmonad中,与CAF闭包分离genRandTupe''.