Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Объединение действий ввода-выводаСодержание книги
Поиск на нашем сайте
Возможность поместить в программу всего один оператор ввода- вывода не очень-то вдохновляет. Но мы можем использовать клю- чевое слово do для того, чтобы «склеить» несколько операторов ввода-вывода в один. Рассмотрим пример: main = do putStrLn "Привет, как тебя зовут?" name <– getLine putStrLn ("Привет, " ++ name ++ ", ну ты и хипстота!") О, новый синтаксис!.. И он похож на синтаксис императивных языков. Если откомпилировать и запустить эту программу, она бу- дет работать так, как вы и предполагаете. Обратите внимание: мы записали ключевое слово do и затем последовательность шагов, как сделали бы в императивном языке. Каждый из этих шагов – дейст- вие ввода-вывода. Расположив их рядом с помощью ключевого сло- ва do, мы свели их в одно действие ввода-вывода. Получившееся действие имеет тип IO(); это тип последнего оператора в цепочке.
По этой причине функция main всегда имеет тип main:: IO < нечто >, где < нечто > – некоторый конкретный тип. По общеприня- тому соглашению обычно не пишут декларацию типа для функции main. В третьей строке можно видеть ещё один не встречавшийся нам ранее элемент синтаксиса, name <– getLine. Создаётся впечатле- ние, будто считанная со стандартного входа строка сохраняется в переменной с именем name. Так ли это на самом деле? Давайте пос- мотрим на тип getLine. ghci>:t getLine getLine:: IO String Ага!.. Функция getLine – действие ввода-вывода, которое содер- жит результирующий тип – строку. Это понятно: действие ждёт, пока пользователь не введёт что-нибудь с терминала, и затем это нечто будет представлено как строка. Что тогда делает выражение name <– getLine? Можно прочи- тать его так: «выполнить дейс- твие getLine и затем связать ре- зультат выполнения с именем name». Функция getLine имеет тип IO String, поэтому образец name будет иметь тип String. Можно представить действие ввода-вы- вода в виде ящика с ножками, который ходит в реальный мир, что-то в нём делает (рисует граф- фити на стене, например) и иног- да приносит обратно какие-либо данные. Если ящик что-либо при- нёс, единственный способ открыть его и извлечь данные – исполь- зовать конструкцию с символом <–. Получить данные из действия ввода-вывода можно только внутри другого действия ввода-вывода. Таким образом, язык Haskell чётко разделяет чистую и «грязную» части кода. Функция getLine – не чистая функция, потому что её результат может быть неодинаковым при последовательных вызо- вах. Вот почему она как бы «запачкана» конструктором типов IO, и мы можем получить данные только внутри действий ввода-вывода,
имеющих в сигнатуре типа маркер IO. Так как код для ввода-выво- да также «испачкан», любое вычисление, зависящее от «испачкан- ных» IO-данных, также будет давать «грязный»результат. Если я говорю «испачканы», это не значит, что мы не сможем использовать результат, содержащийся в типе IO в чистом коде. Мы временно «очищаем» данные внутри действия, когда связываем их с именем. В выражении name <– getLine образец name содержит обыч- ную строку, представляющую содержимое ящика. Мы можем написать сложную функцию, которая, скажем, при- нимает ваше имя как параметр (обычная строка) и предсказывает вашу удачливость или будущее всей вашей жизни, основываясь на имени: main = do putStrLn "Привет, как тебя зовут?" name <– getLine putStrLn $ "Вот твоё будущее: " ++ tellFortune name Функция tellFortune (или любая другая, которой мы передаём значение name) не должна знать ничего про IO – это обычная функ- ция String –> String. Посмотрите на этот образец кода. Корректен ли он? nameTag = "Привет, меня зовут " ++ getLine Если вы ответили «нет», возьмите с полки пирожок. Если отве- тили «да», убейте себя об стену... Шучу, не надо! Это выражение не сработает, потому что оператор ++ требует, чтобы оба параметра были списками одинакового типа. Левый параметр имеет тип String (или [Char], если вам угодно), в то время как функция getLine воз- вращает значение типа IO String. Вы не сможете конкатенировать строку и результат действия ввода-вывода. Для начала нам нужно извлечь результат из действия ввода-вывода, чтобы получить зна- чение типа String, и единственный способ сделать это – выполнить что-то вроде name <– getLine внутри другого действия ввода-вывода. Если мы хотим работать с «нечистыми» данными, то должны де- лать это в «нечистом» окружении!… Итак, грязь от нечистоты рас- пространяется как моровое поветрие, и в наших интересах делать часть для осуществления ввода-вывода настолько малой, насколько это возможно.
Каждое выполненное действие ввода-вывода заключает в себе ре- зультат. Вот почему наш предыдущий пример можно переписать так: main = do foo <- putStrLn "Привет, как тебя зовут?" name <– getLine putStrLn ("Привет, " ++ name ++ ", ну ты и хипстота!") Тем не менее образец foo всегда будет получать значение (), так что большого смысла в этом нет. Заметьте: мы не связываем послед- ний вызов функции putStrLn с именем, потому что в блоке do пос- ледний оператор, в отличие от предыдущих, не может быть связан с именем. Мы узнаем причины такого поведения немного позднее, когда познакомимся с миром монад. До тех пор можно считать, что блок do автоматически получает результат последнего оператора и возвращает его в качестве собственного результата. За исключением последней строчки, каждая строка в блоке do может быть использована для связывания. Например, putStrLn "ЛЯ" может быть записана как _ <– putStrLn "ЛЯ". Но в этом нет никакого смысла, так что мы опускаем <– для действий ввода-вывода, не воз- вращающих значимого результата. Иногда начинающие думают, что вызов myLine = getLine считает значение со стандартного входа и затем свяжет это значе- ние с именем myLine. На самом деле это не так. Такая запись даст фун- кции getLine другое синонимичное имя, в данном случае – myLine. Запомните: чтобы получить значение из действия ввода-вывода, вы должны выполнять его внутри другого действия ввода-вывода и свя- зывать его с именем при помощи символа <–. Действие ввода-вывода будет выполнено, только если его имя main или если оно помещено в составное действие с помощью блока do. Также мы можем использовать блок do для того, чтобы «скле- ить» несколько действий ввода-вывода в одно. Затем можно будет использовать его в другом блоке do и т. д. В любом случае действие будет выполнено, только если оно каким-либо образом вызывается из функции main. Ах, да, есть ещё один способ выполнить действие ввода-выво- да! Если напечатать его в интерпретаторе GHCi и нажать клавишу Enter, действие выполнится.
gchi> putStrLn "При-и-и-вет" При-и-и-вет Даже если мы просто наберём некоторое число или вызовем некоторую функцию в GHCi и нажмём Enter, интерпретатор GHCi вычислит значение, затем вызовет для него функцию show, чтобы получить строку, и напечатает строку на терминале, используя функцию putStrLn.
|
||||||||||||||||||||||||||||||||||
Последнее изменение этой страницы: 2017-02-17; просмотров: 180; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.216.208.243 (0.006 с.) |