我正在尝试修剪随机数列表,以使结果较小的列表中[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
的呼叫之外进行对count
的呼叫。您可以尝试的一件事是在sequence
本身内部对count
进行部分重新实现:
count :: (Num a, Ord a, Monad m) => [m a] -> m (Maybe Int)
count = go 0 0
where go n total [] = pure Nothing
go n total (x:xs) = do
num <- x
let total' = total + num
n' = succ n
if total' >= 1
then pure $ Just n'
else go n' total' xs
我还修改了结果以返回Maybe,因为为空列表返回1(就像您的列表一样),或者返回列表的长度(即使其元素的总和小于1也是错误的。) >
另一个合理的变化是不接受[m a]
,而只接受一个m a
,它可以可靠地多次产生一个值。然后,我们不必担心输入即将用完,因此不需要Maybe:
count :: (Num a, Ord a, Monad m) => m a -> m Int count m = go 0 0 where go n total = do num <- m let total' = total + num n' = succ n if total' >= 1 then pure n' else go n' total'
然后您可以简单地调用
count $ randomRIO (0::Double, 1)
,它将产生所需数量的随机数。
您可以定义一个IO操作来创建无限范围的随机数流,如下所示: