Как избежать IO
зависит от того, почему он вводится в первую очередь. Хотя генераторы псевдослучайных чисел по своей природе ориентированы на состояние, нет причин, по которым необходимо задействовать IO
.
Я собираюсь угадать и сказать, что вы используете newStdGen
или getStdGen
, чтобы получить ваш первоначальный PRNG. Если это так, то нет возможности полностью избежать IO
. Вы могли бы вместо этого перенести PRNG на mkStdGen
, имея в виду, что одно и то же семя приведет к той же последовательности случайных чисел.
Скорее всего, что вы хотите сделать, это получить PRNG внутри IO
, а затем передать это как аргумент чистой функции. Разумеется, все будет по-прежнему завернуто в IO
, но промежуточные вычисления ему не понадобятся. Вот краткий пример, чтобы дать вам идею:
import System.Random
type Rand a = StdGen -> (a, StdGen)
getPRNG = do
rng <- newStdGen
let x = usePRNG rng
print x
usePRNG :: StdGen -> [[Int]]
usePRNG rng = let (x, rng') = randomInts 5 rng
(y, _) = randomInts 10 rng'
in [x, y]
randomInts :: Int -> Rand [Int]
randomInts 0 rng = ([], rng)
randomInts n rng = let (x, rng') = next rng
(xs, rng'') = randomInts (n - 1) rng'
in (x:xs, rng'')
Вы можете заметить, что код, использующий ПГСЧ получает довольно некрасиво из-за прохождение текущего значения назад и вперед постоянно. Это также потенциально подвержено ошибкам, так как было бы легко случайно повторно использовать старое значение. Как упоминалось выше, использование того же значения PRNG даст ту же последовательность чисел, которая обычно не та, что вы хотите. Обе проблемы являются прекрасным примером того, где имеет смысл использовать монаду State
, которая здесь выходит из темы, но вы можете захотеть взглянуть на нее дальше.
момент, когда вы просите образец, вы попадаете в монаду IO –