Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву
Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Ещё немного функций, работающих со случайностьюСодержание книги
Поиск на нашем сайте
А что если бы мы захотели подкинуть четыре монеты? Или пять? На этот случай есть функция 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" Неплохо, выглядит как сверхсекретный пароль или что-то в этом духе!
Случайность и ввод-вывод Вы, должно быть, спрашиваете себя: а какое отношение имеет эта часть главы к системе ввода-вывода? Пока ещё мы не сделали ничего, что имело бы отношение к вводу-выводу! До сих пор мы создавали генераторы случайных чисел вручную, основывая их на некотором целочисленном значении. Проблема в том, что если де- лать так в реальных программах, они всегда будут возвращать оди- наковые последовательности случайных чисел, а это нас не вполне устраивает. Вот почему модуль System.Random содержит действие ввода-вывода getStdGen, тип которого – IO StdGen. При запуске про- грамма запрашивает у системы хороший генератор случайных чи- сел и сохраняет его в так называемом глобальном генераторе. Фун- кция getStdGen передаёт этот глобальный генератор вам, когда вы связываете её с чем-либо.
Вот простая программа, генерирующая случайную строку. import System.Random
main = do gen <– getStdGen
putStrLn $ take 20 (randomRs ('a','z') gen)
Теперь проверим: $./random_string pybphhzzhuepknbykxhe
$./random_string eiqgcxykivpudlsvvjpg
$./random_string nzdceoconysdgcyqjruo
$./random_string bakzhnnuzrkgvesqplrx Но будьте осторожны: если дважды вызвать функцию getStdGen, система два раза вернёт один и тот же генератор. Если сделать так:
import System.Random
main = do gen <– getStdGen putStrLn $ take 20 (randomRs ('a','z') gen) gen2 <– getStdGen
putStr $ take 20 (randomRs ('a','z') gen2) вы получите дважды напечатанную одинаковую строку.
Лучший способ получить две различные строки – использовать действие ввода-вывода newStdGen, которое разбивает текущий гло- бальный генератор на два генератора. Действие замещает глобаль- ный генератор одним из результирующих генераторов и возвраща- ет второй генератор в качестве результата. import System.Random
main = do gen <– getStdGen putStrLn $ take 20 (randomRs ('a','z') gen) gen' <– newStdGen
putStr $ take 20 (randomRs ('a','z') gen') Мы не только получаем новый генератор, когда связываем с чем-либо значение, возвращённое функцией newStdGen, но и заме- няем глобальный генератор; так что если мы воспользуемся функ-
цией getStdGen ещё раз и свяжем его с чем-нибудь, мы получим гене- ратор, отличный от gen.
Вот маленькая программка, которая заставляет пользователя угадывать загаданное число. import System.Random import Control.Monad(when)
main = do gen <- getStdGen askForNumber gen
askForNumber:: StdGen -> IO () askForNumber gen = do let (randNumber, newGen) = randomR (1,10) gen:: (Int, StdGen) putStr "Я задумал число от 1 до 10. Какое? " numberString <- getLine when (not $ null numberString) $ do let number = read numberString if randNumber == number then putStrLn "Правильно!" else putStrLn $ "Извините, но правильный ответ " ++ show randNumber
askForNumber newGen Здесь мы создаём функцию askForNumber, принимающую гене- ратор случайных чисел и возвращающую действие ввода-вывода, которое спросит число у пользователя и сообщит ему, угадал ли он. В этой функции мы сначала генерируем случайное число и новый генератор, основываясь на исходном генераторе; случайное число мы называем randNumber, а новый генератор – newGen. Допустим, что было сгенерировано число 7. Затем мы предлагаем пользователю угадать, какое число мы задумали. Вызываем функцию getLine и свя- зываем её результат с идентификатором numberString. Если пользо- ватель введёт 7, numberString будет равно 7. Далее мы используем функцию when для того, чтобы проверить, не ввёл ли пользователь пустую строку. Если ввёл, выполняется пустое действие ввода-выво- да return(), которое закончит выполнение программы. Если поль- зователь ввёл не пустую строку, выполняется действие, состоящее из блока do. Мы вызываем функцию read со значением numberString в качестве параметра, чтобы преобразовать его в число; образец number становится равным 7.
ПРИМЕЧАНИЕ. Наминуточку!.. Еслипользовательвведёт что-ни- будь, чего функция read не сможет прочесть (например, "хa-хa"), наша программа «упадёт» с ужасным сообщением об ошибке. Если вы не хотите, чтобы программа «падала» на некорректном вводе, используйте функцию reads: она возвращает пустой спи- сок, если у функции не получилось считать строку. Если чтение прошло удачно, функция вернёт список из одного элемента, со- держащий пару, один компонент которой содержит желаемый элемент; второй компонент хранит остаток строки после считы- вания первого. Мы проверяем, равняется ли number случайно сгенерирован- ному числу, и выдаём пользователю соответствующее сообщение. Затем рекурсивно вызываем нашу функцию askForNumber, но на сей
Функция main состоит всего лишь из получения генератора случайных чисел от системы и вызова функции askForNumber с этим генератором для того, чтобы получить первое дейс- твие.
Посмотрим, как работает наша программа! $./guess_the_number Я задумал число от 1 до 10. Какое? 4 Извините, но правильный ответ 3 Я задумал число от 1 до 10. Какое? 10 Правильно! Я задумал число от 1 до 10. Какое? 2 Извините, но правильный ответ 4 Я задумал число от 1 до 10. Какое? 5
Извините, но правильный ответ 10 Я задумал число от 1 до 10. Какое? Можно написать эту же программу по-другому:
import System.Random import Control.Monad (when)
main = do gen <- getStdGen let (randNumber, _) = randomR (1,10) gen:: (Int, StdGen) putStr "Я задумал число от 1 до 10. Какое? " numberString <- getLine when (not $ null numberString) $ do let number = read numberString if randNumber == number then putStrLn "Правильно!" else putStrLn $ "Извините, но правильный ответ " ++ show randNumber
newStdGen main Эта версия очень похожа на предыдущую, но вместо создания функции, которая принимает генератор и вызывает сама себя рекурсивно с вновь полученным генератором, мы производим все действия внутри функции main. После того как пользователь полу- чит ответ, угадал ли он число, мы обновим глобальный генератор и снова вызовем функцию main. Оба подхода хороши, но мне больше нравится первый способ, так как он предусматривает меньше дейс- твий в функции main и даёт нам функцию, которую мы можем легко использовать повторно.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Последнее изменение этой страницы: 2017-02-17; просмотров: 247; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 216.73.217.21 (0.011 с.) |