以下程序正确终止:
import System.Random
randomList = mapM (\_->getStdRandom (randomR (0, 50000::Int))) [0..5000]
main = do
randomInts <- randomList
print $ take 5 randomInts
运行:
$ runhaskell test.hs
[26156,7258,29057,40002,26339]
但是,用无限列表提供它,程序永远不会终止,并且在编译时,最终会产生堆栈溢出错误!
import System.Random
randomList = mapM (\_->getStdRandom (randomR (0, 50000::Int))) [0..]
main = do
randomInts <- randomList
print $ take 5 randomInts
运行,
$ ./test
Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize -RTS' to increase it.
我希望每次从列表中选择一个项目时,程序会懒惰地评估getStdRandom
,这样做5次之后就完成了。为什么要试图评估整个清单?
感谢。
有没有更好的方法来获得无限的随机数列表?我想将此列表传递给纯函数。
编辑:更多阅读揭示了该功能
randomList r = do g <- getStdGen
return $ randomRs r g
是我正在寻找的。 p>
EDIT2:在看完camccann的回答之后,我意识到getStdGen
每次通话都会获得新的种子。相反,最好将此功能用作简单的一次性随机列表生成器:
import System.Random
randomList :: Random a => a -> a -> IO [a]
randomList r g = do s <- newStdGen
return $ randomRs (r,g) s
main = do r <- randomList 0 (50::Int)
print $ take 5 r
但我仍然不明白为什么我的mapM
电话没有终止。显然与随机数无关,但可能与mapM
有关。
例如,我发现以下内容也没有终止:
randomList = mapM (\_->return 0) [0..]
main = do
randomInts <- randomList
print $ take 50000 randomInts
是什么给出的?顺便说一句,恕我直言,上述randomInts
函数应该在System.Random
。能够简单在IO monad中生成随机列表并在需要时将其传递给纯函数非常方便,我不明白为什么这不应该在标准库中。< / p>
答案 0 :(得分:12)
一般的随机数并不严格,但 monadic binding - 这里的问题是mapM
必须对整个列表进行排序。考虑其类型签名(a -> m b) -> [a] -> m [b]
;这意味着,它所做的是先将map
类型[a]
的列表放入类型[m b]
的列表中,然后列出sequence
以获得类型{{m [b]
的结果1}}。因此,当您将mapM
,例如的结果绑定到<-
的右侧时,这意味着“将此函数映射到列表中,然后执行每个monadic动作,并将结果合并回一个列表“。如果列表是无限的,这当然不会终止。
如果您只是想要一个随机数流,则需要生成列表而不使用每个数字的monad。我不完全确定你为什么使用你的设计,但基本的想法是:给定种子值,使用伪随机数生成器生成一对1)随机数2)新种子,然后重复新种子。任何给定的种子当然每次都提供相同的序列。因此,您可以使用函数getStdGen
,它将在IO
monad中提供新种子;然后,您可以使用该种子在完全纯粹的代码中创建无限序列。
事实上,System.Random
提供的功能正是为了这个目的,randoms
或randomRs
而不是random
和randomR
。
如果由于某种原因你想自己做,你想要的基本上是展开。来自unfoldr
的函数Data.List
具有类型签名(b -> Maybe (a, b)) -> b -> [a]
,这是相当不言自明的:给定类型b
的值,它应用函数来获取键入a
以及类型为b
的新生成器值,或Nothing
以指示序列的结束。
您想要一个无限列表,因此永远不需要返回Nothing
。因此,将randomR
部分应用到所需范围并使用Just
进行组合会产生以下结果:
Just . randomR (0, 50000::Int) :: (RandomGen a) => a -> Maybe (Int, a)
将其提供到unfoldr
可以得到:
unfoldr (Just . randomR (0, 50000::Int)) :: (RandomGen a) => a -> [Int]
...完全按照它声明的那样:给定一个RandomGen
的实例,它将产生一个无限(和 lazy )从该种子生成的随机数列表。
答案 1 :(得分:5)
我会做更像这样的事情,让randomRs用初始的RandomGen来完成工作:
#! /usr/bin/env runhaskell
import Control.Monad
import System.Random
randomList :: RandomGen g => g -> [Int]
randomList = randomRs (0, 50000)
main :: IO ()
main = do
randomInts <- liftM randomList newStdGen
print $ take 5 randomInts
至于懒惰,这里发生的事情是mapM
是(sequence . map)
其类型为:mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
它正在映射函数,给出[m b]
,然后需要执行所有这些操作来生成m [b]
。这是永远无法通过无限列表的序列。
在先前问题的答案中更好地解释了这一点:Is Haskell's mapM not lazy?