Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программирование » Изучай Haskell во имя добра! - Миран Липовача

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

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

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 37 38 39 40 41 42 43 44 45 ... 96
Перейти на страницу:

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

True

2

"ха-ха"

3.2

[3,4,3]

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

ghci> 3

3

ghci> print 3

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

2

3

4

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

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

Функция mapM

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

ghci> mapM print [1,2,3]

1

2

3

[(),(),()]

ghci> mapM_ print [1,2,3]

1

2

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.

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

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

1 ... 37 38 39 40 41 42 43 44 45 ... 96
Перейти на страницу:
Тут вы можете бесплатно читать книгу Изучай Haskell во имя добра! - Миран Липовача.
Комментарии