我想在列表中应用需要RandomGen的函数f。我试着生成一个无限的RandomGen列表,如下所示。 (只是由函数“randoms”生成的随机值是不够的,因为所需的值范围取决于f的输入。)
module Test where
import System.Random (getStdGen, RandomGen, randomR, random)
f :: (RandomGen g, Integral a) => g -> a -> Bool
randomGens :: RandomGen g => g -> [g]
randomGens gen =
let (i, gen') = (random gen) :: (Int, g1)
in gen : (repeatGen gen')
不幸的是,编译器告诉我,它失败了
Test.hs:13:26:
Could not deduce (g1 ~ g)
from the context (RandomGen g)
bound by the type signature for
randomGens :: RandomGen g => g -> (g, g)
at Test.hs:11:14-39
or from (RandomGen g1)
bound by an expression type signature: RandomGen g1 => (Int, g1)
at Test.hs:13:19-55
`g1' is a rigid type variable bound by
an expression type signature: RandomGen g1 => (Int, g1)
at Test.hs:13:19
`g' is a rigid type variable bound by
the type signature for randomGens :: RandomGen g => g -> (g, g)
at Test.hs:11:14
In the first argument of `random', namely `gen'
In the expression: random gen :: RandomGen g => (Int, g)
In a pattern binding:
(i, gen') = random gen :: RandomGen g => (Int, g)
只是在let-binding中跳过类型注释(Int,g1)不起作用。他需要具有“随机”
应用的结果类型答案 0 :(得分:4)
忽略生成无限生成器的无限列表是否真的可行,System.Random
中存在一个名为split
的函数,可用于创建新生成器而不是调用虚拟类型上的random
并抛弃生成的值。使用split
,您可以像这样实施randomGens
:
import Data.List (unfoldr)
import System.Random
randomGens :: RandomGen g => g -> [g]
randomGens = unfoldr (Just . split)
但是,您应该只使用randomRs
,它会在给定范围内生成无限的值流。
答案 1 :(得分:2)
您可以将您想要的功能定义为
randomGens :: (RandomGen g) => g -> [g]
randomGens g = let (g0,g1) = split g in g0 : randomGens g1
以上可能不是应用需要随机性到列表的函数的最佳方法。我可以定义一个辅助函数来做到这一点
mapRandom :: (RandomGen g) => (g -> a -> b) -> g -> [a] -> (g, [b])
mapRandom _ g [] = (g, [])
mapRandom f g (a:as) = let (_,g1) = next g
in f g a : mapRandom f g1 as
然后你可以写
>> g <- newStdGen
>> mapRandom f g [1..5]
([False,False,True,False,True], 1839473859 293842934)
函数mapRandom
看起来非常混乱。那是因为我们不得不搞乱手动更新发电机的细节。幸运的是,你不必那样做!包Control.Monad.Random
为您提供了很好的组合,几乎完全抽象出了生成器的概念。说你目前有
f :: (RandomGen g) => g -> Int -> Bool
f g n = let (x,_) = random g in x < n
我会重写那个
f :: (RandomGen g) => Int -> Rand g Bool
f n = do
x <- getRandom
return (x < n)
并使用mapM
将此功能映射到列表中。您可以使用
>> gen <- newStdGen
>> runRand (mapM f [1..10]) gen
([False,True,True,False,True], 1838593757 1838473759)
其中第一个元素是将随机函数映射到列表上的结果,最后一个元素是生成器的当前值。请注意,在定义f
时,您根本不必担心生成器--Haskell会负责更新生成器并在后台生成新的随机数。
答案 2 :(得分:1)
这里的主要问题 - 编译器无法理解等式g1
和g
(列表总是同态的!)
需要使用ScopedTypeVariables
扩展名,如下所示:
{-# LANGUAGE ScopedTypeVariables #-}
randomGens :: forall g. RandomGen g => g -> [g]
randomGens gen =
let (i, gen') = (random gen) :: RandomGen g => (Int, g)
in gen : (randomGens gen')
我们添加forall g
指向g
的范围上下文。
正如克里斯·泰勒所提到的,这个功能无效,需要计算两次随机数 - 第一次计算新g
,第二次计算新的随机数。
因此,使用MonadRand
在州内保存新的生成器数字要好得多。
<强>已更新强>
对于简单的情况,我们可以使用zipWith
randomsMap :: (RandomGen g, Random a) => g -> (a -> b -> c) -> [b] -> [c]
randomsMap g f xs = zipWith f (randoms g) xs