Pobierz prezentację
Pobieranie prezentacji. Proszę czekać
1
Typy i typy klas
2
Haskell jest statycznym typem języka, tzn
Haskell jest statycznym typem języka, tzn., że wszystkie typy i wyrażenia są znane w czasie kompilacji – podnosi to bezpieczeństwo kodu. Jeśli w programie będziesz chciał podzielić zmienną typu bool (prawda albo fałsz) przez liczbę cąłkowitą, program się nie skompiluje.
3
Nie trzeba podawać typów zmiennych i argumentów funkcji
Nie trzeba podawać typów zmiennych i argumentów funkcji. Haskell wywnioskuje to za nas z kontekstu. Jednak pomimo tego, zrozumienie typów jest bardzo ważne.
4
Typy są pewnego rodzaju etykietami które posiada każde wyrażenie
Typy są pewnego rodzaju etykietami które posiada każde wyrażenie. Informuje nas to, do której kategorii pasuje wyrażenie. Przykładowo wartość true należy do typu boolean, a wartość „Hello” jest typu string.
6
Za pomocą polecenia :t możemy sprawdzić typ wyrażenia.
Nazwy wszystkich typów zaczynają się dużą literą. Nawiasy kwadratowe oznaczają, że to lista, w tym przypadku lista
7
Funkcje również posiadają typy
Funkcje również posiadają typy. Podczas pisania własnych funkcji możemy wybrać z góry określony jej typ. Taki zabieg jest zazwyczaj bardzo dobry, z wyjątkiem bardzo krótkich funkcji.
9
Zapis [Char] -> [Char] oznacza, że będziemy mapować listę znaków Char na inną listę Char. Znaczy to że do funkcji będziemy wprowadzać zmienną typu Char, a jako jej rezultat otrzymamy inną zmienną typu Char. Z racji tego że Char jest synonimem typu String, zapis ten można było by zmienić na String -> String
10
W powyższym przykładzie nie musieliśmy wpisywać typu, gdyż kompilator sam by go rozpoznał. Jeśli jednak chcieli byśmy stworzyć funkcję z wieloma parametrami, będziemy musieli podać typy dla każdej zmiennej.
11
Parametry są oddzielone od siebie znakiem -> i nie ma żadnego specjalnego wyróżnienia dla parametrów wejściowych a wyniku funkcji. Wynik jest zawsze na ostatniej pozycji.
12
Jeżeli piszesz własną funkcję i nie jesteś pewien jaki typ powinieneś zadeklarować, wystarczy pominąć ten krok, a następnie sprawdzić jej typ poleceniem :t. Funkcje również są wyrażeniami, i bez żadnego problemu możemy zastosować polecenie :t.
13
Int – są to liczby całkowite z przedziału od -2147483648 do 2147483647
Integer – są to również liczby całkowite, z tą jednak różnicą że nie posiadają granicy. Są nieskończone. Float – są to liczby rzeczywiste z pojedynczą precyzją.
14
Double – liczby rzeczywiste z podwójną precyzją.
Bool – zmienna logiczna przyjmująca wartość true albo false. Char – pojedynczy znak. Zapisuje się ją za pomocą pojedynczych ’. String – jest to lista znaków Char. Zapisujemy je zapocą ”.
15
Zmienne typy gdy zapytamy co pobiera i zwraca funkcja wbudowana head, otrzymamy: Oznacza to, że nazwy typów zaczynają się z wielkiej litery, więc a nie określa typu. Zapis ten oznacza, że funkcja head pobiera listę dowolnych elementów i zwraca jeden z nich (w tym przypadku akurat pierwszy)
16
Zmienne typy w Haskell-u są tym czym są typy generyczne w innych językach.
Funkcje zawierające zmienne typy są nazywane funkcjami polimorficznymi. Zmienne tych funkcji mogą posiadać zmienne o nazwach dłuższych niż jednoliterowe, ale przyjęło się, że używa się właśnie nazw o długości jednej litery.
17
funkcja fst pobiera dwuelementową krotkę i zwraca element który jest tego samego typu co pierwszy element krotki. Elemeny a i b mogą, ale nie musza być tego samego typu.
18
W Haskellu operatory ==, +,. , -, / i wiele innych to funkcje
W Haskellu operatory ==, +, *, -, / i wiele innych to funkcje. Jeżeli funkcja zawiera jedynie znaki specjalne, domyślnie jest funkcją infiksową. Jeżeli chcemy sprawdzić na jakich typach pracuje musimy przekazać ją do innej funkcji lub uczynić tę funkcję prefiksową czyli musimy nazwę tej funkcji zawrzeć w nawiasach.
19
Wszystko co występuje przed symbolem => nazywane jest klasą ograniczeń.W przypadku funkcji == możemy to oczytać jako: funkcja równości pobiera dwa elementy tego samego typu należące do klasy obiektów (typeclass) Eq i zwraca typ Bool.
20
Typeclass Eq udostępnia interfejs do testowania równości
Typeclass Eq udostępnia interfejs do testowania równości. Każdy typ dla którego ma sens porównanie dwóch wartości powinien należeć do klasy Eq. Standard języka Haskell wymaga by wszystkie typy i funkcje, z wyjątkiem IO, należały do klasy Eq. Dlatego możemy wykonywać takie porównania:
21
Typeclass Ord- jest dla typów które mają "kolejność"
Typeclass Ord- jest dla typów które mają "kolejność". Funkcje porównujące >, <, >=, <= pobierają dwa argumenty tego samego typu należące do klasy Ord i zwracają obiekt klasy Ordering, tzn. GT, LT lub EQ (greater than, lesser than i equal)Aby typ należał do klasy Ord, musi należeć do klasy Eq.
22
Typeclass Show- klasa obiektów które mogą być zaprezentowane jako tekst.Najczęściej używaną funkcją należącą do klasy typu Show jest funkcja show. Pobiera ona wartość i wyświetla ją jako tekst.
23
Typeclass Read Funkcją o działaniu odwrotnym do show jest funkcja read. Pobiera ona tekst i zwraca typ należący do Read.
24
Czasem jednak są problemy:
Funkcja nie wie co zwrócić stąd komunikat. Zazwyczaj kompilator może "wywnioskować" z kontekstu jakiego rodzaju danych oczekujemy, ale jak widać nie zawsze.
25
Aby pozbyć się błędu musimy zaznaczyć jakiego wyniku oczekujemy:
26
Typeclass Enum - klasa obiektów do którego należą typy, które mogą być wyliczane. W typie tym dostęp do poprzednika udostępnia funkcja pred, a do następnika funkcja succ. Typy które należą do tej klasy to: (), Bool, Char , Ordering, Int, Integer , Float i Double.
27
Typeclass Bounded - do tej klasy należą typy mające wartości graniczne
Typeclass Bounded - do tej klasy należą typy mające wartości graniczne. Funkcja minBoundzwraca minimalną wartość danego typu,a funkcja maxBoundzwraca maksymalną wartość danego typu. Wszystkie krotki również należą do tej klasy.
28
Typeclass Num -klasa której członkowie mogą zachowywać się jak liczby.
Okazuje się, że funkcja ta akceptuje wszystkie typy należące do klasy Num. Pobiera ona dwie liczby tego samego typu i zwraca liczbę tego samego typu. (5 :: Int) * (6 :: Integer) - wygeneruje błąd, Ale 5 * (6 :: Integer) wykona się ponieważ 5 może działać jak Integer
29
Typeclass Integral - klasa obiektów należąca do klasy Num.
Do Integral należą Int i Integer. Typeclass Floating - typy zmiennoprzecinkowe tzn. Float i Double. Funkcja fromIntegralkonwertuje typy numeryczne na typ Integral. Bardzo użyteczna funkcja jeżeli chcemy używać razem typów zmiennoprzecinkowych z typami całkowitymi.
30
Składnia funkcji
31
lucky :: ( Integral a) => a -> String
lucky 7 = " LUCKY NUMBER SEVEN !" lucky x= "Sorry , you 're out of luck , pal !„ Pierwsza linia to deklaracja funkcji. Mówi ona, że funkcja pobiera wartość całkowitą i zwraca napis. Ta konstrukcja jest równoważna ze switch znanym z innych języków. Program przechodzi przez wszystkie wszystkie linie i sprawdza zgodność. Ostatnia instrukcja jest odpowiednikiem default czyli jeżeli w poprzednich liniach komputer nie znajdzie dopasowania to zostanie wykonana ostatnia instrukcja. W Haskell każda funkcja musi zwrócić wartość.
32
Jeżeli chcieli byśmy napisać funkcję, która posiadała by kilka instrukcji, możemy to zrobić następująco: sayMe :: ( Integral a) => a -> String sayMe 1 = " One ! " sayMe 2 = " Two ! " sayMe 3 = " Three !" sayMe 4 = " Four !" sayMe 5 = " Five !" sayMe x = " Not between 1 and 5"
33
Taki zapis sprawi, że w sytuacji kiedy zostanie wprowadzona liczba z przedziału od 1 do 5, wykona się instrukcja przypisana do danej wartości, a jeżeli zostanie wprowadzona inna liczba, wyświetli się napis „Not between 1 and 5”.
34
W języku Haskell możemy również wykorzystywać rekurencję
W języku Haskell możemy również wykorzystywać rekurencję. Jest ona niezwykle ważna i pomocna. Deklaracja funkcji z użyciem rekurencji wygląda następująco: factorial :: ( Integral a ) => a -> a factorial 0 = 1 factorial n = n * factorial ( n - 1)
35
Linijka factorial 0 = 1 jest warunkiem końcowym, bez niego funkcja wykonywała by się nieskończenie wiele razy. W przypadku kiedy liczba jest większa od 0 zostanie wczytana liczba n, i następnie zostaje wczytana funkcja z argumentem n-1. Będzie się to powtarzać do momentu aż liczba n będzie równa 0.
36
Przydatne jest utrzymywanie jednego stylu pisania funkcji
Przydatne jest utrzymywanie jednego stylu pisania funkcji. Opisywanie wzorów dla poszczególnych warunków najlepiej pisać na początku, natomiast te ogólne, na jej końcu.
37
Dopasowywanie wzorów może jednak się nie udać
Dopasowywanie wzorów może jednak się nie udać. Jeżeli napiszemy funkcję: charName :: Char -> String charName ’a ’ = " Albert " charName ’b ’ = " Broseph " charName ’c ’ = " Cecil " i nie podamy warunku ogólnego, w sytuacji odwołania się do niepodanego wzoru, otrzymamy błąd.
38
Dlatego zawsze powinniśmy dodawać opcję dla pozostałych zmiennych, żeby uniknąć występowania błędów w programie.
39
Dopasowywanie wzorów możemy również stosować z krotkami
Dopasowywanie wzorów możemy również stosować z krotkami. Napiszmy funkcję, pobierająco dwa wektory w przestrzeni 2D i dodajmy je do siebie: addVectors :: ( Num a) = > (a , a) -> (a , a) -> (a , a) addVectors (x1 , y1 ) (x2 , y2 ) = ( x1 + x2 , y1 + y2 )
40
Strażnicy "if" oraz "case" mają swoje odpowiedniki w większości języków programowania. Strażnicy to instrukcja, którą możemy porównać do instrukcji "if" z dodatkowymi warunkami "if else". Strażnicy to kolejne warunki odpowiadające "if else". Aby określić jaka wartość powinna być zwrócona jeśli żaden z warunków nie zostanie spełniony (odpowiednik "else") definiując ostatniego strażnika zamiast warunku należy wpisać "otherwise".
41
Funkcja Max Funkcja max przyjmuje dwie wartości i zwraca większą z nich. Strażnicy mogą być zdefiniowani także w jednej linii. Można również samemu zdefiniować funkcję podobną do funkcji max.
42
Where Klauzula where służy do definiowania wartości i funkcji użytych wewnątrz wyrażeń. Nazwy definiujemy w klauzuli where, gdzie z funkcji są widoczne tylko dla tej funkcji, więc nie musimy się martwić o wystąpienie błędów. Należy jednak umieszczać je w jednej kolumnie, aby Haskell nie pogubił się, czy wszystkie nazwy należą do tego samego bloku funkcji. Jeżeli chcemy używać nazw w wielu innych funkcjach, należy je zdefiniować globalnie.
43
Można także definiować nazwy tak jak na poniższym przykładzie:
Przykład funkcji wyświetlającej inicjały z podanego imienia i nazwiska: Funkcja zwracająca listę par wagi, wysokości i zwracająca listę BMI:
44
Let it be Bardzo podobne do wiązań where są wiązania let. Wiązania where jest składniowym konstruktem, który pozwala powiązać zmienne na końcu funkcji i cała funkcja może je zobaczyć, w tym wszystkich strażników. Wiązania let pozwalają powiązać zmienne w dowolnym miejscu i wyrażają siebie, ale są bardzo lokalne. Tak jak każdy konstrukt w Haskellu, który jest używany do wiązania wartości z nazwami, wiązania let mogą być wykorzystywane do dopasowania wzorców.W ten sposób możemy zdefiniować funkcję, która daje nam pole powierzchni cylindra w oparciu o jego wysokości i promień:
45
Składnia jest let <wiązanie> in <wyrażenie>
Składnia jest let <wiązanie> in <wyrażenie>. Nazwy, które definiujemy w części let są dostępne dla wyrażenia po części in. Jak widać, możemy również zdefiniować to z wiązaniem where. Zauważmy, że nazwy są ustawione w jednej kolumnie. Różnica polega na tym, że wiązanie let wyrażają siebie. Wiązania where to tylko konstrukcje składniowe.
46
Mogą one być również stosowane do wprowadzania funkcji w zasięgu lokalnym:
47
Jeśli chcemy wiązać do kilku zmiennych w linii, to nie możemy wyrównać ich po kolumnach. Dlatego możemy je rozdzielić średnikami
48
Wiązania let są wyrażeniami są dość lokalne w swoim zakresie, nie mogą być wykorzystywane przez strażników. Niektórzy ludzie wolą where wiązanie ponieważ nazwy pochodzą od funkcji, w której były wykorzystywane. W ten sposób, ciało funkcji jest bliżej nazwy i typowi deklaracji i takie, które jest bardziej czytelne.
49
Case expressions Wiele języków (C, C++, Java, itp.) Mają składnie case. W case chodzi o wybraniu zmiennej, a następnie wykonaniu bloków kodu dla określonych wartości tej zmiennej, lub może być blok kodu catch-all w przypadku, gdy zmienna ma jakąś wartość, dla której nie utworzono case.
50
Te dwa fragmenty kodu robią to samo i mogą być stosowane zamiennie:
51
Składnia wyrażeń jest dość prosta:
Expression jest porównywane ze wzorami (pattern). Wzór dopasowania polega na tym, że pierwszy wzór(pattern), który odpowiada expression jest używany. Jeśli przejdzie przez całego case to pojawi się błąd wykonania.
52
Case są przydatne do dopasowywania wzorców w środku wyrażeń.
Podobne prezentacje
© 2024 SlidePlayer.pl Inc.
All rights reserved.