在haskell中生成随机数

0 random haskell

在运行时,genRandTupe我不断获得相同的随机数,但是当作为参数运行genrandListgen,我每次都会获得一组新的数字.我该如何解决这个问题?为什么不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)

lef*_*out 5

genRandTupe是一种不变的应用形式.这意味着它letwhere块中的任何局部变量都被记忆.通常非常方便!

在您的情况下,这意味着列表[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''.

  • 对于危言耸听的语气感到抱歉,但是'unsafePerformIO`是一种害虫.(而且,它实际上根本不属于Haskell,只属于外部函数接口.) (4认同)