Programowanie w języku Haskell

Podobne dokumenty
Programowanie w języku Haskell

Programowanie w języku Haskell

Elementy języka Haskell

WEJŚCIE/WYJŚCIE HASKELL ŁUKASZ PAWLAK DARIUSZ KRYSIAK

- nawiasy kwadratowe oznaczają, że to lista

Języki i Paradygmaty Programowania

Języki programowania Haskell

Zatem w jaki sposób nasze programy mają komunikować się ze światem zewnętrznym?

Programowanie funkcyjne (Haskell Wprowadzenie) Kowalik Adrian

TWORZENIE SWOICH TYPÓW I TYPÓW KLAS HASKELL (RODZIAŁ 8) ZAJĘCIA 4

Programowanie funkcyjne wprowadzenie Specyfikacje formalne i programy funkcyjne

PARADYGMATY I JĘZYKI PROGRAMOWANIA. Haskell. (w11)

Tworzenie własnych typów. danych oraz klas typów

Wstęp do Programowania potok funkcyjny

Haskell Moduły Ładowanie

Prezentacja o Haskell u(rozdział 3 i 4)

Leży u podstaw programowania funkcyjnego. Napisy. 1 Zastapienie dowolnego wyrażenia innym wyrażeniem o tej samej. wartości daje równoważny program.

Nazwa pochodzi od imienia znanego logika Haskell a Brooks a Curry ego ( )

λ parametry. wartość funkcji suma = λ x y. x + y kwadrat = λ x. x * x K.M. Ocetkiewicz, 2008 WETI, PG 2 K.M. Ocetkiewicz, 2008 WETI, PG 3

Wstęp do Programowania potok funkcyjny

Haskell. Rozdział Ogólne informacje o języku

Wstęp do Programowania potok funkcyjny

Paradygmaty programowania

Język programowania Scala / Grzegorz Balcerek. Wyd. 2. Poznań, cop Spis treści

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

Programowanie Funkcyjne. Marcin Kubica Świder,

Język ludzki kod maszynowy

Obiektowy Caml. Paweł Boguszewski

Język programowania zbiór reguł określających, które ciągi symboli tworzą program komputerowy oraz jakie obliczenia opisuje ten program.

Wstęp do Programowania potok funkcyjny

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

Język C++ wykład VIII

Programowanie Komputerów

Programowanie strukturalne. Opis ogólny programu w Turbo Pascalu

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

Składnia funkcji i Rekurencja w języku Haskell

Informatyka I. Typy danych. Operacje arytmetyczne. Konwersje typów. Zmienne. Wczytywanie danych z klawiatury. dr hab. inż. Andrzej Czerepicki

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

Technologie cyfrowe semestr letni 2018/2019

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

Wstęp do programowania

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

Kurs programowania. Wykład 9. Wojciech Macyna. 28 kwiecień 2016

MATERIAŁY DO ZAJĘĆ II

Kurs rozszerzony języka Python

Programowanie obiektowe

Wstęp do programowania INP001213Wcl rok akademicki 2017/18 semestr zimowy. Wykład 12. Karol Tarnowski A-1 p.

Temat 1: Podstawowe pojęcia: program, kompilacja, kod

Technologie cyfrowe semestr letni 2018/2019

Moduły. Źródło: a) Wczytywanie modułów. interparse rozdziela każdy element listy wcześniej podanym znakiem

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

Programowanie obiektowe

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

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

LibreOffice Calc VBA

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

Podstawy programowania 2. Temat: Drzewa binarne. Przygotował: mgr inż. Tomasz Michno

C++ wprowadzanie zmiennych

Wstęp do programowania

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

Właściwości i metody obiektu Comment Właściwości

INSTRUKCJA PUSTA. Nie składa się z żadnych znaków i symboli, niczego nie robi. for i := 1 to 10 do {tu nic nie ma};

2 Przygotował: mgr inż. Maciej Lasota

4. Funkcje. Przykłady

3. Instrukcje warunkowe

ROZDZIAŁ 8: KLASY TYPÓW

Wprowadzenie do programowania w języku Visual Basic. Podstawowe instrukcje języka

Informatyka 1. Przetwarzanie tekstów

Programowanie, algorytmy i struktury danych

Język C zajęcia nr 11. Funkcje

Metody Kompilacji Wykład 7 Analiza Syntaktyczna

1 Podstawy c++ w pigułce.

Podstawy programowania. Wykład Pętle. Tablice. Krzysztof Banaś Podstawy programowania 1

Haskell Input and Output Źródło:

Wykład 15. Literatura. Kompilatory. Elementarne różnice. Preprocesor. Słowa kluczowe

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

Microsoft IT Academy kurs programowania

Tworzenie aplikacji w języku Java

Elementy języka Scheme

Platformy Programistyczne Podstawy języka Java

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

Wstęp do informatyki. stęp do informatyki Polecenia (cz.2)

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

Programowanie obiektowe

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

Podstawy programowania w języku C

Zofia Kruczkiewicz, Programowanie obiektowe - java, wykład 2 1

Wstęp do programowania

Programowanie w C++ Wykład 2. Katarzyna Grzelak. 4 marca K.Grzelak (Wykład 1) Programowanie w C++ 1 / 44

PARADYGMATY PROGRAMOWANIA Wykład 3

Tablice i struktury. czyli złożone typy danych. Programowanie Proceduralne 1

Operacje wejścia/wyjścia odsłona pierwsza

1 Podstawy c++ w pigułce.

WSNHiD, Programowanie 2 Lab. 2 Język Java struktura programu, dziedziczenie, abstrakcja, polimorfizm, interfejsy

/* dołączenie pliku nagłówkowego zawierającego deklaracje symboli dla wykorzystywanego mikrokontrolera */ #include <aduc834.h>

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Języki i metodyka programowania. Typy, operatory, wyrażenia. Wejście i wyjście.

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

Polimorfizm. dr Jarosław Skaruz

Przygotował: Jacek Sroka. PO* - Scala (iteratory, leniwość, view bounds i konwersje)

Transkrypt:

Programowanie w języku Haskell Strona domowa http://www.haskell.org Popularne implementacje Hugs/WinHugs GHC (The Glasgow Haskell Compiler) Źródła wykładu 1 Hal Daume: Yet Another Haskell Tutorial (http://www.cs.utah.edu/ hal/docs/daume02yaht.pdf) 2 Paul Hudak et al.: A Gentle Introduction to Haskell 98 (http://www.haskell.org/tutorial/) 3 Fethi Rabhi, Guy Lapalme: Algorithms: A Functional Programming Approach, Addison-Wesley 1999

Przykładowy plik źródłowy (prog.hs) module Main where x = 5 increment n = n + 1 main = putstrln "Hello World" Kompilacja C:\> ghc prog.hs -o prog.exe C:\> prog.exe Hello World Uruchomienie w trybie interaktywnym C:\> ghci prog.hs lub C:\> ghci Prelude> :l prog.hs Main> <-- wczytanie pliku (:load)

Main> x 5 Main> increment 1 2 Main> main Hello World Main> :r <-- ponowne wczytanie pliku (:reload) Prelude> (2 * 3)^2 36 Prelude> sqrt 2 1.4142135623730951 Prelude> a == b False Prelude> let decrement n = n - 1 Prelude> decrement 2 1

Funkcje Wartość (funkcja bezargumentowa) pi = 3.141592653589793 Funkcja 1-argumentowa square x = x * x Funkcja 2-argumentowa etc. trianglearea a h = 0.5 * a * h Uwagi Nazwy wartości i funkcji muszą zaczynać się małą literą Nie ma zmiennych

Klauzula where Definiuje wartości i funkcje użyte wewnątrz wyrażenia: volume r = a * pi * cube where a = 4 / 3 cube = r * r * r lub volume r = a * pi * (cube r) where a = 4 / 3 cube x = x * x * x Wyrażenie let... in volume r = let a = 4 / 3 cube = r * r * r in a * pi * cube

Lokalność definicji po where (let) a = 1 volume r = a * pi * r * r * r where a = 4 / 3 f x = a * x Main> f 5 5 where i let razem Definicje po let przesłaniają te po where: f x = let y = x + 1 in y where y = x + 2 Main> f 5 6

Wyrażenie if... then... else sgn x = if x < 0 then -1 else if x == 0 then 0 else 1 Uwaga Nie ma konstrukcji if... then Strażnicy (guards) sgn x x < 0 = -1 x == 0 = 0 x > 0 = 1 sgn x x < 0 = -1 x == 0 = 0 otherwise = 1 1, x < 0 sgn(x) = 0, x = 0 1, x > 0 1, x < 0 sgn(x) = 0, x = 0 1, wpp

Strażnicy c.d. Kolejność strażników ma znaczenie: f a b a >= b = "wieksze lub rowne" a == b = "rowne" otherwise = "niewieksze" a < b = "mniejsze" Main> f 1 1 "wieksze lub rowne" Main> f 1 5 "niewieksze"

Dopasowanie wzorca (pattern matching) land a b = if a == True && b == True then True else False lub 1 land True True = True land a b = False 2 land True True = True land = False Kolejność definicji ma znaczenie: land = False land True True = True Main> land True True False

Wyrażenie case... of f 1 = 2 f 2 = 3 f _ = -1 równoważne: f x = case x of 1 -> 2 2 -> 3 _ -> -1 Argumentem case może być dowolne wyrażenie: g x = case x < 0 of True -> "ujemna" False -> "dodatnia"

Przykład { 1, n = 0 n! = n (n 1)!, n 0 1 fact n = if n == 0 then 1 else n * fact (n - 1) 2 fact n n == 0 = 1 otherwise = n * fact (n - 1) 3 fact 0 = 1 fact n = n * fact (n - 1) 4 fact n = case n of 0 -> 1 _ -> n * fact (n - 1)

Operatory infiksowe i prefiksowe Nazwy funkcji infiksowych składają się z symboli (&,!,=,+,*...). Ujęte w ( ) stają się prefiksowe: f x y z = x * y + z f x y z = (+) ((*) x y) z Nazwy funkcji prefiksowych składają się ze znaków alfanumerycznych. Ujęte w stają się infiksowe: land a b a land b Definiowanie operatorów infiksowych x & y = (x + y) / 2 x \/ y = x y x!= y = x /= y

Operator (.) Złożenie funkcji: (f. g) x = f (g x) Main> sqrt (fact 4) 4.898979485566356 Main> (sqrt. fact) 4 4.898979485566356 Main> (sqrt. sqrt. fact) 4 2.213363839400643

Formatowanie kodu Ogólne zasady 1 Definicje najwyższego poziomu zaczynają się w tej samej kolumnie: abs x = if x < 0 then -x else x a = 5 2 Definicja może być złamana w dowolnym miejscu pod warunkiem, że wcięcia będą większe niż w pierwszej linii: abs x = if a = 5 x < 0 then -x else x

3 Jeżeli po where lub let występuje więcej niż jedna definicja lokalna, wszystkie muszą zaczynać się w tej samej kolumnie: f x = a * g x f x = a * g x where where a = 2 a = 2 g x = x * x g x = x * x 4 Wyrażenia po of oraz do muszą zaczynać się w tej samej kolumnie: Uwagi f x = case x of f x = case x of 1 -> 2 1 -> 2 3 -> 4 3 -> 4 Rozmiar znaku tabulatora: 8 Można explicite użyć nawiasów i średników: f x = case x of { 1 -> 2 ; 3 -> 4 }

Komentarze {- To jest komentarz blokowy, który może rozciągać się na {- A to jest komentarz zagnieżdżony -} kilka linii. -} -- ten komentarz rozciąga się do końca linii main = putstrln "Hello World" -- wyświetl tekst Programowanie piśmienne (literate programming) Tekst w pliku z rozszerzeniem *.lhs jest domyślnie komentarzem Linie kodu zaczynają się od > lub umieszczone są pomiędzy \begin{code} i \end{code}

Plik prog.lhs Ten program pokazuje na ekranie "Hello World" > main = putstrln "Hello World" i nie robi nic poza tym. Plik prog.lhs (styl L A TEX-owy) Ten program pokazuje na ekranie "Hello World" \begin{code} main = putstrln "Hello World" \end{code} i nie robi nic poza tym.

\documentclass{article} \usepackage{verbatim} \newenvironment{code}{\verbatim}{\endverbatim} \begin{document} \section{wprowadzenie} Ten program pokazuje na ekranie "Hello World" \begin{code} main = putstrln "Hello World" \end{code} i nie robi nic poza tym. \end{document} 1 Wprowadzenie Ten program pokazuje na ekranie Hello World main = putstrln "Hello World" i nie robi nic poza tym.

Typy Podstawowe typy danych i operacje Int (skończonej precyzji) 56 Integer (nieskończonej precyzji) 732145682358 Float/Double 3.14159265 Bool False Char a String "Ala" a == b a /= b a && b a b not a mod a b div a b równość nierówność koniunkcja alternatywa negacja reszta z dzielenia część całkowita z dzielenia

Typy złożone Krotka (tuple): (Int, Char) (1, a ) (Int, Char, Float) (1, a, 3.4) ((Bool, String), Int) ((True, "Ala"), 2) ([Int], Char) ([-1, 4, 2], c ) Lista: [Int] [1, 2, 3] [Char] [ a, b ] [[Int]] [[1], [1, 4], []] [(String, Bool)] [("Ala", True), ("kot", False)] Funkcja: Int -> Char [Int] -> (Char, Float) -> Bool arg1 -> arg2 ->... -> argn -> wynik

Operator (::) Specyfikuje typ: x = 4 x :: Int y = 5 :: Float isb :: Char -> Bool isb c = (c == B ) (c == b ) Uwaga Specyfikowanie typu jest zazwyczaj opcjonalne. Haskell sam wywnioskuje typ danego identyfikatora: Prelude> let isb c = (c == B ) (c == b ) Prelude> :t isb <-- pokaż typ (:type) isb :: Char -> Bool

Przykład roots :: (Float, Float, Float) -> (Float, Float) roots (a, b, c) = if d < 0 then error "pierwiastki urojone" else (r1, r2) where r1 = e + sqrt d / (2 * a) r2 = e - sqrt d / (2 * a) d = b * b - 4 * a * c e = -b / (2 * a) lub roots :: Float -> Float -> Float -> (Float, Float) roots a b c =... Uwagi Kolejność obliczeń nie jest określona Obliczenia są leniwe (lazy)

Typy polimorficzne Prelude> let fst (x, y) = x Prelude> :t fst fst :: (t, t1) -> t fst (x, y) = x snd (x, y) = y fst :: (a, b) -> a snd :: (a, b) -> b fst3 (x, _, _) = x snd3 (_, y, _) = y thd3 (_, _, z) = z fst3 :: (a, b, c) -> a snd3 :: (a, b, c) -> b thd3 :: (a, b, c) -> c

Listy Definiowanie list l1 = [3, -5, 4, 8, -1] l1 :: [Int] l2 = [[2.4, 5.6], [-45.9, 3.5, 0.05], [], [4.7]] l2 :: [[Float]] l3 = [( a, True), ( 1, False), ( d, True)] l3 :: [(Char, Bool)] el = [] <-- lista pusta el :: [a] [1.. 10] => [1,2,3,4,5,6,7,8,9,10] [1, 3.. 10] => [1,3,5,7,9] [1, 4..] => [1,4,7,10,... lista nieskończona

List comprehensions { x 2 : x {1,..., 9} even(x) } even x = mod x 2 == 0 [x x <- [1.. 9], even x] => [2, 4, 6, 8] [x * x x <- [1.. 9], even x] => [4, 16, 36, 64] [sqrt x x <- [4, 9, 16]] => [2.0, 3.0, 4.0] { (x, y) : x {1, 2, 3} y {8, 9}} [(x, y) x <- [1, 2, 3], y <- [8, 9]] => [(1, 8), (1, 9), (2, 8), (2, 9), (3, 8), (3, 9)] (skrajny prawy generator zmienia się najszybciej)

Operator (:) Konstruuje listę z głowy (head) i ogona (tail): 3 : [4, 5] = [3, 4, 5] True : [] = [True] ( a, 1) : [( b, 5)] = [( a, 1), ( b, 5)] [2, 3] : [[1], [5, 6]] = [[2, 3], [1], [5, 6]] [ a, b ] : [] = [[ a, b ]] [ a, b ] : [[]] = [[ a, b ], []] (:) :: a -> [a] -> [a] <-- typ operatora 1:2:3:[] = [1, 2, 3] <-- prawostronna łączność

Podstawowe operacje na listach length length [] = 0 length (x:xs) = 1 + length xs lub length (_:xs) = 1 + length xs length [4, 3, 7]? length (4 : [3, 7]) => 1 + length [3, 7] 1 + length (3 : [7]) => 1 + 1 + length [7] 1 + 1 + length (7 : []) => 1 + 1 + 1 + length [] => 1 + 1 + 1 + 0 => 3 length [_:xs] = 1 + length xs <-- źle [_:xs] <-- lista zawierająca jedną niepustą listę

!! (!!) :: [a] -> Int -> a (x:_)!! 0 = x (_:xs)!! n = xs!! (n - 1) Prelude> [1, 2, 3]!! 2 3 ++ (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys) Prelude> [1, 2] ++ [2, 3] [1, 2, 2, 3]

head head :: [a] -> a head (x:_) = x Prelude> head [1, 2, 3] 1 tail tail :: [a] -> [a] tail (_:xs) = xs Prelude> tail [1, 2, 3] [2, 3]

Uwaga Prelude> let head (x:_) = x Prelude> head [] *** Exception: Non-exhaustive patterns in function head head (x:_) = x head [] = error "pusta lista nie ma glowy" init init :: [a] -> [a] init [x] = [] init (x:xs) = x : init xs Prelude> init [1, 2, 3] [1, 2]

last last :: [a] -> a last [x] = x last (_:xs) = last xs Prelude> last [1, 2, 3] 3 take take :: Int -> [a] -> [a] take 0 _ = [] take _ [] = [] take n (x:xs) = x : take (n - 1) xs Prelude> take 2 [1, 2, 3] [1, 2]

drop drop :: Int -> [a] -> [a] drop 0 xs = xs drop _ [] = [] drop n (_:xs) = drop (n - 1) xs Prelude> drop 2 [1, 2, 3] [3] elem elem :: (Eq a) => a -> [a] -> Bool elem x [] = False elem x (y:ys) x == y = True otherwise = elem x ys Prelude> elem 1 [1, 2, 3] True Prelude> elem 0 [1, 2, 3] False

sum sum :: (Num a) => [a] -> a sum [] = 0 sum (x:xs) = x + sum xs Prelude> sum [1, 2, 3] 6 reverse reverse :: [a] -> [a] reverse [] = [] reverse (x:xs) = (reverse xs) ++ [x] Prelude> reverse [1, 2, 3] [3, 2, 1]

Uwaga maximum maximum :: (Ord a) => [a] -> a maximum [x] = x maximum (x:y:ys) x > y = maximum (x:ys) otherwise = maximum (y:ys) Prelude> maximum [1, 2, 3] 3 f (1:xs) =... <-- OK f (1:1:xs) =... <-- OK f (x:x:xs) =... <-- tak nie wolno

concat concat :: [[a]] -> [a] concat [] = [] concat ([]:ys) = concat ys concat ((x:xs):ys) = x : concat (xs:ys) Prelude> concat [[1, 2], [], [3]] [1, 2, 3] zip zip :: [a] -> [b] -> [(a, b)] zip (x:xs) (y:ys) = (x, y) : zip xs ys zip = [] Prelude> zip [1, 2] [1, 2, 3] [(1, 1), (2, 2)]

unzip unzip :: [(a, b)] -> ([a], [b]) unzip [] = ([], []) unzip (x, y):ps = (x:xs, y:ys) where (xs, ys) = unzip ps Prelude> unzip [(1, 2), (3, 4)] ([1, 3], [2, 4])

Przykład perms [] = [[]] perms xs = [x:p x <- xs, p <- perms (removefirst x xs)] where removefirst x [] = [] removefirst x (y:ys) x == y = ys otherwise = y : removefirst x ys Main> perms [1, 2, 3] [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] Napisy String = [Char] "Kot Ali" [ K, o, t,, A, l, i ] K : o : t : : A : l : i :[] K : o : t : :"Ali"

Funkcje wyższego rzędu Funkcja wyższego rzędu (higher-order) przyjmuje jako argumenty lub zwraca w wyniku inne funkcje Funkcja mapująca (mapping function) double x = 2 * x doublelist [] = [] doublelist (x:xs) = (double x) : (doublelist xs) Main> doublelist [1, 2, 3] [2, 4, 6] map :: (a -> b) -> [a] -> [b] map f [] = [] map f (x:xs) = (f x) : (map f xs) doublelist xs = map double xs

Przykład one _ = 1 length xs = sum (map one xs) Funkcje anonimowe (λ-abstrakcje) double x = 2 * x doublelist xs = map double xs doublelist xs = map (\x -> 2 * x) xs \x -> 2 * x <-- anonimowa funkcja 1-argumentowa \x y -> x + y <-- anonimowa funkcja 2-argumentowa Uwaga inc x = x + 1 skrót od inc = \x -> x + 1 add x y = x + y skrót od add = \x y -> x + y

Częściowe aplikowanie (partial application) add x y = x + y add 1 => (\y -> 1 + y) Main> map (add 1) [1, 2, 3] [2, 3, 4] Sekcje (sections) Sekcja to częściowo zaaplikowany operator infiksowy: Prelude> map (1+) [1, 2, 3] [2, 3, 4] Prelude> map (2/) [1, 2, 4] [2.0, 1.0, 0.5] Prelude> map (/2) [1, 2, 4] [0.5, 1.0, 2.0]

Uwaga map (-1) [1, 2, 3] <-- nie zadziała (-1) jest interpretowane jako liczba -1 Prelude> map (\x -> x - 1) [1, 2, 3] [0, 1, 2] Funkcje uncurry, curry, flip uncurry uncurry :: (a -> b -> c) -> (a, b) -> c uncurry f (x, y) = f x y Prelude> uncurry (+) (1, 2) 3 Prelude> map (uncurry (*)) [(1, 2), (3, 4)] [2, 12]

curry curry :: ((a, b) -> c) -> a -> b -> c curry f x y = f (x, y) f (x, y) = x * y Main> curry f 3 4 12 flip flip :: (a -> b -> c) -> b -> a -> c flip f x y = f y x Prelude> flip (-) 2 1-1 Prelude> map ((-) 1) [1, 2, 3] [0, -1, -2] Prelude> map (flip (-) 1) [1, 2, 3] [0, 1, 2]

Funkcja zipwith zipwith :: (a -> a -> a) -> [a] -> [a] -> [a] zipwith _ [] _ = [] zipwith [] = [] zipwith f (x:xs) (y:ys) = (f x y) : zipwith f xs ys Prelude> zipwith (*) [1, -4, 3] [3, 0, 2] [3, 0, 6]

Zwijanie list (list folding) sum [] = 0 sum (x:xs) = x + sum xs product [] = 1 product (x:xs) = x * product xs foldr, b, [e 0, e 1, e 2 ] (e 0 (e 1 (e 2 b))) foldr :: (a -> b -> b) -> b -> [a] -> b foldr f b [] = b foldr f b (x:xs) = f x (foldr f b xs) foldl, b, [e 0, e 1, e 2 ] (((b e 0 ) e 1 ) e 2 ) foldl :: (a -> b -> a) -> a -> [b] -> a foldl f b [] = b foldl f b (x:xs) = foldl f (f b x) xs

Przykłady sum xs = foldr (+) 0 xs product xs = foldr (*) 1 xs xs ++ ys = foldr (:) ys xs concat xss = foldr (++) [] xss map f xs = foldr (\x ys -> (f x):ys) [] xs min x y = if x < y then x else y max x y = if x >= y then x else y minimum (x:xs) = foldr min x xs maximum (x:xs) = foldr max x xs sumll xss = foldl (foldl (+)) 0 xss Main> sumll [[1, 3], [], [4]] 8

Filtrowanie list filter filter :: (a -> Bool) -> [a] -> [a] filter _ [] = [] filter p (x:xs) p x = x : filter p xs otherwise = filter p xs Prelude> filter even [3, 2, 1, 4] [2, 4] any any :: (a -> Bool) -> [a] -> Bool any _ [] = False any p (x:xs) p x = True otherwise = any p xs elem n xs = any (\x -> x == n) xs

takewhile takewhile :: (a -> Bool) -> [a] -> [a] takewhile _ [] = [] takewhile p (x:xs) p x = x : takewhile p xs otherwise = [] Prelude> takewhile (/= l ) "kot Ali" "kot A" dropwhile dropwhile _ [] = [] dropwhile p (x:xs) p x = dropwhile p xs otherwise = (x:xs) Prelude> dropwhile (/= l ) "kot Ali" "li"

Przykład [(x 1, y 1 ), (x 2, y 2 ),..., (x n, y n )] n 2F = (x i x i+1 )(y i + y i+1 ), x n+1 = x 1, y n+1 = y 1 i=1 poly (x 1, y 1 ), (x 2, y 2 ), (x 3, y 3 ), (x 4, y 4 ) list1 x 1, x 2, x 3, x 4 map fst poly list2 x 2, x 3, x 4, x 1 tail list1 ++ [head list1] list3 y 1, y 2, y 3, y 4 map snd poly list4 y 2, y 3, y 4, y 1 tail list3 ++ [head list3] parts = zipwith (*) (zipwith (-) list1 list2) (zipwith (+) list3 list4) area = abs ((sum parts) / 2)

area :: [(Float,Float)] -> Float area [] = error "to nie jest wielokat" area [x] = error "punkt nie ma pola" area [x,y] = error "linia nie ma pola" area poly = abs ((sum parts) / 2) where parts = zipwith (*) (zipwith (-) list1 list2) (zipwith (+) list3 list4) list1 = map fst poly list2 = tail list1 ++ [head list1] list3 = map snd poly list4 = tail list3 ++ [head list3]

Funkcje w strukturach danych double x = 2 * x square x = x * x inc x = x + 1 apply [] x = x apply (f:fs) x = f (apply fs x) apply [double, square, inc] 3 => double (square (inc 3)) => 32 Pytanie Jaki jest typ funkcji apply?

Typy definiowane przez użytkownika Synonimy typów Synonimy są skrótami dla już istniejących typów. Definiuje się je słowem kluczowym type: roots :: (Float, Float, Float) -> (Float, Float) type Poly2 = (Float, Float, Float) type Root2 = (Float, Float) roots :: Poly2 -> Root2 type List3D = [(Double, Double, Double)] Uwaga Nazwy typów danych (i synonimów) muszą zaczynać się dużą literą

Typy użytkownika Typ użytkownika definiuje się słowem kluczowym data: data Polynom = Poly Float Float Float Polynom <-- nazwa typu Poly <-- nazwa konstruktora (funkcja) Float <-- typ 1go, 2go i 3go argumentu Poly roots :: (Float, Float, Float) -> (Float, Float) roots (a, b, c) =... roots :: Polynom -> (Float, Float) roots (Poly a b c) =... p1 :: Polynom p1 = Poly 1.0 2.0 (-1.0)

Uwaga Nazwa konstruktora może być taka sama jak nazwa typu: data Poly = Poly Float Float Float Typy użytkownika a dopasowanie wzorca data PointType = Point Float Float xpoint (Point x _) = x ypoint (Point _ y) = y xpoint (Point 1 2) => 1.0 p = Point 1 2 ypoint p => 2.0 firstquad [] = True firstquad ((Point x y):ps) = (x >= 0) && (y >= 0) && (firstquad ps)

Alternatywne konstruktory data PointType = Point Float Float data Shape = Rectangle PointType PointType Circle PointType Float Triangle PointType PointType PointType Element typu Shape jest prostokątem, kołem lub trójkatem. Przykładowe wartości: Rectangle (Point 2 4) (Point 8.5 2) Circle (Point 1 1.5) 5.5 Triangle (Point 0 0) (Point 4.5 6) (Point 9 0)

area :: Shape -> Float <-- pole figury area (Rectangle p1 p2) = abs (xpoint p1 - xpoint p2) * abs (ypoint p1 - ypoint p2) area (Cirlce _ r) = pi * r^2 area (Triangle p1 p2 p3) = sqrt (h * (h - a) * (h - b) * (h - c)) where h = (a + b + c) / 2.0 a = dist p1 p2 b = dist p1 p3 c = dist p2 p3 dist (Point x1 y1) (Point x2 y2) = sqrt ((x1 - x2)^2 * (y1 - y2)^2)

Konstruktory bezargumentowe data Day = Mon Tue Wed Thu Fri Sat Sun nameofday :: Day -> String nameofday d = case d of Mon -> "Poniedzialek" Tue -> "Wtorek" Wed -> "Sroda" Thu -> "Czwartek" Fri -> "Piatek" Sat -> "Sobota" Sun -> "Niedziela"

Typy parametryzowane data PairInt = Pair Int Int data PairType a = Pair a a p :: PairType Int p = Pair 2 5 fstpair :: PairType a -> a fstpair (Pair x _) = x lub data PairType a b = Pair a b p :: PairType Int Char p = Pair 1 a fstpair :: PairType a b -> a fstpair (Pair x _) = x

Typy parametryzowane c.d. data Maybe a = Nothing Just a head :: [a] -> Maybe a head [] = Nothing head (x:xs) = Just x head "haskell" => Just h head [] => Nothing data Either a b = Left a Right b foo :: Bool -> Either Char String foo x = case x of True -> Left a _ -> Right "a"

Typy rekurencyjne Lista jest pusta, albo składa się z głowy i listy: data List a = Empty Cons a (List a) l = Cons 12 (Cons 8 (Cons 10 Empty)) <-- [12, 8, 10] lengthlist :: List a -> Int lengthlist Empty = 0 lengthlist (Cons _ xs) = 1 + lengthlist xs Drzewo jest wierzchołkiem z listą poddrzew: data Tree a = Node a [Tree a]

Przykład - drzewa binarne data BinTree a = Empty Node a (BinTree a) (BinTree a) 5 t = Node 5 (Node 3 (Node 8 Empty Empty) / \ (Node 1 Empty Empty)) 3 4 (Node 4 Empty / \ \ (Node 6 Empty Empty)) 8 1 6 depth :: BinTree a -> Int <-- głębokość drzewa depth Empty = 0 depth (Node _ l r) = 1 + max (depth l) (depth r) tsum :: BinTree Int -> Int <-- suma elementów tsum Empty = 0 tsum (Node a l r) = a + (tsum l) + (tsum r)

Sposoby przechodzenia drzewa: preorder wierzchołek zostaje odwiedzony zanim odwiedzone zostaną jego poddrzewa inorder wierzchołek zostaje odwiedzony po odwiedzeniu lewego i przed odwiedzeniem jego prawego poddrzewa postorder wierzchołek zostaje odwiedzony po odwiedzeniu jego lewego i prawego poddrzewa 5 preorder t = [5, 3, 8, 1, 4, 6] / \ 3 4 inorder t = [8, 3, 1, 5, 4, 6] / \ \ 8 1 6 postorder t = [8, 1, 3, 6, 4, 5]

data BinTree a = Empty Node a (BinTree a) (BinTree a) preorder :: BinTree a -> [a] preorder Empty = [] preorder (Node a l r) = [a] ++ preorder l ++ preorder r inorder :: BinTree a -> [a] inorder Empty = [] inorder (Node a l r) = inorder l ++ [a] ++ inorder r postorder :: BinTree a -> [a] postorder Empty = [] postorder (Node a l r) = postorder l ++ postorder r ++ [a]

Jeszcze o konstruktorach data PairType a b = Pair a b Main> :t Pair Pair :: a -> b -> PairType a b Main> :t Pair a Pair a :: b -> PairType Char b Main> :t Pair a "hello" Pair a "hello" :: PairType Char [Char] Wyświetlanie wartości typu użytkownika data PairType a b = Pair a b Main> head [Pair 1 2, Pair 5 4] No instance for (Show (PairType t t1)) data PairType a b = Pair a b deriving Show Main> head [Pair 1 2, Pair 5 4] Pair 1 2

Tablice Tworzenie tablic import Array array <-- wymagane na początku programu a1 = array (1,3) [(2, a ), (1, b ), (3, c )] a2 = array (0,2) [(i, 2*i) i <- [0..2]] Main> a1 array (1,3) [(1, b ),(2, a ),(3, c )] Main> a2 array (0,2) [(0,0),(1,2),(2,4)]

listarray a3 = listarray (1,3) [5, 3, 8] a4 = listarray (1,3) "abc" Main> a3 array (1,3) [(1,5),(2,3),(3,8)] Main> a4 array (1,3) [(1, a ),(2, b ),(3, c )] accumarray a5 = accumarray (+) 5 (1,3) [(1,0), (3,1), (3,3), (1,3)] Main> a5 array (1,3) [(1,8),(2,5),(3,9)]

Korzystanie z tablic a = array (1,3) [(1, 4), (2, 6), (3, 7)]! Main> a! 2 6 // Uwagi Main> a // [(2, 5)] array (1,3) [(1,4),(2,5),(3,7)] Main> a // [(1, 0), (3, 8)] array (1,3) [(1,0),(2,6),(3,8)] 1 Dostęp do elementu tablicy O(1). 2 Modyfikacja tablicy powoduje utworzenie nowej i przekopiowanie elementów O(n).

Korzystanie z tablic c.d. a = array (1,3) [(1, 4), (2, 6), (3, 7)] bounds Main> bounds a (1,3) indices Main> indices a [1,2,3] elems Main> elems a [4,6,7] assocs Main> assocs a [(1,4),(2,6),(3,7)]

Moduły plik: Tree.hs module Tree where data Tree a = Empty Node a (Tree a) (Tree a) depth :: Tree a -> Int depth Empty = 0 depth (Node _ l r) = 1 + max (depth l) (depth r) plik: Main.hs module Main where import Tree main = putstrln (show (depth (Node 1 Empty Empty))) Uwaga Każdy moduł zaczyna się domyślnie od import Prelude

Eksportowanie wybranych nazw module Tree (Tree(Empty, Node), depth) where data Tree a = Empty Node a (Tree a) (Tree a) depth ::... lub module Tree (Tree(...), depth) where Importowanie nazw import Tree a = depth... lub Tree.depth... import qualified Tree a = Tree.depth... <-- jedyna możliwość import qualified Tree as T a = Tree.depth... lub T.depth...

Importowanie wybranych nazw module Tree (Tree(Empty, Node), depth) where data Tree a = Empty Node a (Tree a) (Tree a) depth ::... import Tree (depth) <-- tylko depth import Tree hiding (depth) <-- wszystko oprócz depth

Klasy typów i instancje Rodzaje polimorfizmu Polimorfizm parametryczny elem x [] = False elem x (y:ys) x == y = True otherwise = elem x ys elem :: a -> [a] -> Bool Przeładowanie (overloading) (==) :: a -> a -> Bool Integer: x == y = integereq x y Float: x == y = floateq x y lista: x == y = listeq x y

Klasy typów class Eq a where (==) :: a -> a -> Bool ozn. "typ a jest instancją klasy Eq jeżeli istnieje dla niego (przeładowana) operacja ==" Prelude> :t (==) (==) :: (Eq a) => a -> a -> Bool ozn. "dla każdego typu a, który jest instancją Eq, (==) ma typ a -> a -> Bool" elem :: (Eq a) => a -> [a] -> Bool (Eq a) <-- kontekst

Instancje typów instance Eq Integer where x == y = integereq x y ozn. Integer jest instancją Eq i definicja operacji (==) jest następująca (metoda) data Tree a = Empty Node a (Tree a) (Tree a) instance (Eq a) => Eq (Tree a) where Empty == Empty = True (Node a1 l1 r1) == (Node a2 l2 r2) = (a1 == a2) && (l1 == l2) && (r1 == r2) _ == _ = False Main> elem Empty [(Node 1 Empty Empty), Empty] True

Metody domyślne class Eq a where (==), (/=) :: a -> a -> Bool x /= y = not (x == y) <-- metoda domyślna Dziedziczenie (inheritance) class (Eq a) => Ord a where (<), (<=), (>=), (>) :: a -> a -> Bool max, min :: a -> a -> a x < y = x <=y && x/= y ozn. Ord jest podklasą Eq (każdy typ klasy Ord musi być też typu Eq) (Eq a, Ord a) => to samo co (Ord a) => class (Eq a, Show a) => C a where <-- wielokrotne

Jeszcze o kontekście qsort :: [a] -> [a] <-- źle qsort [] = [] qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs) Prelude> :l qsort.hs No instance for (Ord a) arising from use of >= Possible fix: add (Ord a) to the type signature(s) for qsort qsort :: (Ord a) => [a] -> [a] <-- dobrze data (Eq a) => Point a = Pt a a <-- kontekst dla typu użytkownika

Klasa Num class (Eq a, Show a) => Num a where (+), (-), (*) :: a -> a -> a negate :: a -> a abs, signum :: a -> a frominteger :: Integer -> a x - y = x + negate y negate x = 0 - x Klasa Show class Show a where show :: a -> String x = 5 y = 3 main = putstrln ("Wynik:" ++ show x ++ "+" ++ show y ++ "=" ++ show (x + y))

Klasa Read read :: (Read a) => String -> a Prelude> read "123" Ambiguous type variable a Prelude> (read "123") :: Float 123.0 Prelude> read "123" + 7 130 Prelude> read "hello" + 7 *** Exception: Prelude.read: no parse

deriving data Tree a = Empty Node a (Tree a) (Tree a) deriving (Eq, Show) Automatycznie utworzy instacje: instance (Eq a) => Eq (Tree a) where... instance (Show a) => Show (Tree a) where... Main> (Node 1 Empty Empty) == Empty False Main> show (Node 1 Empty Empty) "Node 1 Empty Empty" Uwaga Klauzula deriving może być użyta do tworzenia instancji klas: Eq, Ord, Show, Read, Enum, Bounded.

Klasa Enum class Enum a where succ, pred :: a -> a toenum :: Int -> a fromenum :: a -> Int data Day = Mon Tue Wed Thu Fri Sat Sun deriving (Show, Enum) Main> succ Mon Tue Main> pred Mon *** Exception: tried to take pred of first tag in enumeration Main> (toenum 5) :: Day Sat Main> fromenum Mon 0

Przykład (baza danych) type ID = Int type Attrib = (String, String) data Object = Obj ID [Attrib] deriving Show object :: ID -> [Attrib] -> Object object i as = Obj i as getid :: Object -> ID getid (Obj i as) = i getatts :: Object -> [Attrib] getatts (Obj i as) = as getname :: Object -> String getname o = snd (head (filter ((== "name").fst) (getatts o)))

class Databases d where empty :: d getlastid :: d -> ID getobjects :: d -> [Object] setlastid :: ID -> d -> d setobjects :: [Object] -> d -> d insert :: [Attrib] -> d -> d insert as db = setlastid i db where db = setobjects os db os = o : os os = getobjects db o = object i as i = 1 + getlastid db select :: ID -> d -> Object select i db = head (filter ((== i).getid) (getobjects db)) selectby :: (Object -> Bool) -> d -> [Object] selectby f db = filter f (getobjects db)

data DBS = DB ID [Object] deriving Show instance Databases DBS where empty = DB 0 [] getlastid (DB i os) = i setlastid i (DB j os) = DB i os getobjects (DB i os) = os setobjects os (DB i ps) = DB i os d0, d1, d2 :: DBS d0 = empty d1 = insert [("name", "john"),("age", "30")] d0 d2 = insert [("name", "mary"),("age", "20")] d1 Main> select 1 d1 Obj 1 [("name","john"),("age","30")] Main> selectby ((== "mary").getname) d2 [Obj 2 [("name","mary"),("age","20")]]

Wejście/Wyjście Uwagi Funkcje wejścia/wyjścia (IO) są przykładem akcji Typem wyniku akcji IO jest IO typ import IO <-- biblioteka IO getline :: IO String <-- akcja getline po wykonaniu zwraca wartość typu String putstrln :: String -> IO () Typ jednostkowy data () = ()

Słowo kluczowe do Definiuje sekwencję wyrażeń wykonywanych kolejno. Wyrażeniem może być: akcja przypisanie wyniku akcji do zmiennej (<-) definicja lokalna (let) main :: IO () main = do putstrln "Podaj swoje imie:" name <- getline putstrln ("Czesc " ++ name)

Słowo kluczowe do c.d. main = do putstrln "Podaj liczbe:" n <- getline let x = read n y = x^2 putstrln (n ++ " do kwadratu: " ++ show y) Przykład module Main where import IO import Random main = do n <- randomrio (1::Int, 100) putstrln "Zgadnij o jakiej liczbie mysle (1..100):" doguessing n

Przykład c.d. doguessing n = do putstrln "Wpisz liczbe:" w <- getline let x = read w if x < n then do putstrln "Za malo!" doguessing n else if x > n then do putstrln "Za duzo!" doguessing n else putstrln "Brawo!"

Zwracanie wartości Wartością sekwencji do jest wartość ostatniego wyrażenia: end :: IO Bool end = do putstrln "Koniec? (tak/nie)" c <- getline return (c == "tak") return :: a -> IO a <-- zamienia wartość typu a na IO a Uwaga Funkcja return nie robi nic poza zamianą typu wartości. Nie przerywa sekwencji do.

Przykład getwords = do putstrln "Wpisz slowo:" w <- getline if w == "" then return [] else do ws <- getwords return (w:ws) Main> :t getwords getwords :: IO [String] Main> getwords Wpisz slowo: Ala Wpisz slowo: kot Wpisz slowo: ["Ala","kot"] Main>

Biblioteka IO data IOMode = ReadMode WriteMode AppendMode ReadWriteMode type FilePath = String openfile :: FilePath -> IOMode -> IO Handle hclose :: Handle -> IO () hiseof :: Handle -> IO Bool hgetchar :: Handle -> IO Char hgetline :: Handle -> IO String hgetcontents :: Handle -> IO String getchar :: IO Char <-- hgetchar stdin getline :: IO String

Biblioteka IO c.d. hputchar :: Handle -> Char -> IO () hputstr :: Handle -> String -> IO () hputstrln :: Handle -> String -> IO () putchar :: Char -> IO () <-- hputchar stdout putstr :: String -> IO () putstrln :: String -> IO () readfile :: FilePath -> IO String writefile :: FilePath -> String -> IO () appendfile :: FilePath -> String -> IO ()

Przykład module Main where import IO main = doloop doloop = do putstrln "Wybierz polecenie cnazwapliku, znazwapliku lub w (wyjscie):" cmd <- getline case cmd of w :_ -> return () c :fname -> do putstrln ("Odczyt " ++ fname) doread fname doloop z :fname -> do putstrln ("Zapis " ++ fname) dowrite fname doloop _ -> doloop

Przykład c.d. doread fname = do handle <- openfile fname ReadMode contents <- hgetcontents handle putstrln "Pierwsze 100 znakow:" putstrln (take 100 contents) hclose handle dowrite fname = do putstrln "Wpisz tekst:" contents <- getline handle <- openfile fname WriteMode hputstrln handle contents hclose handle

Rodzaje błędów IO data IOError =... <-- typ błędu isdoesnotexisterror :: IOError -> Bool isalreadyinuseerror :: IOError -> Bool ispermissionerror :: IOError -> Bool iseoferror :: IOError -> Bool Obsługa wyjątków Funkcja catch definiuje obsługę wyjątków, które może zgłosić akcja lub zbiór akcji: catch :: IO a -> (IOError -> IO a) -> IO a readline :: Handle -> IO String readline h = catch (hgetline h) (\e -> return "")

Obsługa wyjątków c.d. readline :: Handle -> IO String readline h = catch (hgetline h) errorhandler where errorhandler e = if iseoferror e then return "" else ioerror e ioerror :: IOError -> IO a <-- przekazuje wyjątek dalej

Przykład doread :: String -> IO () doread fname = catch (do handle <- openfile fname ReadMode contents <- hgetcontents handle putstrln "Pierwsze 100 znakow:" putstrln (take 100 contents) hclose handle ) errorhandler where errorhandler e = if isdoesnotexisterror e then putstrln ("Nie istnieje " ++ fname) else return () <-- inne ignoruj

Uwagi końcowe Sposoby wartościowania wyrażeń add x y = x + y double x = add x x 1 Ścisłe (strict) double (5*4) => double 20 => add 20 20 => 20 + 20 => 40 2 Nieścisłe (non-strict) double (5*4) => add (5*4) (5*4) => (5*4) + (5*4) => 20 + (5*4) => 20 + 20 => 40

3 Leniwe (lazy) double (5*4) => add (5*4) (5*4) => (5*4) + (5*4) => 20 + 20 => 40 double $! (5*4) <-- wymuszenie ścisłego wartościowania Przykład a = error "Hello world" f x = 1 <-- równość wyrażeń Main> f a 1 Main> f $! a *** Exception: Hello world

Podsumowanie cech Haskella Czysto funkcyjny Leniwe obliczanie Funkcje wyższego rzędu Polimorfizm Klasy typów Monady