Ещё немного функций, работающих со случайностью

А что если бы мы захотели подкинуть четыре монеты? Или пять? На этот случай есть функция randoms, которая принимает генератор и возвращает бесконечную последовательность значений, основываясь на переданном генераторе.

ghci> take 5 $ randoms (mkStdGen 11) :: [Int]

[–1807975507,545074951,–1015194702,–1622477312,–502893664]

ghci> take 5 $ randoms (mkStdGen 11) :: [Bool]

[True,True,True,True,False]

ghci> take 5 $ randoms (mkStdGen 11) :: [Float]

[7.904789e–2,0.62691015,0.26363158,0.12223756,0.38291094]

Почему функция randoms не возвращает новый генератор вместе со списком? Мы легко могли бы реализовать функцию randoms вот так:

randoms' :: (RandomGen g, Random a) => g –> [a]

randoms' gen = let (value, newGen) = random gen in value:randoms' newGen

Рекурсивное определение. Мы получаем случайное значение и новый генератор из текущего генератора, а затем создаём список, который помещает сгенерированное значение в «голову» списка, а значения, сгенерированные по новому генератору, – в «хвост». Так как теоретически мы можем генерировать бесконечное количество чисел, вернуть новый генератор нельзя.

Мы могли бы создать функцию, которая генерирует конечный поток чисел и новый генератор таким образом:

finiteRandoms :: (RandomGen g, Random a, Num n) => n –> g –> ([a], g)

finiteRandoms 0 gen = ([], gen)

finiteRandoms n gen =

   let (value, newGen) = random gen

       (restOfList, finalGen) = finiteRandoms (n–1) newGen

   in  (value:restOfList, finalGen)

Опять рекурсивное определение. Мы полагаем, что если нам нужно 0 чисел, мы возвращаем пустой список и исходный генератор. Для любого другого количества требуемых случайных значений вначале мы получаем одно случайное число и новый генератор. Это будет «голова» списка. Затем мы говорим, что «хвост» будет состоять из (n – 1) чисел, сгенерированных новым генератором. Далее возвращаем объединённые «голову» и остаток списка и финальный генератор, который мы получили после вычисления (n – 1) случайных чисел.

Ну а если мы захотим получить случайное число в некотором диапазоне? Все случайные числа до сих пор были чрезмерно большими или маленькими. Что если нам нужно подбросить игральную кость?.. Для этих целей используем функцию randomR. Она имеет следующий тип:

randomR :: (RandomGen g, Random a) :: (a, a) –> g –> (a, g)

Это значит, что функция похожа на функцию random, но получает в первом параметре пару значений, определяющих верхнюю и нижнюю границы диапазона, и возвращаемое значение будет в границах этого диапазона.

ghci> randomR (1,6) (mkStdGen 359353)

(6,1494289578 40692)

ghci> randomR (1,6) (mkStdGen 35935335)

(3,1250031057 40692)

Также существует функция randomRs, которая возвращает поток случайных значений в заданном нами диапазоне. Смотрим:

ghci> take 10 $ randomRs ('a','z') (mkStdGen 3) :: [Char]

"ndkxbvmomg"

Неплохо, выглядит как сверхсекретный пароль или что-то в этом духе!