Типы. Классы.
Типы, определяемые пользователем
Типы, определяемые пользователем
Рекурсивные типы
Примеры функций для работы с бинарными деревьями
Клиент. Описание данных
Создание значений типа Client
Client: унификация информации о людях
Client: Функция выбора имени компании/клиента
Client: Функция выбора имени компании/клиента
Встроенный тип Maybe («может быть») имеет только два вида значения:
Client: выбор имени компании (если это не компания – «ничего»)
Client: еще функции
Проверка статуса клиента
Ad-hoc полиморфизм
Классы типов
Объявление класса
Класс типов для определения равенства
Сравнение типов, определяемых пользователем, на равенство
Сравнение типов, определяемых пользователем, на равенство
Равенство для бинарных деревьев
Декларация экземпляра
Равенство для бинарных деревьев
Сравнение типов, определяемых пользователем
Сравнение типов, определяемых пользователем
Сравнение типов, определяемых пользователем
Определение метода ‘<‘ для типа Data
Определение метода ‘<‘ для типа Data
Определение корректного сравнения для типа Data
Другой вариант описания типа Data
Сортировка дат типа Data
Создание собственного класса «почти равно»
Создание собственного класса «почти равно»
«почти равно» для бинарных деревьев
Некоторые ограничения на декларацию экземпляров
284.00K
Категория: ПрограммированиеПрограммирование

Типы. Классы

1. Типы. Классы.

Лектор:
доцент каф. АОИ
Салмина Нина
Юрьевна

2. Типы, определяемые пользователем

Декларация данных: data
Перечислимые типы (конечное число нульарных конструкторов данных):
data Color = Red | Green | Blue | Yellow
-- четыре величины
Полиморфный тип с одним конструктором
Color, Point –имена
data Point a = Pt a a
(конструкторы) типов
Red, Green, Pt – конструкторы
Примеры:
данных
e1 :: Color
e3 :: Point Float
e1 = Red
e3 = Pt 2.0 3.0
e2 :: [Color]
e4 :: Point (Point Int)
e2 = [Red, Blue]
e4 = Pt (Pt 1 2) (Pt 3 4)
Конструктор типа и конструктор данных могут иметь одно имя:
data Point a = Point a a

3. Типы, определяемые пользователем

Выражение deriving Show
дает возможность печатать значения новых типов:
Примеры:
data Color = Red | Green | Blue | Yellow deriving Show
data Point a = Pt a a deriving Show
e1 :: Color
e3 :: Point Float
e1 = Red
e3 = Pt 2.0 3.0
e2 :: [Color]
e4 :: Point (Point Int)
e2 = [Red, Blue]
e4 = Pt (Pt 1 2) (Pt 3 4)
> e1
Red
> e4
Pt (Pt 1 2) (Pt 3 4)

4. Рекурсивные типы

Тип для бинарных деревьев:
data Tree a = Leaf a | Branch (Tree a) (Tree a)
deriving Show
вершины дерева – либо листья, содержащие
величину типа а, либо внутренними узлами,
содержащими два поддерева

5. Примеры функций для работы с бинарными деревьями

Отображение дерева в список листьев:
fringe :: Tree a -> [a]
fringe (Leaf x) = [x]
fringe (Branch left right) = fringe left ++ fringe right
Имеют ли два дерева одну и ту же форму:
sameShape :: Tree a -> Tree b -> Bool
sameShape (Leaf x) (Leaf y) = True
sameShape (Branch l1 r1) (Branch l2 r2) =
sameShape l1 l2 && sameShape r1 r2
sameShape x y = False

6. Клиент. Описание данных

-- три типа клиентов:
-- 1. правительственные организации, иденцифицируемые по названиям
-- 2. компании: название, идентификационный номер, данные контактного лица с
указанием его места в иерархии компании
-- 3. отдельные клиенты: фамилия, имя, желание получать в дальнейшем
информацию о предложениях и скидках
data Client = GovOrg String
| Company String Integer String String
| Individual String String Bool
deriving Show
-- Client - название типа
-- GovOrg, Company, Individual - имена конструкторов + типы их аргументов
Разные конструкторы – для построения разных вариантов значений!

7. Создание значений типа Client

data Client = GovOrg String
| Company String Integer String String
| Individual String String Bool
deriving Show
Для создания значений типа Client указывается имя конструктора
и значения аргументов в том порядке, в котором эти аргументы
появляются в объявлении:
*Main> Individual “Ivanov" “Ivan" True
Individual “Ivanov" “Ivan" True
*Main> Company “IT co" 342 “Petrov" “Director”
Company “IT co" 342 “Petrov" “Director”

8. Client: унификация информации о людях

data Client = GovOrg String
| Company String Integer Person String
| Individual Person Bool
deriving Show
data Person = Person String String
deriving Show
-- !!! ВАЖНО! внутри модуля ВСЕ конструкторы должны иметь разные
имена: даже в разных типах должны быть разные имена
конструкторов!
-- НО! имя типа и имя конструктора могут совпадать
*Main> Company “Co_pravo" 123 (Person "Ivanov" "Ivan") "director"
Company “Co_pravo" 123 (Person "Ivanov" "Ivan") "director"

9. Client: Функция выбора имени компании/клиента

clientName :: Client -> String
clientName (GovOrg name) = name
clientName (Company name _ _ _ ) = name
clientName (Individual (Person fName lName) _ )
= fName ++ " " ++ lName
*Main> clientName (Individual (Person “Ivanov" “Ivan") False)
“Ivanov Ivan"

10. Client: Функция выбора имени компании/клиента

Что будет, если функция рассмотрит не все случаи?
clientName :: Client -> String
clientName (Company name _ _ _ ) = name
*Main> clientName (Individual (Person "aaa" "sss") False)
"*** Exception: D:\\UCH\Хаскел\data.hs:37:1-39: Non-exhaustive
patterns in function clientName
Часто встречается на практике

11. Встроенный тип Maybe («может быть») имеет только два вида значения:

data Maybe a = Nothing
| Just a
Nothing – пустой конструктор (функция не имеет ничего
подходящего)
Just – непустой конструктор с параметром типа а
Используется для отслеживания различных
ошибочных ситуаций:
firstElem [ ] = Nothing
firstElem (x:xs) = Just x

12. Client: выбор имени компании (если это не компания – «ничего»)

clientNameN :: Client -> Maybe String
clientNameN (Company name _ _ _ ) = Just name
clientNameN ( _ ) = Nothing
*Main> clientNameN (Company "zzz" 12 (Person "aaa" "sss") "qqq")
Just "zzz"
*Main> clientNameN (Individual (Person "aaa" "sss") False)
Nothing
Подобные функции называются «частичными»

13. Client: еще функции

--функция, определяющая ответственность (должность) клиента:
responsibility :: Client -> String
responsibility (Company _ _ _ x) = x
responsibility _ = "Unknowm"
--особый статус клиента: если он директор, или если его имя "Mr.
Boss"
specialClient :: Client -> Bool
specialClient x | "Mr. Boss" == clientName x = True
specialClient x | responsibility x == "Director" = True
specialClient _ = False
Или:
specialClient x | "Mr. Boss" == clientName x = True
| responsibility x == "Director" = True
| otherwise = False

14. Проверка статуса клиента

asas1 = Individual (Person "aaa" "sss") False
asas2 = Company "eee" 123 (Person "aa" "xx") "Director"
asas3 = GovOrg "Mr. Boss"
*Main> specialClient asas1
False
*Main> specialClient asas2
True
*Main> specialClient asas3
True

15. Ad-hoc полиморфизм

Специальный полиморфизм (ad-hoc polimorfism)
позволяет выразить тот факт, что
определенные типы проявляют общее для
них поведение.
Другое название - перегрузка
Примеры полиморфизма ad-hoc:
- Численные операторы (+, -, *) работают на числах
различных типов
- Логические операторы ==, >, < работают не только на
числах, но и на многих других типах

16. Классы типов

Классы типов обеспечивают структурный
способ для управления полиморфизмом
ad-hoc.
Позволяют декларировать, какие типы есть
экземпляры какого класса, а также
обеспечивают определения перегруженных
операций, ассоциированных с классом.

17. Объявление класса

class Имя класса переменная where
функция1 :: тип1
...
функцияN :: типN
определения_предлагаемые_по_умолчанию

18. Класс типов для определения равенства

class Eq a where
(==), (/=) :: a -> a -> Bool
x /= y = not (x==y)
Eq – имя класса
==, /= - операторы класса
«тип а есть экземпляр класса Eq, если существуют
операторы (==), (/=) подходящего типа,
определенные для элементов класса а»

19. Сравнение типов, определяемых пользователем, на равенство

data Color = Red | Green | Blue | Yellow deriving Show
data Point a = Pt a a deriving Show
e1 :: Color
e3 :: Point Float
e1 = Red
e3 = Pt 2.0 3.0
e2 :: [Color]
e4 :: Point (Point Int)
e2 = [Red, Blue]
e4 = Pt (Pt 1 2) (Pt 3 4)
> e1 == Red
<interactive>:1:1: error:
* No instance for (Eq Color) arising from a use of `=='
* In the expression: e1 == Red
In an equation for `it': it = e1 == Red

20. Сравнение типов, определяемых пользователем, на равенство

data Color = Red | Green | Blue | Yellow deriving (Eq, Show)
data Point a = Pt a a deriving (Show,Eq)
e1 :: Color
e3 :: Point Float
e1 = Red
e3 = Pt 2.0 3.0
e2 :: [Color]
e4 :: Point (Point Int)
e2 = [Red, Blue]
e4 = Pt (Pt 1 2) (Pt 3 4)
> e1 == Red
True
> e2 == [ ]
False

21. Равенство для бинарных деревьев

data Tree a = Leaf a | Branch (Tree a) (Tree a)
deriving Show
Как сравнивать?

22. Декларация экземпляра

instance Имя_класса тип where
функция1 = … -- тело функции
...
функцияN = … -- тело функции
Для типов, определяемых пользователем:
instance (Имя_класса тип) =>
Имя_класса (Тип_пользователя тип) where

Знак наследования

23. Равенство для бинарных деревьев

data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Show
instance (Eq a) => Eq (Tree a) where
(Leaf a) == (Leaf b) = a == b
(Branch l1 r1) == (Branch l2 r2) = (l1 == l2) && (r1 == r2)
tree1,tree2 :: Tree Int
tree1 = Branch (Leaf 1) (Branch (Leaf 2) (Leaf 3))
tree2 = Branch (Leaf 1) (Branch (Leaf 2) (Leaf 3))
*Main> tree1 == tree2
True

24. Сравнение типов, определяемых пользователем

Классовое расширение: существует класс Ord, который
наследует все операции из Eq, но дополнительно имеет
операции сравнения и функции минимума и максимума:
class (Eq a) => Ord a where
(<), (<=), (>=), (>) :: a -> a -> Bool
max, min :: a -> a -> a
Eq есть суперкласс для Ord (Ord есть подкласс Eq): любой
тип, являющийся экземпляром Ord, должен быть
экземпляром Eq
Как следствие – методы для операций подкласса могут
использовать методы суперкласса

25. Сравнение типов, определяемых пользователем

data Color = Red | Green | Blue | Yellow deriving (Eq, Ord, Show)
data Point a = Pt a a deriving (Eq, Ord, Show)
q1 :: Color
q1 = Red
q6 :: ([Color], Int, Point Int)
q6 = ([Red, Yellow], 34, Pt 5 5)
*Main> q6 > ([Red, Yellow], 34, Pt 5 1)
True
*Main> q6 < ([Blue, Yellow], 34, Pt 5 5)
True
*Main> q1 < Blue
True
*Main> "Red" < "Blue"
False

26. Сравнение типов, определяемых пользователем

Тип Data: число, месяц, год
data Data a = Data (a,a,a) deriving (Eq,Show,Ord)
Неверное сравнение дат:
> Data (31,2,1000) < Data (2,12,2022)
False
Необходимо решить следующий вопрос: как определить
действительное поведение ‘<‘ на элементах типа Data?
Для этого используется декларация экземпляра: instance

27. Определение метода ‘<‘ для типа Data

Определение метода ‘<‘ для типа
Data
data Data a = Data (a,a,a) deriving (Eq,Show)
instance (Ord a) => Ord (Data a) where
Data (a1,b1,c1) < Data (a2,b2,c2) =
(c1 < c2 ) ||
((c1 == c2) && (b1 < b2)) ||
((c1 == c2) && (b1 == b2) && (a1<a2))
«Тип Data есть экземпляр типа класса Ord и для него
определен метод, соответствующий операции ‘<‘»
> Data (31,2,1000) < Data (2,12,2022)
True

28. Определение метода ‘<‘ для типа Data

Определение метода ‘<‘ для типа
Data
data Data a = Data (a,a,a) deriving (Eq,Show)
instance (Ord a) => Ord (Data a) where
Data (a1,b1,c1) < Data (a2,b2,c2) =
Предупреждение!!!
‘<=‘
(c1 < c2 ) ||
((c1 == c2) && (b1 < b2)) ||
((c1 == c2) && (b1 == b2) && (a1<a2))
«Тип Data есть экземпляр типа класса Ord и для него
определен метод, соответствующий операции ‘<‘»
> Data (31,2,1000) < Data (2,12,2022)
True

29. Определение корректного сравнения для типа Data

data Data a = Data (a,a,a) deriving (Eq,Show)
instance (Ord a) => Ord (Data a) where
Data (a1,b1,c1) < Data (a2,b2,c2) =
(c1 < c2 ) ||
((c1 == c2) && (b1 < b2)) ||
((c1 == c2) && (b1 == b2) && (a1<a2))
Data a <= Data b = (Data a < Data b) || (Data a == Data b)
Теперь сравнение дат будет корректно для любых знаков:
> Data (31,2,1000) > Data (2,12,2022)
False
> Data (21,1,1) <= Data (1,1,12)
True

30. Другой вариант описания типа Data

data Month = January | February | March | April | May | June | July
| … deriving (Eq,Ord,Show)
data Data1 a b = Data1 (a, b, a) deriving (Eq,Show)
instance (Ord a, Ord b) => Ord (Data1 a b) where
Data1 (a1,b1,c1) < Data1 (a2,b2,c2) = (c1 < c2 ) ||
((c1 == c2) && (b1 < b2)) ||
((c1 == c2) && (b1 == b2) && (a1<a2))
Data1 (a1,b1,c1) <= Data1 (a2,b2,c2) = (c1 < c2 ) ||
((c1 == c2) && (b1 < b2)) ||
((c1 == c2) && (b1 == b2) && (a1<a2)) ||
((c1 == c2) && (b1 == b2) && (a1==a2))

31. Сортировка дат типа Data

data Month = January | February | March | April | May | June | July
| … deriving (Eq,Ord,Show)
data Data1 a b = Data1 (a, b, a) deriving (Eq,Show)
*Main> sort [Data (12,2,2000),Data (1,2,2000),
Data (12,2,1298),Data (24,5,2000)]
[Data (12,2,1298), Data (1,2,2000), Data (12,2,2000),
Data (24,5,2000)]

32. Создание собственного класса «почти равно»

class EqNear a where
eq :: a -> a -> Bool
instance EqNear Int where
eq x y = abs (x-y) < 3 -- "почти равно"
instance EqNear Float where
eq x y = abs (x-y) < 0.1
Внимание!!! Числа в языке Hascell являются перегруженными
– выражение «eq 3 4» вызовет ошибку!
Необходимо явно указывать тип:
*Main> eq (3::Int) (4::Int)
True

33. Создание собственного класса «почти равно»

class EqNear a where
eq :: a -> a -> Bool
instance EqNear Int where
eq x y = abs (x-y) < 3 -- "почти равно"
instance EqNear Float where
eq x y = abs (x-y) < 0.1
При использовании данного метода в функциях, где явно
указан тип переменных, ошибки не будет:
dd1 :: Int
*Main> eq dd1 dd2
dd1 = 3
True
dd2 :: Int
dd2 = 4

34. «почти равно» для бинарных деревьев

data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Show
instance (EqNear a) => EqNear (Tree a) where
eq (Leaf a) (Leaf b) = eq a b
eq (Branch l1 r1) (Branch l2 r2) = (eq l1 l2) && (eq r1 r2)
tree1,tree2 :: Tree Int
tree1 = Branch (Leaf 1) (Branch (Leaf 2) (Leaf 3))
tree2 = Branch (Leaf 2) (Branch (Leaf 2) (Leaf 2))
*Main> eq tree1 tree2
True

35. Некоторые ограничения на декларацию экземпляров

Не может быть больше одной декларации для данной
комбинации типа данных и класса;
Если тип декларируется как экземпляр какого-то класса, то
он становится экземпляром всех суперклассов этого
класса;
Декларация экземпляра не обязана содержать описание
метода для каждого оператора данного класса. Если метод
не описан в декларации экземпляра и отсутствует его
описание по умолчанию, то при его вызове может
возникнуть ошибка исполнения.
English     Русский Правила