Písomka, dátové typy

Ciele

  1. Vyskúšať prácu s vlastnou dátovou štruktúrou.
  2. Naučiť sa definovať vlastné algebraické údajové typy a funkcie na prácu s nimi.
  3. 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 osobou
  • borrowBook – 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 na Available
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.