您可以定义一个函数,其类型与您想要的类型相匹配,尽管更普遍。
import System.Random
randoms' :: (RandomGen g, Random a) => g -> Int -> ([a], g)
randoms' g n =
let (g1, g2) = split g
in (take n $ randoms g1, g2)
尽管它使用split
split :: g -> (g, g)
该split
操作允许获得两个不同的随机数生成器。这在函数式程序中非常有用(例如,在将随机数生成器向下传递给递归调用时),但在统计上稳健的实现方面所做的工作却很少split
……</p>
它仍然没有做你想做的事。(我Bool
在下面的示例中使用以便更容易进行视觉比较。)
ghci> g <- getStdGen
ghci> randoms' g 5 :: ([Bool], StdGen)
([False,False,False,True,False],1648254783 2147483398)
ghci> randoms' g 5 :: ([Bool], StdGen)
([False,False,False,True,False],1648254783 2147483398)
请注意,随机数组是相同的。
尽管该函数很麻烦地拆分了生成器,但我们还是立即将其丢弃。相反,g2
通过将其线程化到后续调用来使用,如
ghci> let (a1,g2) = randoms' g 5 :: ([Bool], StdGen)
ghci> let (a2,_) = randoms' g2 5 :: ([Bool], StdGen)
ghci> (a1,a2)
([False,False,False,True,False],[True,True,True,False,True]
如果您的代码在IO
monad 中运行,则可以setStdGen
在末尾使用替换全局随机数生成器,如
myAction :: Int -> IO ([Float],[Float])
myAction n = do
g <- getStdGen
let (f1,g2) = randoms' g n
let (f2,g3) = randoms' g2 n
setStdGen g3
return (f1, f2)
线程状态是尴尬且容易出错的。考虑使用State
或者ST
如果你有很多重复的样板。