Ciele
- Vyskúšať prácu s vlastnou dátovou štruktúrou.
- Naučiť sa definovať vlastné algebraické údajové typy a funkcie na prácu s nimi.
- Naučiť sa pracovať so štruktúrami definovanými pomocou record syntaxe.
Postup
Krok 1: Vlastné dátové typy
Majme dátový typ Date pre reprezentáciu dátumov v tváre rok–mesiac–deň:
data Date = Date Int Int Int
deriving Show
today = Date 2020 10 14
Úloha 1.1
Definujte funkciu isLastInMonth a pomocou nej funkciu nextDay:
isLastInMonth :: Date -> Bool
nextDay :: Date -> Date
Poznámka
Využite funkciu isLeapYear z cvičenia 1.
Krok 2: Algebraické dátové typy
Haskell podporuje algebraické dátové typy, takže dátový typ môže mať niekoľko konštruktorov s rôznymi typmi parametrov.
Poznámka
Konštruktor nie je to isté, ako názov typu, no oba sa môžu volať rovnako, hlavne ak je konštruktor iba jeden.
Definujme vlastný typ pre tvary, konkrétne obdĺžnik a kruh:
data Point = Point Double Double
deriving (Eq, Show)
data Shape = Circle Point Double
| Rectangle Point Point
deriving Show
smallCircle = Circle (Point 0 0) 1
largeCircle = Circle (Point 0 0) 10
someRectangle = Rectangle (Point 0 0) (Point 10 5)
aSquare = Rectangle (Point 0 0) (Point 10 10)
shapes = [smallCircle, largeCircle, someRectangle, aSquare]
Konštuktory typu sa dajú použiť aj pri porovnávaní vzorov na ľavej strane definície funkcie:
area :: Shape -> Double
area (Circle _ r) = pi * r^2
area (Rectangle (Point x1 y1) (Point x2 y2)) = (abs (x2-x1)) * (abs (y2-y1))
Úloha 2.1
Definujte funkciu perimeter, ktorá vypočíta obvod zadaného útvaru:
perimeter :: Shape -> Double
Úloha 2.2
Definujte funkciu move, ktorá posunie útvar o zadaný vektor:
data Vector = Vec Double Double
deriving (Eq, Show)
move :: Vector -> Shape -> Shape
Krok 3: Zložitejšie štruktúry
Haskell umožňuje priradiť mená zložka definovanej dátovej štruktúry. Takémuto zápisu sa hovorí record syntax.
data Person = Person
{ name :: String
, age :: Int
} deriving (Eq, Show)
Haskell automaticky vygeneruje funkcie name a age, ktoré vrátia hodnoty zodpovedajúcich polí štruktúry. Napríklad:
name (Person "John" 30) == "John"
Môžeme tiež upravovať hodnoty jednotlivých polí. Výsledkom je však vždy nová štruktúra, v ktorej hodnoty ostatných polí sú skopírované. Napríklad:
john = Person "John" 30
olderJohn = john { age = 31 }
Majme dátové typy pre reprezentáciu knih v knižnici a ich stavu:
data LoanStatus
= Available
| Borrowed String -- meno osoby, ktorá si knihu požičala
deriving (Eq, Show)
data Book = Book
{ title :: String
, author :: String
, loanStatus :: LoanStatus
} deriving (Eq, Show)
Úloha 3.1
Implementujte funkcie pre manipuláciu s knihami:
availableTitles, ktorá vráti zoznam názvov kníh, ktoré sú dostupnéborrowedBy, ktorá vráti knihy, vypožíčané zadanou osobouborrowBook– vráti knihu s nastaveným stavom vypožičania (alebo vráti bez zmeneny, ak už je vypožičaná)returnBook– vrati knihu so stavom zmeneným naAvailable
availableTitles :: [Book] -> [String]
borrowedBy :: String -> [Book] -> [Book]
borrowBook :: String -> Book -> Book
returnBook :: Book -> Book
V niektorých prípadoch potrebujeme, aby funkcia nemusela vždy vratiť výsledok. Napríklad, ak chceme vypožičať knihu, ktorá už je vypožičaná, tak je to neplatná operácia. Pre tento prípad sa nám hodí typ Maybe, ktorý je podobný Optional v jazyku Java.
Úloha 3.2
Zmeňte typ návratovej hodnoty funkcie borrowBook na Maybe Book. V prípade, ak je kniha už vypožičaná, funkcia vratí Nothing, inak vráti Just s novou hodnotou knihy.