Изучай Haskell во имя добра! (Липовача) - страница 145

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

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