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



Функция >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 и даёт нам функцию, которую мы можем легко использовать повторно.

Bytestring: тот же String, но быстрее

Список – полезная и удобная структура данных. Мы использовали списки почти что везде. Существует очень много функций, работающих со списками, и ленивость языка Haskell позволяет нам заменить циклы типа >for и >while из других языков программирования на фильтрацию и отображение списков, потому что вычисление произойдёт только тогда, когда оно действительно понадобится. Вот почему такие вещи, как бесконечные списки (и даже бесконечные списки бесконечных списков!) для нас не проблема. По той же причине списки могут быть использованы в качестве потоков, читаем ли мы со стандартного ввода или из файла. Мы можем открыть файл и считать его как строку, но на самом деле обращение к файлу будет происходить только по мере необходимости.

Тем не менее обработка файлов как строк имеет один недостаток: она может оказаться медленной. Как вы знаете, тип >String – это просто синоним для типа >[Char]. У символов нет фиксированного размера, так как для представления, скажем, символа в кодировке Unicode может потребоваться несколько байтов. Более того, список – ленивая структура. Если у вас есть, например, список