Ещё немного функций, работающих со случайностью
А что если бы мы захотели подкинуть четыре монеты? Или пять? На этот случай есть функция 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"
Неплохо, выглядит как сверхсекретный пароль или что-то в этом духе!