Haskell/Typy definiowane przez użytkownika

Typy definiowane przez użytkownika

edytuj

Haskell dostarcza trzy sposoby definiowania nowych typów:

  • data
  • type
  • newtype

Mechanizm synonimów (type) został opisany w rozdziale Typy danych. newtype to mechanizm wykorzystywany głownie do optymalizacji. W tym rozdziale przedstawione zostanie wykorzystanie słowa kluczowego data, dzięki któremu możemy definiować struktury oraz typy wyliczeniowe.

Przykład prostego typu wyliczeniowego

edytuj

Przykładem prostego typu wyliczeniowego może być typ Sygnalizacja opisujący kolory sygnalizacji świetlnej:

data Sygnalizacja = Czerwone | Zolte | Zielone

Sygnalizacja jest tutaj konstruktorem typu, natomiast Czerwone, Zolte i Zielone to konstruktory wartości. Zarówno konstruktor typu, jak i konstruktory wartości w tym przypadku nie przyjmują parametrów.

Typ Sygnalizacja możemy użyć w funkcji coRobic:

coRobic::Sygnalizacja -> String
coRobic Czerwone = "Stój"
coRobic Zolte = "Uważaj"
coRobic Zielone = "Jedź"

Złożone typy definiowane przez użytkownika

edytuj

Możemy tworzyć bardziej skomplikowane struktury danych, na przykład typ opisujący produkty w sklepie:

 data Produkty =
    Ksiazka String String Int Double     --tytul, autor, liczba stron, cena
    | Gazeta String Int Double           --tytuł, numer, cena
    | Zeszyt Int Double                  --liczba kartek, cena

Po utworzeni struktury można definiować zmienne typu Produkty. Konstruktor typu Produkty podobnie jak w poprzednim przykładzie nie wymaga podania parametrów, natomiast konstruktory Ksiazka, Gazeta oraz Zeszyt wymagają podania odpowiednich zestawów parametrów (odpowiednich dla każdej wartości)

 p1 :: Produkt
 p1 = Zeszyt 60 2.3

 p2 :: Produkt
 p2 = Gazeta "Ciekawa gazeta" 123 8.5

 p3 :: Produkt
 p3 = Ksiazka "Haskell to fajny jezyk" "Jas Kowalski" 450 70

Ponieważ Książka, Gazeta i Zeszyt są tego samego typu, można utworzyć funkcję, która będzie przyjmowała dowolny Produkt, na przykład funkcję zwracającą opis produktu:

 pokazProdukt :: Produkt -> String
 
 pokazProdukt (Ksiazka tytul autor l_stron cena) = tytul ++ "; autor: " ++ autor ++ "; "  ++ show 
 l_stron ++ " stron; " ++ show cena ++ "PLN"
 
 pokazProdukt (Gazeta tytul numer cena) = tytul ++ "; numer: " ++ show numer ++ "; " ++ show cena 
 ++ "PLN"
 
 pokazProdukt (Zeszyt l_kartek cena) = "Zeszyt " ++ show l_kartek ++ " kartkowy; " ++ show cena ++
 "PLN"

Typy rekursywne

edytuj

Haskell pozwala na definicję rekursywnych typów danych (takich jak drzewo, kolejka itp.)

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

Jest to definicja polimorficznej struktury danych reprezentującej drzewo binarne. Element typu Tree mogą być:

  • liśćmi (Leaf) - przechowują wartość typu a
  • gałęziami (Branch) - składają się z dwóch innych drzew (które mogą być liśćmi lub innymi gałęziami)

Ponieważ jest to typ polimorficzny, konstruktor tego typu wymaga podania parametru. W tym przypadku jest to typ wartości przechowywanej w liściach drzewa. Również konstruktory Leaf oraz Branch wymagają podania odpowiedniego zestawu parametrów.

Zdefiniujmy teraz funkcję działającą na drzewie. Funkcja ta powinna odczytać wszystkie wartości przechowywane w liściach drzewa i zwrócić je w postaci listy:

fringe :: Tree a -> [a]
fringe (Leaf x) = [x]
fringe (Branch left right) = fringe left ++ fringe right

Funkcja wywołana z parametrem będącym liściem zwraca listę jednoelementową zawierającą wartość przechowywaną w liściu. Funkcja wywołana z parametrem będącym gałęzią wywołuje rekurencyjnie tą samą funkcję w stosunku do dwóch swoich poddrzew oraz łączy listy uzyskane z tych wywołań.