Я попытался реализовать простую выборку из резервуара в Haskell, следуя http://jeremykun.com/2013/07/05/reservoir-sampling/ (обратите внимание, что показанный алгоритм, возможно, семантически неверен)
В соответствии с этим: Итеративная или ленивая выборка резервуара ленивая выборка резервуара невозможна, если вы не знаете численность населения заблаговременно.
Тем не менее, я не понимаю, почему (с точки зрения эксплуатации) приведенный ниже 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
To:
if r < (1.0 / fromIntegral i) then
do
print e
fmap (e:) reservoir
Это показывает результаты, когда функция выполняет итерацию по списку. Используя абстракцию сопрограммы, возможно, вместо print e
может быть yield e
, а остальные вычисления можно провести как продолжение.
IO
(передайте генератор с помощьюrandomR
? - person Random Dev   schedule 25.11.2015sampleReservoir
можно сделать достаточно ленивым; однако у него неправильное обозначение. Он не создает элемент, нарисованный случайным образом из входного списка. Действительно, он создает список, а не элемент; но даже список, который он производит, не содержит равномерно распределенных значений. - person Daniel Wagner   schedule 25.11.2015