【发布时间】:2020-02-11 12:25:15
【问题描述】:
我正在尝试修剪一个随机数列表,以便生成的较小列表中 [0,1] 中的数字总和累积到小于 1 的值。
这很有趣,因为这些列表前缀长度的平均值是e、somehow。
在获取前缀长度时遇到了一个问题 - 我设法让程序在确定的无限列表或随机列表切片上工作,但程序挂在无限随机列表上。我做错了什么?
import System.Random
-- Count list items that accumulate to 1.
count :: (Num a, Ord a) => [a] -> Int
count xs = 1 + length xs'
where xs'= takeWhile (< 1) $ scanl1 (+) xs
-- Works of infinite list
a = (return (repeat 0.015)) :: IO [Double]
fa = fmap count a
--67
-- Works on trimmed list of randoms
rio = randomRIO (0::Double, 1)
b = sequence $ take 10 (repeat rio)
fb = fmap count b
-- fb is usually 2 to 5
-- Hangs on infinite list of randoms
c = sequence (repeat rio)
fc = fmap count c
-- fc hangs... ;(
【问题讨论】:
-
sequence (repeat rio)本身挂起,因为它必须在产生任何信息之前完成无限多个 IO 操作。 IO 很严格。 -
@luqui 也许更值得注意的是
take 10 . sequence $ repeat rio也挂了。 Evgeny 很可能认为sequence $ repeat rio成功返回了一个惰性列表,并且只是试图强制它实际上挂起,就像repeat 0“挂起”从未完成评估一样。 -
从概念上讲,
IO [Double]是一个指令列表,或一个命令式程序,用于计算[Double]。正如@luqui 所说,sequence (repeat rio)是一个无限程序,它会永远重复调用rio。如果不需要,惰性将使您不必自己计算无限的指令列表,但如果您想要其结果,它不会使您不必执行整个程序。由于程序是无限的,执行永远不会终止,结果也永远不会出现。 -
@malloy - 事实上,根据所有详细的 cmets,我认为一旦 a、b 和 c 的类型签名相同,它们的“惰性级别”应该是相同的,但事实并非如此提供。