210 likes | 425 Views
Лекция. типы и классы. Пользовательские типы. Для задания пользовательских типов служит ключевое слово data . Объявим тип Point - точка на плоскости. Если понимать точку как два вещественных числа, то определение типа будет таким. data Point = Point Double Double
E N D
Лекция типы и классы
Пользовательские типы Для задания пользовательских типов служит ключевое слово data. Объявим тип Point- точка на плоскости. Если понимать точку как два вещественных числа, то определение типа будет таким. data Point = Point Double Double data– ключевое слово. Point – имя типа, пишется с большой буквы. Point Double Double– конструктор, создает точку из двух чисел. Первое слово конструктора – метка.
Именованные поля Тип можно объявить так, что отдельные части его значения будут иметь имена. data Point = Point {x::Double, y::Double} point1 = Point {x = 1.5, y = 3.0} или data Book = Book {author::String, title::String, year::Int } book1 = Book {author="Goethe", title="Faust", year=1831} Имена полей одновременно являются и функциями (как свойства в C#). > author faust "Goethe" > title faust "Faust"
Множественные конструкторы Конструкторов у типа может быть много. Они могут быть константами, и тогда тип напоминает перечислитель из C#: • data Color = Red | Green | Blue • dataBool = True | False Конструкторы могут иметь параметры. data Color = Red | Green | Blue | RGB IntIntInt Последний конструктор создает значение цвета из трех целых чисел. Тип, имеющий несколько конструкторов, называют полиморфным.
Параметрические типы Можно объявить и более общий тип – параметрический. Конструктор такого типа создает точки из двух любых однотипных значений. data Point a = Point a a a – тип-параметр (как в шаблонах C#). Point a a– конструктор типа. Он определяет функцию Point:: a->a->Point a которая создает точку из двух однотипных значений. data Point a = Point a a len:: Point Float -> Float len (Point x y) = sqrt (x * x + y * y)
Определение типа Bool Определим самодельный логический тип в файле MyBool.hs. module MyBool where import Prelude hiding (Bool) data Bool = True | False Чтобы пользовательские типы приносили пользу, нужно определять функции для работы с ними.
Функции для типа Bool module MyBool where import Prelude hiding (Bool) data Bool = True | False and:: Bool -> Bool -> Bool and True True = True and _ _ = False not:: Bool -> Bool not True = False not False = True toStr:: Bool -> String toStr True = "T" toStr False = "F" Задача. Определить функцию or.
Ответ: функция or or:: Bool -> Bool -> Bool -- вариант 1 ------------------ or False False = False or _ _ = True -- вариант 2 ------------------ or x y = not (not x `and` not y)
Пример определения типа – Nat Для обоснования непротиворечивости арифметики итальянский математик Джузеппе Пеано использовал числа, которые строятся рекурсивно из 0 при помощи функции Succ: 0 = 0 1= Succ 0 2= Succ (Succ 0) 3= Succ(Succ (Succ 0)) ... data Nat = Zero | Succ Nat deriving Show (+)::Nat->Nat->Nat (+) a Zero = a (+) a (Succ b) = Succ(a + b) Джузеппе Пеано Задача. Определить функцию умножения для Nat.
Ответ: умножение для Nat data Nat = Zero | Succ Nat deriving Show (+)::Nat->Nat->Nat (+) a Zero = a (+) a (Succ b) = Succ(a + b) (*)::Nat->Nat->Nat (*) _ Zero = Zero (*) Zero _ = Zero (*) a (Succ Zero) = a (*) a (Succ b) = a * b + a
Рекурсивные типы Рекурсивные – такие типы, в конструкторах которых присутствует сам определяемый тип. Тип Nat – рекурсивный. Если в конструкторе типа определяемый тип встречается один раз, получаются линейные структуры, если много раз – древовидные. Дадим собственное определение списка. data List a = E | L a (List a) E – означает [] (Empty) L – метка конструктора, подобного (x:xs)
Пример: самодельный список import Prelude hiding ( (++), sum ) data List a = E | L a (List a) deriving Show (%):: a -> List a -> List a -- то же, что (:) (%) x xs = L x xs infixr 5 % (++)::List a -> List a -> List a (++) E xs = xs (++) (L x xs) ys = x % (xs ++ ys) sum :: Num a => List a -> a sum E = 0 sum (L x xs) = x + sum xs -- примеры списков list1 = 1 % 2 % 3 % E list2 = L 1 (L 2 (L 3 E))
Тип Tree – бинарное дерево Каждый узел дерева содержит значение типа aи может ссылаться на два дочерние узла. Пустое дерево будем обозначать Nil. data Tree a = Node a (Tree a) (Tree a) | Nil deriving (Show, Eq) Принадлежность типа к классам Show и Eqозначает, что деревья можно выводить на экран и сравнивать между собой. countNodes:: (Tree a) -> Int countNodes Nil = 0 countNodes (Node _ t1 t2) = 1 + countNodes t1 + countNodes t2 Задача. Объявить функцию копирования дерева copyTree::(Tree a) -> (Tree a)
Ответ copyTree::(Tree a) -> (Tree a) copyTree Nil = Nil copyTree (Node a t1 t2) = Node a (copyTree t1) (copyTree t2)
Арифметическое выражение Арифметические выражения можно однозначно представить в виде дерева. Для простоты ограничимся только целыми числами и операциями сложения и умножения. Дерево выражения (2 + 3) * (4 + 5) • Листьями дерева всегда являются числовые константы, внутренними узлами – арифметические операции. 2 3 4 5 + + * data Expr = C Integer -- константа | A ExprExpr -- сложение | M ExprExpr -- умножение
Арифметическое выражение import Prelude hiding ((+), (*)) import Mock data Expr = C Integer | A ExprExpr -- A - сложение | M ExprExpr -- M - умножение deriving Show e = (M (A (C 2) (C 3)) (A (C 4) (C 5))) eval :: Expr -> Integer eval (C x) = x eval (A x y) = eval x `plus` eval y eval (M x y) = eval x `star` eval y 2 3 4 5 (+) = A (*) = M infixl 4 + infixl 5 * e1 = C 2 + C 3 * C 5 + + *
Класс типов Класс типов эквивалентен понятию "интерфейс" в императивном программировании. Любой интерфейс выделяет среди всех типов те, которые его реализуют. • Пример – класс Show. • Типы из этого класса могут быть показаны в интерпретаторе, т.к. для них реализована функция show • class Show a where • show :: a -> String • -- и др.функции • Класс содержит только объявления функций, их определения находятся в реализациях класса. • Реализуем класс Show для типа Boolих модуля MyBool.
Примеры реализации класса -- Реализация класса Show для типа Bool. instance Show Boolwhere show True = "T" show False = "F" -- Реализация класса Show для типа Point a. instance Show a => Show (Point a) where show (Point x y) = "x=" ++ (show x) ++ " y=" ++ (show y)
Классы из Prelude Show (show) Read (read) Eq ((==), (/=) ) Ord ((<), (<=), (>), (>=), max, min) Bounded (minBound, maxBound) Enum ( succ, pred, …) Num Real Integral Fractional Floating Monad Functor
Вывод реализации Реализацию некоторых классов компилятор способен вывести самостоятельно. Чтобы санкционировать этот вывод, существует ключевое слово deriving. dataBool = True | False deriving (Show, Read, Eq, Ord)
Самостоятельно Для типа Nat определите операции: возведения в степень вычисления факториала преобразования к целому преобразования из целого взятия предыдущего числа (для Zero это должно быть Zero). Для типа Nat реализуйте класс Eq, Ord.