我尝试在http://jeremykun.com/2013/07/05/reservoir-sampling/之后在 haskell 中实现一个简单的水库采样(请注意,显示的算法可能在语义上不正确)
据此:迭代或惰性水库采样惰性水库采样是不可能的,除非您提前知道人口规模。
即便如此,我还是不明白为什么(从操作上讲)下面的sampleReservoir
内容不适用于无限列表。懒惰到底在哪里被打破了?
import System.Random (randomRIO)
-- equivalent to python's enumerate
enumerate :: (Num i, Enum i) => i -> [e] -> [(i, e)]
enumerate start = zip [start..]
sampleReservoir stream =
foldr
(\(i, e) reservoir -> do
r <- randomRIO (0.0, 1.0) :: IO Double
-- randomRIO gets confused about 0.0 and 1.0
if r < (1.0 / fromIntegral i) then
fmap (e:) reservoir
else
reservoir)
(return [])
(enumerate 1 stream)
挑战和考验是fmap (take 1) $ sampleReservoir [1..]
。
此外,如果水库采样不能惰性,那么什么可以接收惰性列表并生成采样惰性列表?
我认为必须有一种方法可以使上述函数在输出中变得惰性,因为我可以更改它:
if r < (1.0 / fromIntegral i) then
fmap (e:) reservoir
else
至:
if r < (1.0 / fromIntegral i) then
do
print e
fmap (e:) reservoir
这显示了函数在列表上迭代时的结果。使用协程抽象,也许可以代替print e
a yield e
,其余的计算可以作为延续。