在haskell中生成随机数

时间:2016-02-18 15:37:20

标签: haskell random

运行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

1 个答案:

答案 0 :(得分:5)

genRandTupeconstant applicative form。这意味着其letwhere块中的任何局部变量都会被记忆。通常非常方便!

在您的情况下,这意味着列表[a,b]仅在整个程序中计算一次。它的计算方式实际上是非法的(不要使用unsafePerformIO !),但它并不重要,因为它只会发生一次。将此常量元组包含在IO return genRandTupe' :: RandTupe genRandTupe' = let [a,b] = genrandList g in (a,b) where g = newStdGen 中实际上完全是流畅的,您可以写完

genrandList gen

OTOH,当您在多个不同的地方评估unsafePerformIO(不是CAF)时,结果不一定会被存储。相反,该函数是重新计算的,使用genRandList不安全地修改全局状态(或者可能不是......编译器实际上可以自由地优化它,因为,你知道,{{ 1}}被认为是一个纯函数...)因此每次产生不同的结果。

当然,正确的做法是远离unsafePerformIO。实际上根本不需要在genRandList中执行IO,因为它已经接受了一个随机生成器......只是在传递它之前将该生成器绑定到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)

请注意,因为let [a,b] = ...现在位于do块中,所以它现在位于IO monad中,与genRandTupe''的CAF闭包分离。