我正在尝试在Haskell中生成随机数的样本
import System.Random
getSample n = take n $ randoms g where
g = newStdGen
但似乎我并没有以正确的方式使用newStdGen
。我错过了什么?
答案 0 :(得分:1)
首先,您可能不想使用newStdGen
。最大的问题是,每次运行程序时都会得到不同的种子,因此没有结果可以重现。在我看来,mkStdGen
是一个更好的选择,因为它需要你给它种子。这意味着每次都会获得相同的(伪)随机数序列。如果你想要一个不同的序列,只需改变种子。
newStdGen
的第二个问题是,由于它不纯,你最终会进入IO monad,这可能有点不方便。
sample :: Int -> IO [Int]
sample n = do
gen <- newStdGen
return $ take n $ randoms gen
你可以使用do-notation来提取&#39;价值然后总结:
main :: IO ()
main = do
xs <- sample 10
s = sum xs
print s
或者你可以&#39; fmap&#39;结果的函数(但请注意,在某些时候你可能需要提取值):
main :: IO ()
main = do
s <- fmap sum $ sample 10
print s
fmap
函数是map
的通用版本。就像map
将函数应用于列表中的值一样,fmap
可以将函数应用于IO
内的值。
这个sample
函数的另一个问题是,如果我们再次调用它,它将以新的种子开始,而不是继续前一个(伪)随机序列。同样,这使得复制结果变得不可能。为了解决这个问题,我们需要传入种子并返回一个新的种子。遗憾的是,randoms
并未为我们返回下一个种子,因此我们必须使用random
从头开始编写此内容。
sample :: Int -> StdGen -> ([Int],StdGen)
sample n seed1 = case n of
0 -> ([],seed1)
k -> let (rs,seed2) = sample (k-1) seed1
(r, seed3) = random seed2
in ((r:rs),seed3)
我们的主要功能现在是
main :: IO ()
main = do
let seed1 = mkStdGen 123456
(xs,seed2) = sample 10 seed1
s = sum xs
(ys,seed3) = sample 10 seed2
t = sum ys
print s
print t
我知道这对于使用随机数似乎是一项非常多的工作,但优点是值得的。我们可以使用单个种子生成所有随机性,以保证结果可以重现。
当然,这是Haskell,我们可以利用Monads来摆脱种子值的所有手动线程。这是一种稍微高级的方法,但值得学习,因为monads在Haskell代码中无处不在。
我们需要这些进口:
import System.Random
import Control.Monad
import Control.Applicative
然后我们将创建一个newtype,它表示将种子转换为值和下一个种子的动作。
newtype Rand a = Rand { runRand :: StdGen -> (a,StdGen) }
我们需要Functor
和Applicative
个实例或GHC会抱怨,但我们可以避免在此示例中实现它们。
instance Functor Rand
instance Applicative Rand
现在为Monad实例。这就是魔术发生的地方。 >>=
函数(称为bind)是我们指定如何通过计算线程化种子值的地方。
instance Monad Rand where
return x = Rand ( \seed -> (x,seed) )
ra >>= f = Rand ( \s1 -> let (a,s2) = runRand ra s1
in runRand (f a) s2 )
newRand :: Rand Int
newRand = Rand ( \seed -> random seed )
现在我们的sample
功能非常简单!我们可以利用来自replicateM
的{{1}}重复给定的操作并将结果累积到列表中。有种子价值的所有有趣的事业都在幕后处理
Control.Monad
如果我们需要多次生成随机值,我们甚至可以留在sample :: Int -> Rand [Int]
sample n = replicateM n newRand
main :: IO ()
main = do
let seed1 = mkStdGen 124567
(xs,seed2) = runRand (sample 10) seed1
s = sum xs
print s
monad中。
Rand
我希望这有帮助!