, её можно написать короче:
>import Data.Char
>main = do
> contents <- getContents
> putStr $ map toUpper contents
Мы выполняем действие >getContents
и даём имя >contents
строке, которую она прочтёт. Затем проходим функцией >toUpper
по всем символам этой строки и выводим результат на терминал. Имейте в виду: поскольку строки являются списками, а списки ленивы, как и действие >getContents
, программа не будет пытаться прочесть и сохранить в памяти всё содержимое входного потока. Вместо этого она будет читать данные порциями, переводить каждую порцию в верхний регистр и печатать результат.
Давайте проверим:
>$ ./capslocker < haiku.txt
>Я МАЛЕНЬКИЙ ЧАЙНИК
>ОХ УЖ ЭТОТ ОБЕД В САМОЛЁТЕ
>ОН СТОЛЬ МАЛ И НЕВКУСЕН
Работает. А что если мы просто запустим capslocker и будем печатать строки вручную (для выхода из программы нужно нажать Ctrl+D)?
>$ ./capslocker
>хей хо
>ХЕЙ ХО
>идём
>ИДЁМ
Чудесно! Как видите, программа печатает строки в верхнем регистре по мере ввода строк. Когда результат действия >getContents
связывается с идентификатором >сontents
, он представляется в памяти не в виде настоящей строки, но в виде обещания, что рано или поздно он вернёт строку. Также есть обещание применить функцию >toUpper
ко всем символам строки >сontents
. Когда выполняется функция >putStr
, она говорит предыдущему обещанию: «Эй, мне нужна строка в верхнем регистре!». Поскольку никакой строки ещё нет, она говорит идентификатору >сontents
: «Аллё, а не считать ли строку с терминала?». Вот тогда функция >getContents
в самом деле считывает с терминала и передаёт строку коду, который её запрашивал, чтобы сделать что-нибудь осязаемое. Затем этот код применяет функцию >toUpper
к символам строки и отдаёт результат в функцию >putStr
, которая его печатает. После чего функция >putStr
говорит, «Ау, мне нужна следующая строка, шевелись!» – и так продолжается до тех пор, пока не закончатся строки на входе, что мы обозначаем символом конца файла.
Теперь давайте напишем программу, которая будет принимать некоторый вход и печатать только те строки, длина которых меньше 15 символов. Смотрим:
>main = do
> contents <- getContents
> putStr $ shortLinesOnly contents
>shortLinesOnly :: String -> String
>shortLinesOnly = unlines . filter (\line -> length line < 15) . lines
Фрагмент программы, ответственный за ввод-вывод, сделан настолько малым, насколько это вообще возможно. Так как предполагается, что наша программа печатает результат, основываясь на входных данных, её можно реализовать согласно следующей логике: читаем содержимое входного потока, запускаем на этом содержимом некоторую функцию, печатаем результат работы этой функции.