ТОП 10:

Некоторые полезные функции для ввода-вывода



В стандартной библиотеке языка Haskell имеется масса полезных функций и действий ввода-вывода. Давайте рассмотрим некоторые из них и увидим, как ими пользоваться.

 

Функция putStr

Функция putStr похожа на функцию putStrLn – она принимает стро- ку как параметр и возвращает действие ввода-вывода, которое печа- тает строку на терминале. Единственное отличие: функция putStr не выполняет перевод на новую строку после печати, как это делает putStrLn.


 

main = do

 
 

putStr "Привет, " putStr "я " putStrLn "Энди!"

Если мы скомпилируем эту программу, то при запуске получим:

 
 

Привет, я Энди!

 

Функция putChar

 
 

Функция putChar принимает символ и возвращает действие ввода- вывода, которое напечатает его на терминале.

main = do

 
 

putChar 'А' putChar 'Б' putChar 'В'

Функция putStr определена рекурсивно с помощью функции putChar. Базовый случай для функции putStr – это пустая строка. Если печатаемая строка пуста, функция возвращает пустое дейс- твие ввода-вывода, то есть return (). Если строка не пуста, функция выводит на терминал первый символ этой строки, вызывая функ- цию putChar, а затем выводит остальные символы, снова рекурсив- но вызывая саму себя.

putStr :: String –> IO () putStr [] = return () putStr (x:xs) = do

 
 

putChar x putStr xs

Как вы заметили, мы можем использовать рекурсию в системе ввода-вывода подобно тому, как делаем это в чистом коде. Точно так же образом мы определяем базовые случаи, а затем думаем, что будет результатом. В результате мы получим действие, которое вы- ведет первый символ, а затем остаток строки.

 

Функция print

Функция print принимает значение любого типа – экземпляра класса Show (то есть мы знаем, как представить значение этого типа


 

 
 

в виде строки), вызывает функцию show, чтобы получить из дан- ного значения строку, и затем выводит её на экран. По сути, это putStrLn.show. Это выражение сначала вызывает функцию show на переданном параметре, а затем «скармливает» результат функции putStrLn, которая возвращает действие ввода-вывода; оно, в свою очередь, печатает заданное значение.

main = do

print True print 2

print "ха-ха" print 3.2

 
 

print [3,4,3]

После компиляции и запуска получаем:

True 2

"ха-ха" 3.2

 
 

[3,4,3]

Как вы могли заметить, это очень полезная функция. Помните, мы говорили о том, что действия ввода-вывода выполняются толь- ко из функции main или когда мы выполняем их в интерпретаторе GHCi? После того как мы напечатаем значение (например, 3 или [1, 2, 3]) и нажмём клавишу «Ввод», интерпретатор GHCi вызовет функцию print с введённым значением для вывода на терминал!

ghci> 3

ghci> print 3

ghci> map (++"!") ["хей","хо","ууу"]

["хей!","хо!","ууу!"]

ghci> print $ map (++"!") ["хей","хо","ууу"]

 
 

["хей!","хо!","ууу!"]

Как правило, мы хотим видеть строку на экране, не заключён- ную в кавычки, поэтому для печати строк обычно используется функция putStrLn. Но для печати значений других типов преиму- щественно используется функция print.


 

Функция when

Функция when находится в модуле Control.Monad (чтобы к ней обра- титься, воспользуйтесь import Control.Monad). Она интересна, по- тому что выглядит как оператор управления ходом вычислений, но на самом деле это обычная функция. Она принимает булевское значение и действие ввода-вывода. Если булевское значение истин- но, она возвращает второй параметр – действие ввода-вывода. Если первый параметр ложен, функция возвращает return (), то есть пус- тое действие.

 
 

Напишем программу, которая запрашивает строку текста и, ес- ли строка равна «РЫБА-МЕЧ», печатает её:

import Control.Monad

 

main = do

input <- getLine

 
 

when (input == "РЫБА-МЕЧ") $ do putStrLn input

Без when нам понадобилось бы написать нечто такое:

main = do

input <- getLine

 
 

if (input == "РЫБА-МЕЧ") then putStrLn input else return ()

Как вы видите, функция when позволяет выполнить заданное действие в случае, если некоторое условие истинно, и ничего не делать в противном случае.

 

Функция sequence

 
 

Функция sequence принимает список действий ввода-вывода и воз- вращает одно действие ввода-вывода, последовательно выполня- ющее действия из списка. Результат выполнения этого действия – список результатов вложенных действий. Сигнатура типа функции: sequence :: [IO a] –> IO [a]. Выполним следующее:

main = do

a <– getLine


 

 
 

b <– getLine c <– getLine print [a,b,c]

То же самое, но с использованием функции sequence:

main = do

 
 

rs <– sequence [getLine, getLine, getLine] print rs

Итак, выражение sequence [getLine, getLine, getLine] создаст действие ввода-вывода, которое выполнит функцию getLine три раза. Если мы свяжем это действие с именем, результат будет пред- ставлять собой список результатов действий из изначального спис- ка, в нашем случае – то, что пользователь введёт с клавиатуры.

 
 

Функцияsequenceобычноиспользуется, еслимыхотимпройтись по списку функциями print или putStrLn. Вызов map print [1,2,3,4] не создаёт действия ввода-вывода – вместо этого создаётся список действий. Такой код на самом деле эквивалентен следующему:

[print 1, print 2, print 3, print 4]

 
 

Если мы хотим преобразовать список действий в действие, то необходимо воспользоваться функцией sequence:

ghci> sequence $ map print [1,2,3,4] 1

 
 

4 [(),(),(),()]

Но что это за [(),(),(),()] в конце вывода? При выполнении в GHCi действия ввода-вывода помимо самого действия выводит- ся результат выполнения, но только если этот результат не есть (). Поэтому при выполнении в GHCi putStrLn "ха-ха" просто выводит- ся строка – результатом является (). Если же попробовать ввести getLine, то помимо собственно ввода с клавиатуры будет выведено введённое значение – результатом является IO String.

 

Функция mapM

Поскольку применение функции, возвращающей действие ввода- вывода, к элементам списка и последующее выполнение всех полу-


 

 
 

ченных действий очень распространено, для этих целей были вве- дены две вспомогательные функции – mapM и mapM_. Функция mapM принимает функцию и список, применяет функцию к элементам списка, сводит элементы в одно действие ввода-вывода и выпол- няет их. Функция mapM_ работает так же, но отбрасывает результат действия ввода-вывода. Она используется, когда нам не важен ре- зультат комбинированного действия ввода-вывода.

ghci> mapM print [1,2,3] 1

3 [(),(),()]

ghci> mapM_ print [1,2,3] 1

 
 

3

 

Функция forever

 
 

Функция forever принимает действие ввода-вывода – параметр и воз- вращает действие ввода-вывода – результат. Действие-результат будет повторять действие-параметр вечно. Эта функция входит в модуль Control.Monad. Следующая программа будет бесконечно спрашивать у пользователя строку и возвращать её в верхнем ре- гистре:

import Control.Monad import Data.Char

 

main = forever $ do

putStr "Введите что-нибудь: " l <– getLine

 
 

putStrLn $ map toUpper l

 

Функция forM

Функция forM (определена в модуле Control.Monad) похожа на функ- цию mapM, но её параметры поменяны местами. Первый параметр – это список, второй – это функция, которую надо применить к спис- ку и затем свести действия из списка в одно действие. Для чего


 

 
 

это придумано? Если творчески использовать лямбда-выражения и ключевое слово do, можно проделывать такие фокусы:

import Control.Monad

 

main = do

colors <– forM [1,2,3,4] (\a –> do

putStrLn $ "С каким цветом ассоциируется число "

++ show a ++ "?" color <– getLine

return color)

 
 

putStrLn "Цвета, ассоциирующиеся с 1, 2, 3 и 4: " mapM putStrLn colors

Вот что мы получим при запуске:

С каким цветом ассоциируется число 1? белый

С каким цветом ассоциируется число 2? синий

С каким цветом ассоциируется число 3? красный

С каким цветом ассоциируется число 4? оранжевый

Цвета, ассоциирующиеся с 1, 2, 3 и 4: белый

 
 

синий красный оранжевый

Анонимная функция (\a –> do ... ) – это функция, которая при- нимает число и возвращает действие ввода-вывода. Нам пришлось поместить её в скобки, иначе анонимная функция решит, что сле- дующие два действия ввода-вывода принадлежат ей. Обратите вни- мание, что мы производим вызов return color внутри блока do. Это делается для того, чтобы действие ввода-вывода, возвращаемое бло- ком do, содержало в себе цвет. На самом деле мы не обязаны этого делать, потому что функция getLine уже содержит цвет внутри себя. Выполняя color <– getLine и затем return color, мы распаковываем ре- зультат getLine и затем запаковываем его обратно, то есть это то же самое, что просто вызвать функцию getLine. Функция forM (вызывае- мая с двумя параметрами) создаёт действие ввода-вывода, результат


 

которого мы связываем с идентификатором colors. Этот идентифи- катор – обычный список, содержащий строки. В конце мы распеча- тываем все цвета, вызывая выражение mapM putStrLn colors.

Вы можете думать, что функция forM имеет следующий смысл:

«Создай действие ввода-вывода для каждого элемента в списке. Ка- ков будет результат каждого такого действия, может зависеть от элемента, из которого оно создаётся. После создания списка дейс- твий исполни их и привяжи их результаты к чему-либо». Однако мы не обязаны их связывать – результаты можно просто отбросить.

На самом деле мы могли бы сделать это без использования функ- ции forM, но так легче читается. Обычно эта функция используется, когда нам нужно отобразить (map) и объединить (sequence) действия, которые мы тут же определяем в секции do. Таким образом, мы могли бы заменить последнюю строку на выражение forM colors putStrLn.

 

Обзор системы ввода-вывода

В этой главе мы изучили основы системы ввода-вывода языка Has- kell. Также мы узнали, что такое действия ввода-вывода, как они позволяют выполнять ввод-вывод, в какой момент они выполня- ются. Итак, повторим пройденное: действия ввода-вывода – это значения, такие же, как любые другие в языке Haskell. Мы можем передать их в функции как параметры, функции могут возвращать действия ввода-вывода в качестве результата. Они отличаются тем, что если они попадут в функцию main (или их введут в интерпре- таторе GHCi), то будут выполнены. В этот момент они могут вы- водить что-либо на экран или управлять звуковыводящим устройс- твом. Каждое действие ввода-вывода может содержать результат общения с реальным миром.

Не думайте о функции, например о putStrLn, как о функции, ко- торая принимает строку и печатает её на экране. Думайте о ней как о функции, которая принимает строку и возвращает действие вво- да-вывода. Это действие при выполнении печатает нечто ценное на вашем терминале.


 

БОЛЬШЕ ВВОДА

И ВЫВОДА

Теперь, когда вы понимаете идеи, лежащие в основе ввода-вывода в языке Haskell, можно приступать к интересным штукам. В этой главе мы будем обрабатывать файлы, генерировать случайные чис- ла, читать аргументы командной строки и много чего ещё. Будьте готовы!

 

Файлы и потоки

Вооружившись знанием того, как работают действия ввода-выво- да, можно перейти к чтению и записи файлов. Но прежде давайте посмотрим, как Haskell умеет работать с потоками данных. Пото- ком называется последо-

вательность фрагментов данных, которые посту- пают на вход программы и выводятся в результате её работы. Например, когда вы вводите в про- грамму символы, печатая их на клавиатуре, после- довательность этих сим- волов может рассматри- ваться как поток.


 

Перенаправление ввода

Многие интерактивные программы получают пользовательский ввод с клавиатуры. Однако зачастую гораздо удобнее «скормить» программе содержимое текстового файла. Такой способ подачи входных данных называется перенаправлением ввода.

 
 

Посмотрим, как перенаправление ввода работает с програм- мой на языке 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: <stdin>: hGetLine: end of file


 

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

«Погоди, не читай ничего с клавиатуры, возьми содержимое это- го файла!».

 







Последнее изменение этой страницы: 2017-02-17; Нарушение авторского права страницы

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.204.55.168 (0.012 с.)