Элементарное решение:
В качестве альтернативы ответу @ danidiaz можно решить проблему, не прибегая к дополнительным библиотекам, таким как Streaming
, при условии, что роль ввода-вывода может быть минимизирована.
Большая часть необходимого кода может быть написана в терминах класса MonadRandom , одним из которых является IO. Нет необходимости использовать IO для генерации псевдослучайных чисел.
Требуемая итерационная функция может быть написана так в do нотации :
import System.Random
import Control.Monad.Random.Lazy
iterateM1 :: MonadRandom mr => (a -> mr a) -> a -> mr [a]
iterateM1 fn x0 =
do
y <- fn x0
ys <- iterateM1 fn y
return (x0:ys)
К сожалению, текст вопроса не определяет точно, что такое объект Frame или что делает степпинг-функция next
; поэтому я должен как-то заполнить пробелы. Также имя next
определено в задействованных библиотеках, поэтому мне придется использовать nextFrame
вместо просто next
.
Давайте предположим, что объект Frame - это просто точка в 3-мерном пространстве. и что на каждом случайном шаге одно и только одно из трех измерений выбирается случайным образом, а соответствующая координата увеличивается на величину либо +1, либо -1 с равными вероятностями. Это дает следующий код:
data Frame = Frame Int Int Int deriving Show
nextFrame :: MonadRandom mr => Frame -> mr Frame
nextFrame (Frame x y z) =
do
-- 3 dimensions times 2 possible steps: 1 & -1, hence 6 possibilities
n <- getRandomR (0::Int, 5::Int)
let fr = case n of
0 -> Frame (x-1) y z
1 -> Frame (x+1) y z
2 -> Frame x (y-1) z
3 -> Frame x (y+1) z
4 -> Frame x y (z-1)
5 -> Frame x y (z+1)
_ -> Frame x y z
return fr
На этом этапе нетрудно написать код, который создает неограниченный список объектов Frame, представляющих историю моделирования. Создание этого списка не приводит к тому, что код будет l oop навсегда, и обычная функция take
может использоваться для выбора первых нескольких элементов такого списка.
Объединение всего кода:
import System.Random
import Control.Monad.Random.Lazy
iterateM1 :: MonadRandom mr => (a -> mr a) -> a -> mr [a]
iterateM1 fn x0 =
do
y <- fn x0
ys <- iterateM1 fn y
return (x0:ys)
data Frame = Frame Int Int Int deriving Show
nextFrame :: MonadRandom mr => Frame -> mr Frame
nextFrame (Frame x y z) =
do
-- 3 dimensions times 2 possible steps: 1 & -1, hence 6 possibilities
n <- getRandomR (0::Int, 5::Int)
let fr = case n of
0 -> Frame (x-1) y z
1 -> Frame (x+1) y z
2 -> Frame x (y-1) z
3 -> Frame x (y+1) z
4 -> Frame x y (z-1)
5 -> Frame x y (z+1)
_ -> Frame x y z
return fr
runSimulation :: MonadRandom mr => Int -> Int -> Int -> mr [Frame]
runSimulation x y z = let fr0 = Frame x y z in iterateM1 nextFrame fr0
main = do
rng0 <- getStdGen -- PRNG hosted in IO monad
-- Could use mkStdGen or MkTFGen instead
let
sim = runSimulation 0 0 0
allFrames = evalRand sim rng0 -- unlimited list of frames !
frameCount = 10
frames = take frameCount allFrames
mapM_ (putStrLn . show) frames
Выполнение программы:
$ ./frame
Frame 0 0 0
Frame 0 1 0
Frame 0 0 0
Frame 0 (-1) 0
Frame 1 (-1) 0
Frame 1 (-2) 0
Frame 1 (-1) 0
Frame 1 (-1) 1
Frame 1 0 1
Frame 2 0 1
$
Для больших значений frameCount
время выполнения является квазилинейной функцией frameCount
, как и ожидалось.
Подробнее о монади c действия для генерации случайных чисел здесь .