几天来我一直在搞乱Haskell,偶然发现了一个问题。
我需要一个返回随机整数列表的方法( Rand [[Int]] )。
所以,我定义了一个类型:type Rand a = StdGen -> (a, StdGen)
. 我能够以某种方式产生Rand IO Integer
和Rand [IO Integer]
((returnR lst) :: StdGen -> ([IO Integer], StdGen)
)。任何提示如何生产Rand [[Int]]
?
如何避免IO
取决于首先引入它的原因。虽然伪随机数生成器本质上是面向状态的,但没有理由IO
需要涉及。
我将猜测并说您正在使用newStdGen
或getStdGen
获取您的初始 PRNG。如果真是这样,那就没有办法彻底逃脱了IO
。您可以改为直接使用 为 PRNG 播种,请mkStdGen
记住,相同的种子将产生相同的“随机”数字序列。
更有可能的是,您想要做的是在内部获取 PRNG IO
,然后将其作为参数传递给纯函数。当然,整个事情最终仍然会被包裹起来IO
,但是中间计算不需要它。这里有一个简单的例子来告诉你这个想法:
import System.Random
type Rand a = StdGen -> (a, StdGen)
getPRNG = do
rng <- newStdGen
let x = usePRNG rng
print x
usePRNG :: StdGen -> [[Int]]
usePRNG rng = let (x, rng') = randomInts 5 rng
(y, _) = randomInts 10 rng'
in [x, y]
randomInts :: Int -> Rand [Int]
randomInts 0 rng = ([], rng)
randomInts n rng = let (x, rng') = next rng
(xs, rng'') = randomInts (n - 1) rng'
in (x:xs, rng'')
您可能会注意到,由于不断地来回传递当前值,使用 PRNG 的代码变得非常难看。它也可能容易出错,因为很容易意外重用旧值。如上所述,使用相同的 PRNG 值会给出相同的数字序列,这通常不是您想要的。这两个问题都是一个完美的例子,说明使用 monad 是有意义的State
——这在这里离题了,但你可能想接下来研究它。
您正在在Hackage上重新创建 MonadRandom。如果这不仅仅是一个试验,看看你是否能做到,你可能想改用那个库。
如果你想获得一个无限的 s 列表,Integer
你会遇到问题,因为你永远不会得到一个StdGen
值。您在这里要做的是split
第StdGen
一个,再次传递一半并“用完”另一半以生成无限的整数列表。像这样的东西:
infiniteRandomInts :: Rand [Int]
infiniteRandomInts g = (ints, g2) where
(g1,g2) = split g
ints = randoms g1
但是,如果您然后重复这种方法以获得 s 的无限矩阵Integer
(您似乎想要,通过使用Rand [[Int]]
),您可能会遇到统计性质的问题:我不知道如何StdGen
处理重复split
ting。也许另一种实现RandomGen
可能会更好,或者您可以尝试使用某种对角线步行将 a[Int]
变成 a [[Int]]
。
使用一元符号,你应该能够写出类似的东西
randomList gen = do
randomLength <- yourRandomInteger
loop gen (randomLength + 1)
where
loop gen 1 = gen
loop gen n = do { x <- gen; xs <- loop gen (n - 1); return (x:xs) }
有了这个
randomInts :: Rand [Int]
randomInts = randomList yourRandomInteger
randomLists :: Rand [[Int]]
randomLists = randomList randomInts
关于单子计算本身,看看这篇文章。请注意,随机生成器是纯粹的,你不应该IO
仅仅为了这个目的而需要任何生成器。