Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь 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; просмотров: 177; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.116.24.148 (0.006 с.) |