Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Использование ключевого слова let внутри блока do

Поиск

 
 

Помните связывания при помощи ключевого слова let? Если уже подзабыли, освежите свои знания. Связывания должны быть тако- го вида: let < определения > in < выражение >, где < определения > – это име- на, даваемые выражениям, а < выражение > использует имена из < опре- делений >. Также мы говорили, что в списковых выражениях часть in не нужна. Так вот, в блоках do можно использовать выражение let таким же образом, как и в списковых выражениях. Смотрите:

import Data.Char

 

main = do

putStrLn "Ваше имя?" firstName <– getLine putStrLn "Ваша фамилия?" lastName <– getLine

let bigFirstName = map toUpper firstName bigLastName = map toUpper lastName

putStrLn $ "Привет, " ++ bigFirstName ++ " "

++ bigLastName

 
 

++ ", как дела?"

Видите, как выровнены операторы действий ввода-вывода в блоке do? Обратите внимание и на то, как выровнено выраже- ние let по отношению к действиям ввода-вывода и как выровнены образцы внутри выражения let. Это хороший пример, потому что выравнивание текста очень важно в языке Haskell. Далее мы запи- сали вызов map toUpper firstName, что превратит, например, "Иван" в намного более солидное "ИВАН". Мы связали эту строку в верхнем регистре с именем, которое использовали в дальнейшем при выво- де на терминал.


 

Вам может быть непонятно, когда использовать символ <–, а ког- да выражение let. Запомните: символ <– (в случае действий ввода- вывода) используется для выполнения действий ввода-вывода и свя- зывания результатов с именами. Выражение map toUpper firstName не является действием ввода-вывода – это чистое выражение. Со- ответственно, используйте символ <– для связывания результатов действий ввода-вывода с именами, а выражение let – для связыва- ния имён с чистыми значениями. Если бы мы выполнили что-то вроде let firstName = getLine, то просто создали бы синоним функ- ции getLine, для которого значение всё равно должно получаться с помощью символа <–.

 

Обращение строк

 
 

Теперь напишем программу, которая будет считывать строки, пере- ставлять в обратном порядке буквы в словах и распечатывать их. Вы- полнение программы прекращается при вводе пустой строки. Итак:

main = do

line <– getLine if null line

then return () else do

putStrLn $ reverseWords line main

 

 
 

reverseWords:: String –> String reverseWords = unwords. map reverse. words

Чтобы лучше понять, как работает программа, сохраните её в файле reverse.hs, скомпилируйте и запустите:

$ ghc reverse.hs

[1 of 1] Compiling Main (reverse.hs, reverse.o) Linking reverse...

$./reverse

уберитесь в проходе номер 9 ьсетиребу в едохорп ремон 9 козёл ошибки осветит твою жизнь лёзок икбишо титевсо юовт ьнзиж но это всё мечты

 
 

он отэ ёсв ытчем


 

Для начала посмотрим на функцию reverseWords. Это обыч- ная функция, которая принимает строку, например "эй ты мужик", и вызывает функцию words, чтобы получить список слов ["эй", "ты","мужик"]. Затем мы применяем функцию reverse к каждому элементу списка, получаем ["йэ","ыт","кижум"] и помещаем ре- зультат обратно в строку, используя функцию unwords. Конечным результатом будет "йэ ыт кижум".

Теперь посмотрим на функцию main. Сначала мы получаем стро- ку с терминала с помощью функции getLine. Далее у нас имеется условное выражение. Запомните, что в языке Haskell каждое клю- чевое слово if должно сопровождаться секцией else, так как каж- дое выражение должно иметь некоторое значение. Наш оператор записан так, что если условие истинно (в нашем случае – когда вве- дут пустую строку), мы выполним одно действие ввода-вывода; если оно ложно – выполним действие ввода-вывода из секции else. По той же причине в блоке do условные операторы if должны иметь вид if < условие > then < действие ввода-вывода > else < действие ввода- вывода >.

 
 

Вначале посмотрим, что делается в секции else. Поскольку можно поместить только одно действие ввода-вывода после клю- чевого слова else, мы используем блок do для того, чтобы «склеить» несколько операторов в один. Эту часть можно было бы написать так:

else (do

 
 

putStrLn $ reverseWords line main)

Подобная запись явно показывает, что блок do может рассмат- риваться как одно действие ввода-вывода, но и выглядит она не очень красиво. В любом случае внутри блока do мы можем вызвать функцию reverseWords со строкой – результатом действия getLine и распечатать результат. После этого мы выполняем функцию main. Получается, что функция main вызывается рекурсивно, и в этом нет ничего необычного, так как сама по себе функция main – тоже действие ввода-вывода. Таким образом, мы возвращаемся к началу программы в следующей рекурсивной итерации.

Ну а что случится, если мы получим на вход пустую строку? В этом случае выполнится часть после ключевого слова then. То есть выпол- нится выражение return (). Если вам приходилось писать на импера-


 

тивных языках вроде C, Java или на Python, вы наверняка уверены, что знаете, как работает функция return – и, возможно, у вас воз- никнет искушение пропустить эту часть текста. Но не стоит спе- шить: функция return в языке Haskell работает совершенно не так, как в большинстве других языков! Её название сбивает с толку, но на самом деле она довольно сильно отличается от своих «тёзок». В императивных языках ключевое слово return обычно прекращает выполнение метода или процедуры и возвращает некоторое зна- чение вызывающему коду. В языке Haskell (и особенно в действиях ввода-вывода) одноимённая функция создаёт действие ввода-выво- да из чистого значения. Если продолжать аналогию с коробками, она берёт значение и помещает его в «коробочку». Получившееся в результате действие ввода-вывода на самом деле не выполняет ни- каких действий – оно просто инкапсулирует некоторое значение. Таким образом, в контексте системы ввода-вывода return "ха-ха" будет иметь тип IO String. Какой смысл преобразовывать чистое значение в действие ввода-вывода, которое ничего не делает? За- чем «пачкать» нашу программу больше необходимого? Нам нужно некоторое действие ввода-вывода для второй части условного опе- ратора, чтобы обработать случай пустой строки. Вот для чего мы создали фиктивное действие ввода-вывода, которое ничего не де- лает, записав return ().

 
 

Вызов функции return не прекращает выполнение блока do – ничего подобного! Например, следующая программа успешно вы- полнится вся до последней строчки:

main = do

return ()

return "ХА-ХА-ХА" line <– getLine return "ЛЯ-ЛЯ-ЛЯ" return 4

 
 

putStrLn line

Всё, что делает функция return, – создаёт действия ввода-выво- да, которые не делают ничего, кроме как содержат значения, и все они отбрасываются, поскольку не привязаны к образцам. Мы мо- жем использовать функцию return вместе с символом <– для того, чтобы связывать значения с образцами.


 

main = do

let a = "ад"

b = "да!"

 
 

putStrLn $ a ++ " " ++ b

Как вы можете видеть, функция return выполняет обратную операцию по отношению к операции <–. В то время как функция return принимает значение и помещает его в «коробку», операция <– принимает (и исполняет) «коробку», а затем привязывает получен- ное из неё значение к имени. Но всё это выглядит лишним, так как в блоках do можно использовать выражение let для привязки к име- нам, например так:

main = do

let a = "hell" b = "yeah"

 
 

putStrLn $ a ++ " " ++ b

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

 



Поделиться:


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

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