以下程序正确终止:

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

是我一直在寻找的东西。

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中生成一个随机列表,并在需要时将其传递给纯函数是非常方便的,我不明白为什么它不应该出现在标准库中。

最佳答案

我会做更多类似的事情,让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?

10-04 12:36