Россия, Петерубрг, СПБ-ГПУ, 1998 |
Основные операции ввода - вывода
Система ввода - вывода в Haskell является чисто функциональной, но при этом обладает выразительной мощью обычных языков программирования. Чтобы достичь этого, Haskell использует монаду для интеграции операций ввода - вывода в чисто функциональный контекст.
Монада ввода - вывода используется в Haskell как связующее звено между значениями, присущими функциональному языку, и действиями, характеризующими операции ввода - вывода и императивное программирование в общем. Порядок вычисления выражений в Haskell ограничен только зависимостями данных; реализация обладает значительной свободой в выборе этого порядка. Действия, тем не менее, должны быть упорядочены определенным образом для выполнения программы и, в частности, ввода - вывода, для того чтобы быть правильно интерпретированы. В Haskell монада ввода - вывода предоставляет пользователю способ указать последовательное связывание действий, и реализация обязана соблюдать этот порядок.
Термин монада происходит из отрасли математики, известной как теория категорий. Однако, с точки зрения программиста Haskell , лучше думать о монаде как об абстрактном типе данных. В случае монады ввода - вывода абстрактными значениями являются упомянутые выше действия. Некоторые операции являются примитивными действиями, соответствующими обычным операциям ввода - вывода. Специальные операции (методы в классе Monad, см. раздел "6.3.6" ) последовательно связывают действия, соответствующие последовательным операторам (таким как точка с запятой) в императивных языках.
7.1. Стандартные функции ввода - вывода
Хотя Haskell обеспечивает довольно сложные средства ввода - вывода, определенные в библиотеке IO, многие программы на Haskell можно писать, используя лишь несколько простых функций, которые экспортируются из Prelude и которые описаны в этом разделе.
Все функции ввода - вывода, описанные здесь, имеют дело с символами. Обработка символа новой строки будет различаться в различных системах. Например, два символа ввода, возврат каретки и перевод строки, могут быть считаны как один символ новой строки. Эти функции нельзя использовать в переносимых программах для бинарного ввода - вывода.
Далее, вспомним, что String является синонимом для [Char] (раздел "6.1.2" ).
Функции вывода
Эти функции записывают в стандартное устройство вывода (обычно это пользовательский терминал).
putChar :: Char -> IO () putStr :: String -> IO () putStrLn :: String -> IO () - добавляет символ новой строки print :: Show a => a -> IO ()
Функция print выводит значение любого пригодного для печати типа на стандартное устройство вывода. Пригодные для печати типы - это те типы, которые являются экземплярами класса Show; print преобразует значения в строки для вывода, используя операцию show, и добавляет символ новой строки.
Например, программа для печати первых 20 целых чисел и их степеней 2 могла быть записана так:
main = print ([(n, 2^n) | n >- [0..19]])
Функции ввода
Эти функции считывают данные из стандартного устройства ввода (обычно это пользовательский терминал).
getChar :: IO Char getLine :: IO String getContents :: IO String interact :: (String -> String) -> IO () readIO :: Read a => String -> IO a readLn :: Read a => IO a
Операция getChar вызывает исключение (раздел "7.3" ) при появлении признака конца файла, a предикат isEOFError, который распознает это исключение, определен в библиотеке IO. Операция getLine вызывает исключение при тех же обстоятельствах, что и hGetLine, определенная в библиотеке IO.
Операция getContents возвращает весь пользовательский ввод в виде одной строки, которая считывается лениво, по мере надобности. Функция interact принимает в качестве аргумента функцию типа String -> String. Весь ввод из стандартного устройства ввода передается этой функции в качестве аргумента, а результирующая строка выводится на стандартное устройство вывода.
Обычно операция read из класса Read используется для преобразования строки в значение. Функция readIO похожа на read, за исключением того, что она предупреждает монаду ввода - вывода об ошибке разбора вместо завершения программы. Функция readLn объединяет getLine и readIO.
Следующая программа просто удаляет все символы, не являющиеся ASCII, из своего стандартного ввода и отображает результат на своем стандартном выводе. (Функция isAscii определена в библиотеке.)
main = interact (filter isAscii)
Файлы
Эти функции оперируют файлами символов. Файлы указываются посредством строк, используя некоторый, зависящий от реализации, метод разрешения строк как имен файлов.
Функции writeFile и appendFile соответственно записывают или добавляют в конец строку, свой второй аргумент, в файл, свой первый аргумент. Функция readFile считывает файл и возвращает содержимое файла в виде строки. Файл считывается лениво, по требованию, как в getContents.
type FilePath = String writeFile :: FilePath -> String -> IO () appendFile :: FilePath -> String -> IO () readFile :: FilePath -> IO String
Обратите внимание, что writeFile и appendFile записывают литеральную строку в файл. Для того чтобы записать значение любого пригодного для печати типа, как в print, сначала используется функция show для преобразования значения в строку.
main = appendFile "квадраты" (show [(x,x*x) | x >- [0,0.1..2]])