Programowanie funkcyjne wprowadzenie Specyfikacje formalne i programy funkcyjne dr inż. Marcin Szlenk Politechnika Warszawska Wydział Elektroniki i Technik Informacyjnych m.szlenk@elka.pw.edu.pl
Paradygmaty programowania Programowanie imperatywne Program jest sekwencją instrukcji do wykonania Wykonywanie instrukcji polega na zmianie stanu programu C, C++, Java, Programowanie funkcyjne Wykonanie programu polega na obliczaniu wartości funkcji matematycznych Definicja funkcji przedstawia zależność pomiędzy danymi wejściowymi i wyjściowymi Lisp, ML, Haskell, 2
Przykład Algorytm Quicksort 5 3 2 6 4 1 7 3 2 4 1 5 6 7 2 1 3 4 6 7 1 2 1 2 3 4 5 6 7 3
Implementacja w C/C++ void quicksort (int beg, int end) { int i = beg, j = end, v; int x = A[beg]; do { while (A[ i ] < x) i++; while (A[ j ] > x) j--; if (i <= j ) { v = A[ i ]; A[ i ] = A[ j ]; A[ j ] = v; i++; j--; } } } while (i <= j); if (beg < j) quicksort(beg, j); if (i < end) quicksort(i, end); 4
Implementacja w Haskellu Wynikiem sortowania ciągu pustego jest ciąg pusty quicksort [ ] = [ ] Wybierz elementy < x z ciągu xs quicksort (x : xs) = quicksort (filter (< x) xs) ++ [ x ] ++ quicksort (filter (>= x) xs) Połącz ciągi Ciąg niepusty składa się z pierwszego elementu x i reszty xs Wybierz elementy x z ciągu xs Nie ma przypisywania wartości do zmiennych Kolejność obliczeń nie jest explicite określona 5
Historia języków funkcyjnych Języki mieszane 1930 1940 Rachunek Lambda (1936) 1950 Języki czysto funkcyjne LISP (1958) 1960 Erlang (1986) ML (1973) 1970 1980 1990 Miranda (1985) Haskell (1990) Clean (1987) Scala (2003) OCaml (1996) F# (2002) Clojure (2007) 2000 2010 6
Podstawowe koncepcje Funkcje czyste Ścisłe, nieścisłe i leniwe wartościowanie Rekurencja Funkcje wyższego rzędu Polimorfizm 7
Funkcje czyste Efekt uboczny (side effect) to zmiana stanu spowodowana przez funkcję lub wyrażenie: modyfikacja zmiennej wyświetlenie na ekranie zapis do pliku Funkcja jest czysta (pure) jeżeli nie ma efektów ubocznych 8
Własności funkcji czystych Mogą być wykonywane w dowolnej kolejności y = f(a) * g(a) Wartości f(a) i g(a) mogą być obliczone równolegle Łatwość analizy a = f(x) = y = f(a) W każdym miejscu programu wartość f(a) będzie taka sama 9
Własności funkcji czystych Jeżeli zdefiniowano równość dwóch wyrażeń, to mogą być używane zamiennie (referential transparency) z = f(a) * g(b) * c Wyrażenia z i f(a) * g(b) * c są całkowicie zamienne y = f(a) * f(a) Można zoptymalizować z = f(a) y = z * z Równoważne powyższemu, ale f(a) liczymy tylko raz 10
Własności funkcji czystych y = random() * random() z = random() y = z * z Nie jest równoważne powyższemu y = printf( x ) * printf ( x ) z = printf( x ) y = z * z Nie jest równoważne powyższemu 11
Ścisłe, nieścisłe i leniwe wartościowanie f(x) = x 2 + x g(x, y) = x + y Ścisłe wartościowanie (strict evaluation) f(g(1, 4)) f(1+4) f(5) 5 2 + 5 30 Nieścisłe wartościowanie (non-strict evaluation) f(g(1,4)) g(1,4) 2 + g(1,4) (1+4) 2 + (1+4) 5 2 + 5 30 Leniwe wartościowanie (lazy evaluation) Argument funkcji zostanie policzony co najwyżej raz, wtedy gdy będzie potrzebny 12
Leniwe wartościowanie ax 2 + bx + c = 0 Trzeba obliczyć d 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) r1, r2 zostaną obliczone, gdy d 0 e zostanie obliczone, gdy będą obliczane r1 lub r2 (tylko raz) 13
Leniwe wartościowanie 1 / 0 a = 1 / 0 a + 1 Błąd const x = 1 const a 1 OK. Definicja, a nie przypisanie Definicja funkcji stałej OK. Argument nie jest obliczany, bo nie jest potrzebny Nieskończone struktury danych [1.. ] [1, 2, 3, 4, 5, 6, 7, 8, Lista nieskończona take 3 [1.. ] [1, 2, 3] OK. Weź 3 pierwsze elementy 14
Rekurencja Nie ma zmiennych nie ma pętli int length = 0; node_ptr = head_ptr; while (node_ptr!= NULL) { length++; node_ptr = node_ptr->next; } Obliczenie długości listy w C/C++ i w Haskellu length [ ] = 0 length (x : xs) = 1 + length xs Lista pusta ma długość zero Lista niepusta ma długość 1+ długość ogona 15
Funkcje wyższego rzędu Funkcja wyższego rzędu (higher order) przyjmuje jako argumenty lub zwraca w wyniku inne funkcje f x = 2 * x Definicja funkcji f map f [1, 2, 3] [2, 4, 6] Zastosuj funkcję f do każdego elementu listy 16
Polimorfizm quicksort [ ] = [ ] quicksort (x : xs) = quicksort (filter (< x) xs) ++ [ x ] ++ quicksort (filter (>= x) xs) Sortuje elementy dowolnego typu, dla którego zdefiniowano operatory <, >= quicksort [ 2, 1, 3 ] [ 1, 2, 3 ] quicksort [ b, a, c ] [ a, b, c ] quicksort [ ala, ma, kota ] [ ala, kota, ma ] quicksort [ (2, a ), (1, b ) ] [ (1, b ), (2, a ) ] 17
Myślenie imperatywne a funkcyjne Imperatywne: c := 0 for i = 1 to n do c := c + a [ i ] b [ i ] a a. a 1 2 n b b. b 1 2 n n i1 a i b i Funkcyjne: Podatne na błędy sum ( zipwith () a b ) [a 1, a 2,..., a n ] [b 1, b 2,, b n ] zipwith () [a 1 b 1, a 2 b 2,, a n b n ] Funkcja wyższego rzędu 18
Zastosowania komercyjne Ericsson oprogramowanie urządzeń sieciowych i telekomunikacyjnych (Erlang) www.erlang.se Bluespec narzędzia do projektowania układów ASIC i FPGA (Haskell) www.bluespec.com Jane Street Capital analiza statystyczna danych finansowych (OCaml) www.janestcapital.com Commercial Users of Functional Programming, www.cufp.org 19
Zalety i wady Deklaratywność Kolejność wykonania nie określana przez programistę Funkcje definiowane jako zależności pomiędzy wartościami Duża ekspresyjność Funkcje wyższego rzędu Polimorfizm Łatwiejsza weryfikacja (przez indukcję matematyczną) Spadek wydajności Problemy z wnioskowaniem nt. wydajności Wciąż eksperymentalne (brak narzędzi i programistów) 20