Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь 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 – действие ввода-вывода, которое содер- жит результирующий тип – строку. Это понятно: действие ждёт, пока пользователь не введёт что-нибудь с терминала, и затем это нечто будет представлено как строка. Что тогда делает выражение
нёс, единственный способ открыть его и извлечь данные – исполь- зовать конструкцию с символом <–. Получить данные из действия ввода-вывода можно только внутри другого действия ввода-вывода. Таким образом, язык 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; просмотров: 251; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 216.73.216.156 (0.01 с.) |