Haskell/Typy definiowane przez użytkownika

Z Wikibooks, biblioteki wolnych podręczników.

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ń.