Как вы заметили, мы можем использовать рекурсию в системе ввода-вывода подобно тому, как делаем это в чистом коде. Точно так же образом мы определяем базовые случаи, а затем думаем, что будет результатом. В результате мы получим действие, которое выведет первый символ, а затем остаток строки.
Функция print
Функция print
принимает значение любого типа – экземпляра класса Show
(то есть мы знаем, как представить значение этого типа в виде строки), вызывает функцию show
, чтобы получить из данного значения строку, и затем выводит её на экран. По сути, это putStrLn.show
. Это выражение сначала вызывает функцию show
на переданном параметре, а затем «скармливает» результат функции putStrLn
, которая возвращает действие ввода-вывода; оно, в свою очередь, печатает заданное значение.
main = do
print True
print 2
print "ха-ха"
print 3.2
print [3,4,3]
После компиляции и запуска получаем:
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
[(),(),(),()]