Prezentacja o Haskell u(rozdział 3 i 4)

Podobne dokumenty
Składnia funkcji i Rekurencja w języku Haskell

- nawiasy kwadratowe oznaczają, że to lista

Typy, klasy typów, składnie w funkcji

HASKELL 2 R O Z D Z I A Ł 4 SKŁADNIA W FUNKCJACH R O Z D Z I A Ł 5 REKURENCJA. Learn You a Haskell for Great Good! Miran Lipovac

Podstawy Programowania Podstawowa składnia języka C++

LibreOffice Calc VBA

Języki i Paradygmaty Programowania

1 Podstawy c++ w pigułce.

Podstawy programowania funkcjonalnego

1 Podstawy c++ w pigułce.

REKURENCJA W JĘZYKU HASKELL. Autor: Walczak Michał

Podstawą w systemie dwójkowym jest liczba 2 a w systemie dziesiętnym liczba 10.

Język skryptowy: Laboratorium 1. Wprowadzenie do języka Python

Po uruchomieniu programu nasza litera zostanie wyświetlona na ekranie

4. Funkcje. Przykłady

Bloki anonimowe w PL/SQL

Laboratorium Wstawianie skryptu na stroną: 2. Komentarze: 3. Deklaracja zmiennych

Podstawy Programowania C++

C++ wprowadzanie zmiennych

Programowanie funkcyjne (Haskell Wprowadzenie) Kowalik Adrian

Jak napisać program obliczający pola powierzchni różnych figur płaskich?

Bash - wprowadzenie. Bash - wprowadzenie 1/39

Część 4 życie programu

Swift (pol. jerzyk) nowy język programowania zaprezentowany latem 2014 r. (prace od 2010 r.)

MATERIAŁY DO ZAJĘĆ II

Odczyt danych z klawiatury Operatory w Javie

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Opis: Instrukcja warunkowa Składnia: IF [NOT] warunek [AND [NOT] warunek] [OR [NOT] warunek].

Podstawy i języki programowania

Podstawy języka C++ Maciej Trzebiński. Praktyki studenckie na LHC IFJ PAN. Instytut Fizyki Jądrowej Polskiej Akademii Nauk. M. Trzebiński C++ 1/16

Podstawy języka C++ Maciej Trzebiński. Instytut Fizyki Jądrowej Polskiej Akademii Nauk. Praktyki studenckie na LHC IVedycja,2016r.

Wstęp do programowania. Różne różności

Haskell Moduły Ładowanie

Naukę zaczynamy od poznania interpretera. Interpreter uruchamiamy z konsoli poleceniem

3. Instrukcje warunkowe

Operacje wykonywane są na operandach (argumentach operatorów). Przy operacji dodawania: argumentami operatora dodawania + są dwa operandy 2 i 5.

Paradygmaty programowania

Instrukcje warunkowe i skoku. Spotkanie 2. Wyrażenia i operatory logiczne. Instrukcje warunkowe: if else, switch.

Java. język programowania obiektowego. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak

C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów. C++ - przeciążanie operatorów

Rekurencja (rekursja)

Python wprowadzenie. Warszawa, 24 marca PROGRAMOWANIE I SZKOLENIA

C++ Przeładowanie operatorów i wzorce w klasach

Definicje wyższego poziomu

Warunek wielokrotnego wyboru switch... case

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

ZMIENNE. Podstawy PHP

PARADYGMATY PROGRAMOWANIA Wykład 4

Przykład 1: Funkcja jest obiektem, przypisanie funkcji o nazwie function() do zmiennej o nazwie funkcja1

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Języki i techniki programowania Ćwiczenia 2

Programowanie - wykład 4

Struktura pliku projektu Console Application

Swift (pol. jerzyk) nowy język programowania zaprezentowany latem 2014 r. (prace od 2010 r.)

Proste programy w C++ zadania

LABORATORIUM 3 ALGORYTMY OBLICZENIOWE W ELEKTRONICE I TELEKOMUNIKACJI. Wprowadzenie do środowiska Matlab

Informacja o języku. Osadzanie skryptów. Instrukcje, komentarze, zmienne, typy, stałe. Operatory. Struktury kontrolne. Tablice.

Kiedy i czy konieczne?

Podstawy i języki programowania

Elementy języka Haskell

Programowanie strukturalne i obiektowe. Funkcje

Celem tego projektu jest stworzenie

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Metody numeryczne Laboratorium 2

Stałe, znaki, łańcuchy znaków, wejście i wyjście sformatowane

W dowolnym momencie można zmienić typ wskaźnika.

Algorytmy i struktury danych

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

W przeciwnym wypadku wykonaj instrukcję z bloku drugiego. Ćwiczenie 1 utworzyć program dzielący przez siebie dwie liczby

Paradygmaty programowania

Języki programowania Haskell

Programowanie. Lista zadań nr 15. Na ćwiczenia 11, 19 i 23 czerwca 2008

Programowanie komputerowe. Zajęcia 1

Operatory cd. Relacyjne: ==!= < > <= >= bool b; int i =10, j =20; dzielenie całkowitych wynik jest całkowity! Łączenie tekstu: + string s = "Ala ma ";

Funkcje w PL/SQL Funkcja to nazwany blok języka PL/SQL. Jest przechowywana w bazie i musi zwracać wynik. Z reguły, funkcji utworzonych w PL/SQL-u

Szablony funkcji i szablony klas

Wskaźnik może wskazywać na jakąś zmienną, strukturę, tablicę a nawet funkcję. Oto podstawowe operatory niezbędne do operowania wskaźnikami:

2 Przygotował: mgr inż. Maciej Lasota

Algorytmy i złożoności. Wykład 3. Listy jednokierunkowe

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

Wstęp do programowania

Podstawy JavaScript ćwiczenia

Szablony klas, zastosowanie szablonów w programach

ISO/ANSI C - funkcje. Funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje

Programowanie funkcyjne wprowadzenie Specyfikacje formalne i programy funkcyjne

Podstawy Programowania.

Java Podstawy. Michał Bereta

Cw.12 JAVAScript w dokumentach HTML

Algorytmy i struktury danych. wykład 1

Wstęp do Programowania, laboratorium 02

JAVAScript w dokumentach HTML (1) JavaScript jest to interpretowany, zorientowany obiektowo, skryptowy język programowania.

Wstęp do wiadomości teoretycznych (nie, nie jest to masło maślane ani wstęp, wstępów proszę cierpliwie czytać)

1 Obsługiwane funkcje wyzwalaczy

Podstawy programowania skrót z wykładów:

Analiza algorytmów zadania podstawowe

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Pytania sprawdzające wiedzę z programowania C++

Programowanie proceduralne INP001210WL rok akademicki 2018/19 semestr letni. Wykład 6. Karol Tarnowski A-1 p.

Transkrypt:

9 marca 2017

Spis treści 1 2

Wprowadzenie Każda wartość jak i funkcja ma w haskellu ściśle określony typ. Jawne definiowanie typów nie jest konieczne, ponieważ Haskell sam rozpoznaje typ wartości. Warto jednak jawnie określać typ wyrażenia, ponieważ bardzo ułatwia to analizę i dokumentację kodu oraz usuwanie błędów. Najprostszym sposobem na poznanie typów jest użycie polecenia interpretera, dzięki któremu możemy sprawdzić typ dowolnego wyrażenia. Poleceniem tym jest :type, lub krócej :t. ( :: jest odczytywany jako, jest typu ) 1. Prelude>:t "witaj" "witaj"::[char] 2. Prelude>:t e e ::Char 3. Prelude>let i=5 Prelude>:t i i::integer 4. Prelude>lez z=5.6 Prelude>:t z z::double

Wprowadzenie Podstawowe typy w haskell: Int - Liczby całkowite z zakresu [-2^29.. 2^29-1] Integer - Wartością Integer może być dowolna liczba całkowita (zarówno ujemna jak i dodatnia) Float - Liczba zmiennoprzecinkowa pojedynczej precyzji Double - Liczba zmiennoprzecinkowa podwójnej precyzji Bool - Podobnie jak w innych językach, do reprezentowania zmiennych logicznych Char Typ znakowy Dodatkowo mamy również typ wyliczeniowy posiadający trzy wartości: Np. LT - mniejszy niż EQ równy GT większy niż Prelude> compare 1 2 LT

Wprowadzenie Nie tylko zmienne, ale również funkcje mają w Haskellu swój typ. Na typ funkcji składają się typy przyjmowanych przez nią parametrów oraz typ wartości zwracanej przez funkcję. Typy te podajemy w następujący sposób: Nazwa_funkcji :: TypParametru1 -> TypParamentru2 ->... -> TypParametru_n -> TypWartosciZwracanej Np. Dodawanie :: Double -> Double -> Double

Klasy typów Dla typów polimorficznych często zachodzi potrzeba ograniczenia klasy typów w zależności od kontekstu zastosowań. Predefiniowane klasy typów: Num - klasa której członkowie mogą zachowywać się jak liczby. ghci> :t 20 20 :: (Num t) => t ghci> 20 :: Int 20 ghci> 20 :: Integer 20 ghci> 20 :: Float 20.0 ghci> 20 :: Double 20.0

Klasy typów Eq - klasa typów, dla których zdefiniowane jest porównywanie (operatory == i /=) ghci> :t (==) (==) :: (Eq a) => a -> a -> Bool Tak zdeklarowany typ funkcji == oznacza, że funkcja porównania pobiera na wejściu dwie wartości tego samego typu i zwraca wartość typu Bool, gdzie obie wartości wejściowe muszą należeć do klasy typów Eq. => Symbol ten oznacza, że wszystko przed nim nazywamy ograniczeniem klasy ghci> 5 /= 5 False ghci> a == a True ghci> "Ho Ho" == "Ho Ho" True ghci> 3.432 == 3.432 True

Klasy typów Show - klasa typów, których wartości można wypisać na ekranie ghci> show 3 "3" ghci> show 5.334 "5.334" ghci> show True "True" 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. ghci> [ a.. e ] "abcde" ghci> [LT.. GT] [LT,EQ,GT] ghci> [3.. 5] [3,4,5] ghci> succ B C

Klasy typów 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. Aby typ należał do klasy Ord, musi należeć do klasy Eq. ghci> :t (>) (>) :: (Ord a) => a -> a -> Bool ghci> "Abrakadabra" < "Zebra" True ghci> "Abrakadabra" compare "Zebra" LT ghci> 5 >= 2 True ghci> 5 compate 3 GT

Klasy typów Read - jest pewnego rodzaju przeciwieństwem typu klasy Show. Funkcja Read pobiera string i zwraca typ który jest częścią funkcji Read. ghci> read "True" False True ghci> read "8.2" + 3.8 12.0 ghci> read "5" - 2 3 ghci> read "[1,2,3,4]" ++ [3] [1,2,3,4,3] Wykorzystanie Read do rzutowania wyniku na dany typ: ghci> read "5" :: Int 5 ghci> read "5" :: Float 5.0 ghci> (read "5" :: Float) * 4 20.0 ghci> read "[1,2,3,4]" :: [Int] [1,2,3,4] ghci> read "(3, a )" :: (Int, Char) (3, a )

Dopasowywanie wzorców (pattern matching) W języku Haskell możliwe jest zdefiniowanie kilku różnych ciał funkcji, których wywołanie będzie zależne od konkretnych parametrów odpowiadających zadanym wzorcom. lucky :: ( Integral a) => a -> String lucky 7 = " LUCKY NUMBER SEVEN! " lucky x = " Sorry, you re out of luck, pal!" W takim wypadku wszystkie możliwości są sprawdzane od góry do dołu, i kiedy podany na wejściu parametr odpowiada jednemu z wzorców, wtedy zostaje wywołane odpowiadające mu ciało funkcji. Widzimy w przykładzie, że w przypadku wywołania funkcji lucky z parametrem 7, funkcja zwróci nam stringa LUCKY NUMBER SEVEN!. Ważna jest możliwość obsługiwania wszystkich możliwości, dlatego więc dla dowolnej innej niż 7 liczby wprowadzonej na wejściu zostanie wypisane " Sorry, you re out of luck, pal!" Ponieważ Haskell sprawdza możliwości zaczynając od góry, w przypadku użycia takiego wzorca musimy umieścić go na końcu kodu. W innym wypadku wzorce znajdujące się po nim nie zostaną w ogóle sprawdzone

Dopasowywanie wzorców (pattern matching) tarcza : : ( Integral a ) => a - > String tarcza x = Nie trafiles. tarcza 1 = Trafiles 1 punkt. tarcza 2 = Trafiles 2 punkt. tarcza 3 = Trafiles 3 punkt. tarcza 4 = Trafiles 4 punkt. tarcza 5 = Trafiles 5 punkt. W tym przypadku bez znaczenia jaki wzorzec wywołamy, z każdym razem otrzymamy komunikat Nie trafiles ponieważ wzorzec z parametrem x jest na samym początku. Przy dopasowaniu wzorców możemy oczywiście używać rekursji. Oto przykład: silnia :: ( Integral a ) => a -> a silnia 0 = 1 silnia n = n * silnia ( n - 1) Na początku ustalamy, że dla parametru 0 silnia wynosi 1. Następnie dodajemy wzorzec dla dowolnej innej liczby zgodnie z definicją silni.

Dopasowywanie wzorców (pattern matching) Jeżeli nie przewidzimy możliwości występowania dowolnego parametru. W przypadku wywołania innego wzorca niż podane program zwróci błąd. Rozważmy następujący przykład: charname :: Char -> String charname a = " Albert " charname b = " Broseph charname c = " Cecil " Teraz gdy wywołamy nieznany parametr (h), zostanie zwrócony błąd. ghci > charname a " Albert " ghci > charname b " Broseph " ghci > charname h " *** Exception : tut. hs :(53,0) -(55,21): Non - exhaustive patterns in function charname

Dopasowywanie wzorców (pattern matching) Wzorce można także stosować na krotkach. Poniższy przykład ilustruje funkcję, która dodaje do siebie wektory. addvectors :: ( Num a) = > (a, a) -> (a, a) -> (a, a) addvectors (x1, y1 ) (x2, y2 ) = ( x1 + x2, y1 + y2 ) Tak skonstruowana funkcja przyjmuje na wejście dowolne zmienne i zwraca wynik w postaci dwóch par liczb (wektorów). Jest również możliwe stosowanie wzorców na listach. W tym przykładzie wyszukiwane i sumowane są elementy listy odpowiadające zadanemu wzorcowi (a, b). ghci > let xs = [(1,3), (4,3), (2,4), (5,3), (5,6), (3,1)] ghci > [a+ b (a, b) <- xs ] [4,7,6,8,11,4] W przypadku kiedy dopasowanie wzorca jest niemożliwe, funkcja przechodzi do następnego elementu listy.

Dopasowywanie wzorców (pattern matching) Dopuszczalne jest także użycie listy pustej w określaniu wzorca. Tu na przykładzie zmodyfikowanej funkcji head: head :: [a] -> a head [] = error " Can t call head on an empty list, dummy!" head (x: _) = x I działa to następująco ghci > head [4,5,6] 4 ghci > head " Hello " H Możemy też zastosować rekurencje, w zmodyfikowanej wersji funkcji sum, która dodaje do siebie wszystkie elementy listy: sum :: ( Num a) => [a] -> a sum [] = 0 sum (x: xs ) = x + sum xs

Dopasowywanie wzorców (pattern matching) Można również za pomocą wzorca odwołać się do całości dowolnej listy, bez potrzeby przepisywania w każdym miejscu jej nazwy lub definicji. W tym celu stosuje się znak "@" wstawiany bezpośrednio przed wzorcem capital :: String -> String capital "" = " Empty string, whoops!" capital all@ (x : xs ) = " The first letter of " ++ all ++ " is " ++ [ x]

Osłony (guards) Osłony spełniają taką samą funkcję jak wyrażenie if służą do stwierdzenia czy postawiony warunek został spełniony. W odróżnieniu jednak od składni if, osłony są bardziej czytelne przy dużej liczbie warunków dla jednego wyrażenia oraz dobrze współgrają z haskellowymi wzorcami. bmitell :: ( RealFloat a) => a -> String bmitell bmi bmi <= 18.5 = " You re underweight, you emo, you!" bmi <= 25.0 = " You re supposedly normal. Pffft, I bet you re ugly!" bmi <= 30.0 = " You re fat! Lose some weight, fatty!" otherwise = " You re a whale, congratulations!" Osłony oznacza się znakiem " ", każda osłona jest wyrażeniem typu boolean przyjmującym wartość True lub False. Znajdujące się na końcu wyrażenie otherwise jest odpowiednikiem else znanego z konstrukcji if-else. Podany powyżej przykład w zależności od podanego przez nas BMI wyświetla odpowiedni tekst. Oczywiście do poprawnego działania, konieczne jest aby wzorce były ustawione w kolejności rosnącej.

Osłony (guards) Możemy też łatwo zmodyfikować naszą funkcję i stworzyć kalkulator BMI. Wystarczy, że zamiast wartości BMI na wejściu będziemy podawać nasz wzrost i wagę a reszta obliczy się sama. Wygląda to następująco: bmitell :: ( RealFloat a) => a -> a -> String bmitell weight height weight / height ^ 2 <= 18.5 = " You re underweight, you emo, you!" weight / height ^ 2 <= 25.0 = " You re supposedly normal. Pffft, I bet you re ugly!" weight / height ^ 2 <= 30.0 = " You re fat! Lose some weight, fatty!" otherwise = " You re a whale, congratulations!" Inny przykład zastosowania: max :: ( Ord a) => a -> a -> a max a b a > b = a otherwise = b W tym przypadku osłony wykorzystujemy do znajdowania większej liczby. Jak widzimy jest to tą metodą bardzo proste.

Klauzula where Klauzula where jest to miejsce, w którym wyrażeniom lub wartościom przypisuje się nazwę, tworząc stałe. Umieszcza się ją po osłonach. Jest przydatna, gdy funkcja zawiera obliczenia, które muszą być kilkakrotnie powtarzane. Aby zrozumieć kiedy ją używać weźmy nasz kalkulator BMI pokazany wcześniej. bmitell :: ( RealFloat a) => a -> a -> String bmitell weight height weight / height ^ 2 <= 18.5 = " You re underweight, you emo, you!" weight / height ^ 2 <= 25.0 = " You re supposedly normal. Pffft, I bet you re ugly!" weight / height ^ 2 <= 30.0 = " You re fat! Lose some weight, fatty!" otherwise = " You re a whale, congratulations!" Widzimy tutaj, że przy każdej osłonie musimy na nowo liczyć BMI i pisać weight / height ^ 2. I tutaj właśnie z pomocą przychodzi nam klauzula where, która znacząco uprości nam cały zapis. Po zmianie nasza funkcja wygląda tak: bmitell :: ( RealFloat a) => a -> a -> String bmitell weight height bmi <= 18.5 = " You re underweight, you emo, you!" bmi <= 25.0 = " You re supposedly normal. Pffft, I bet you re ugly!" bmi <= 30.0 = " You re fat! Lose some weight, fatty!" otherwise = " You re a whale, congratulations!" where bmi = weight / height ^ 2 Wszystko co musieliśmy zrobić to tylko na końcu dodać linijkę where bmi = weight / height ^ 2.

Klauzula where Możemy pójść o krok dalej i przekształcić naszą funkcję w następujący sposób: bmitell :: ( RealFloat a) => a -> a -> String bmitell weight height bmi <= skinny = " You re underweight, you emo, you!" bmi <= normal = " You re supposedly normal. Pffft, I bet you re ugly! " bmi <= fat = " You re fat! Lose some weight, fatty! " otherwise = " You re a whale, congratulations!" where bmi = weight / height ^ 2 skinny = 18.5 normal = 25.0 fat = 30.0 Wszystkie wartości zdefiniowane w where są widoczne tylko w obrębie danej funkcji. Sekcja where pozwala również na dopasowywanie wzorców. Musimy wtedy przepisać sekcję where naszej poprzedniej funkcji w następujący sposób:... where bmi = weight / height ^ 2 ( skinny, normal, fat ) = (18.5, 25.0, 30.0) Lepiej jednak robić to, definiując ciała funkcji, ale ten przykład pokazuje, że takie coś jest możliwe.

Klauzula where Tak jak wcześniej definiowaliśmy za pomocą where stałe, istnieje również możliwość definiowania funkcji. Stwórzmy funkcję, która przyjmuje listę par (waga, wzrost) i dla każdej takiej pary zwraca BMI. calcbmis :: ( RealFloat a ) = > [(a, a )] -> [a] calcbmis xs = [ bmi w h (w, h) <- xs ] where bmi weight height = weight / height ^ 2

Klauzula let Jest podobna do klauzuli where. Jest jednak między nimi istotna różnica. Otóż where jest tylko częścią składni i nie może istnieć samodzielnie, natomiast klauzula let jest wyrażeniem, więc możemy używać jej gdziekolwiek zechcemy. Poniżej przykład: cylinder :: ( RealFloat a) =& gt ; a -& gt ; a -& gt ; a cylinder r h = let sidearea = 2 * pi * r * h toparea = pi * r ^2 in sidearea + 2 * toparea Podana funkcja policzy nam pole powierzchni walca. Let możemy także użyć aby wstawić funkcję do listy: ghci > [ let square x = x * x in ( square 5, square 3, square 2)] lub rozbić krotkę na komponenty i przypisanie im nazw: ghci > ( let (a,b,c ) = (1,2,3) in a+b +c) * 100 Dzięki let unikniemy tworzenia funkcji pomocniczych: calcbmis :: ( RealFloat a) = > [(a, a )] -> [a] calcbmis xs = [ bmi (w, h) <- xs, let bmi = w / h ^ 2] Jak widać let zostało użyte od razu przy tworzeniu listy.

Klauzula let Kolejne zastosowanie to tworzenie w konsoli zmiennych i funkcji, do których mamy dostęp do momentu zamknięcia konsoli: ghci > let zoot x y z = x * y + z ghci > zoot 3 9 2

Wyrażenie warunkowe case Jest oparte na dopasowywaniu do wzorca i rozszerza je, bo dopasowywanie do wzorca mogło być użyte tylko w definicji funkcji, a wyrażeń warunkowych możemy użyć tam, gdzie to potrzebne. head :: [a] -> a head xs = case xs of [] -> error " No head for empty lists!" (x: _) -> x Case nie jest skomplikowany, używamy go następująco: case expression of pattern -> result pattern -> result pattern -> result...

Wyrażenie warunkowe case Tutaj kolejny prosty przykład wykorzystania case describelist :: [a ] -> String describelist xs = " The list is " ++ case xs of [] -> " empty."