Module Tree ( Tree(Leaf, Branch), leafList) where
Поможем в ✍️ написании учебной работы
Поможем с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой

data Tree a = Leaf a | Branch (Tree a) (Tree a)

leafList (Leaf x) = [x]

leafList (Branch left right) = leafList left ++ leafList right

Модуль явно экспортирует Tree, Leaf, BranchиleafList. Экспортируемые из модуля имена перечисляются в скобках после ключевого слова module. Если это перечисление не указано, по умолчанию из модуля экспортируются все имена. Заметьте, что имена типа и его конструкторов должны быть сгруппированы, как в конструкции Tree(Leaf,Branch). В качестве сокращения можно использовать запись Tree(..). Также возможно экспортировать только часть конструкторов данных.

Модуль Tree теперь может быть импортирован в какой-либо другой модуль:

Module Main where import Tree (Tree(Leaf,Branch), leafList)

Здесь мы явно указали список импортируемых сущностей; если опустить его, импортируются все сущности, экспортируемые из модуля.

Очевидно, если в двух импортируемых модулях содержатся различные сущности с одним именем, возникнет проблема. Для того, чтобы избежать ее в языке существует ключевое слово qualified, при помощи которого определяются те импортируемые модули, имена объектов которых приобретают вид: «Модуль.Объект». Например, для модуля Tree:

Module Main where import qualified Tree

leafList = Tree.leafList

4.8. Абстрактные типы данных

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

Module Dictionary where

data Dictionary = Dictionary [(String,String)]

getMeaning :: Dictionary -> String -> Maybe String

getMeaning [] _ = Nothing

getMeaning ((word,meaning):xs) w | w == word = Just meaning

| otherwise = getMeaning xs w

Функция getMeaning по заданному словарю и слову возвращает найденное значение (с использованием типа Maybe). Сам словарь представляется списком пар.

Пользователь этого модуля может определить addWord, которая добавляет пару «слово-значение» в словарь и возвращает модифицированный словарь:

Import Dictionary

addWord (Dictionary dict) word meaning = Dictionary ((word,meaning):dict)

Здесь пользователь видит представление словаря в виде списка и может воспользоваться этим. Однако в дальнейшем мы можем захотеть изменить представление словаря. Список — довольно неэффективная структура данных для поиска, если он становится велик. Гораздо лучше использовать хеш-таблицы или деревья поиска. Однако, если представление типа Dictionary открыто, мы не можем изменить его без риска нарушить функционирование пользовательских программ.

Сделаем тип Dictionary абстрактным, чтобы скрыть от пользователей модуля его внутреннее представление. Определим в модуле значение emptyDict, представляющее собой пустой словарь и функцию addWord. Тогда пользователи смогут общаться со значениями типа Dictionary только с помощью разрешенных функций:

Module Dictionary (Dictionary, getMeaning, addWord, emptyDict) where

data Dictionary = Dictionary [(String,String)]

getMeaning :: Dictionary -> String -> Maybe String

getMeaning [] _ = Nothing

getMeaning ((word,meaning):xs) w | w == word = Just meaning

| otherwise = getMeaning xs w

addWord (Dictionary dict) w m = Dictionary ((w,m):dict)

emptyDict = Dictionary []

Абстрактные типы данных предоставляют механизм сокрытия данных, который в объектно-ориентированных языках программирования называется инкапсуляцией.

4.9. Операции ввода-вывода

4.9.1. Базовые операции ввода-вывода

В отличие от других функций операции ввода-вывода выполняют некоторое действие и возвращают некоторое значение. В системе типов это значение «помечено» типом IO, который отличает действия от других значений. Например, рассмотрим функцию getChar:

GetChar :: IO Char

IO Char показывает, что getChar при вызове выполняет некоторое действие, которое возвращает символ. Действия, которые не возвраща­ют результата, используют тип IO (). Символ () означает пустой тип. Например, функция putChar:

putChar :: Char -> IO ()

Она принимает символ и не возвращает ничего.

Действия связываются друг с другом с помощью оператора >>=, который определяется посредством монад (см. далее), может быть объяснен с помощью do-нотации. Ключевое слово do начинает последовательность операторов, которые выполняются по порядку. Оператор может быть либо действием, либо образцом, связываемым с результатом действия с помощью <- (присваивание).Следующая программа считывает символ и печатает его:

Main :: IO ()

main = do c <-getChar

PutChar c

Функция main модуля Main является точкой входа в программу на языке HASKELL. Ее тип должен быть IO ().

Допустим, нам необходимо определить функцию ready, которая считывает символ и возвращает True, если он равен ’y’. Нам нужно создать действие, которое ничего не делает, но возвращает это некоторое значение в качестве результата. Для этого служит функция return:

return :: a -> IO a

Таким образом, ready определяется так:

Ready :: IO Bool

ready = do c <-getChar

return (c == ’y’)

Теперь можно определить более сложные функции ввода-вывода. Функция getLine, возвращающая строку, считанную с клавиатуры с символом конца строки в качестве завершающего:

GetLine :: IO String

getLine = do c <-getChar

if c== ’\n’

then return ""

else do l <-getLine

Return (c:l)

Действие ввода-вывода не может быть выполнено в обычном выражении, поскольку оно не оперирует типом IO.

Дата: 2016-10-02, просмотров: 222.