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

.

Посмотрим, как перенаправление ввода работает с программой на языке Haskell. Для начала создадим текстовый файл, содержащий небольшое хайку, и сохраним его под именем haiku.txt:

>Я маленький чайник

>Ох уж этот обед в самолёте

>Он столь мал и невкусен

Ну да, хайку, прямо скажем, не шедевр – и что? Если кто в курсе, где найти хороший учебник по хайку, дайте знать.

Теперь напишем маленькую программу, которая непрерывно читает строку ввода и выводит её в верхнем регистре:

>import Control.Monad

>import Data.Char


>main = forever $ do

>   l <- getLine

>   putStrLn $ map toUpper l

Сохраните эту программу в файле capslocker.hs и скомпилируйте её.

Вместо того чтобы вводить строки с клавиатуры, мы перенаправим на вход программы содержимое файла haiku.txt. Чтобы сделать это, нужно добавить символ >< после имени программы и затем указать имя файла, в котором хранятся исходные данные. Посмотрите:

>$ ghc capslocker

>[1 of 1] Compiling Main  ( capslocker.hs, capslocker.o )

>Linking capslocker ...

>$ ./capslocker < haiku.txt

>Я МАЛЕНЬКИЙ ЧАЙНИК

>ОХ УЖ ЭТОТ ОБЕД В САМОЛЁТЕ

>ОН СТОЛЬ МАЛ И НЕВКУСЕН

>capslocker: : hGetLine: end of file

То, что мы проделали, практически эквивалентно запуску программы >capslocker, вводу нашего хайку с клавиатуры и передаче символа конца файла (обычно это делается нажатием клавиш Ctrl+D). С тем же успехом можно было бы запустить >capslocker и сказать: «Погоди, не читай ничего с клавиатуры, возьми содержимое этого файла!».

Получение строк из входного потока

Давайте посмотрим на действие ввода-вывода >getContents, упрощающее обработку входного потока за счёт того, что оно позволяет рассматривать весь поток как обычную строку. Действие >getContents читает всё содержимое стандартного потока ввода вплоть до обнаружения символа конца файла. Его тип: >getContents :: IO String. Самое приятное в этом действии то, что ввод-вывод в его исполнении является ленивым. Это означает, что выполнение >foo <- getContents не приводит к загрузке в память всего содержимого потока и связыванию его с именем >foo. Нет, действие >getContents для этого слишком лениво. Оно скажет: «Да, да, я прочту входные данные с терминала как-нибудь потом, когда это действительно понадобится!».

В примере capslocker.hs для чтения ввода строка за строкой и печати их в верхнем регистре использовалась функция >forever. Если мы перейдём на >getContents, то она возьмёт на себя все заботы о деталях ввода-вывода – о том, когда и какую часть входных данных нужно прочитать. Поскольку наша программа просто берёт входные данные, преобразует их и выводит результат, пользуясь