Kniga-Online.club
» » » » Миран Липовача - Изучай Haskell во имя добра!

Миран Липовача - Изучай Haskell во имя добра!

Читать бесплатно Миран Липовача - Изучай Haskell во имя добра!. Жанр: Программирование издательство -, год 2004. Так же читаем полные версии (весь текст) онлайн без регистрации и SMS на сайте kniga-online.club или прочесть краткое содержание, предисловие (аннотацию), описание и ознакомиться с отзывами (комментариями) о произведении.
Перейти на страницу:

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.

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

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

Функция putStr

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

main = do

   putStr "Привет, "

   putStr "я "

   putStrLn "Энди!"

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

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

Функция putChar

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

main = do

   putChar 'A'

   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

Перейти на страницу:

Миран Липовача читать все книги автора по порядку

Миран Липовача - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки kniga-online.club.


Изучай Haskell во имя добра! отзывы

Отзывы читателей о книге Изучай Haskell во имя добра!, автор: Миран Липовача. Читайте комментарии и мнения людей о произведении.


Уважаемые читатели и просто посетители нашей библиотеки! Просим Вас придерживаться определенных правил при комментировании литературных произведений.

  • 1. Просьба отказаться от дискриминационных высказываний. Мы защищаем право наших читателей свободно выражать свою точку зрения. Вместе с тем мы не терпим агрессии. На сайте запрещено оставлять комментарий, который содержит унизительные высказывания или призывы к насилию по отношению к отдельным лицам или группам людей на основании их расы, этнического происхождения, вероисповедания, недееспособности, пола, возраста, статуса ветерана, касты или сексуальной ориентации.
  • 2. Просьба отказаться от оскорблений, угроз и запугиваний.
  • 3. Просьба отказаться от нецензурной лексики.
  • 4. Просьба вести себя максимально корректно как по отношению к авторам, так и по отношению к другим читателям и их комментариям.

Надеемся на Ваше понимание и благоразумие. С уважением, администратор kniga-online.


Прокомментировать
Подтвердите что вы не робот:*
Подтвердите что вы не робот:*