Programowanie funkcyjne - wprowadzenie
Transkrypt
Programowanie funkcyjne - wprowadzenie
Programowanie funkcyjne – wprowadzenie Specyfikacje formalne i programy funkcyjne dr inż. Marcin Szlenk Politechnika Warszawska Wydział Elektroniki i Technik Informacyjnych [email protected] 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 1 1 2 1 2 3 2 4 2 6 1 3 4 3 4 4 5 5 1 7 6 7 6 7 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) Ciąg niepusty składa się z pierwszego elementu x i reszty xs Połącz ciągi 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 1930 Rachunek Lambda (1936) 1940 Języki mieszane Języki czysto funkcyjne 1950 LISP 1960 (1958) 1970 ML (1973) 1980 Miranda (1985) Erlang Haskell 1990 (1986) Clean (1987) (1990) OCaml 2000 (1996) Scala (2003) F# (2002) Clojure 2010 (2007) 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 y = f(a) * f(a) z = f(a) y=z*z Wyrażenia z i f(a) * g(b) * c są całkowicie zamienne Można zoptymalizować… 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) = x2 + x g(x, y) = x + y Ścisłe wartościowanie (strict evaluation) f(g(1, 4)) → f(1+4) → f(5) → 52 + 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) → 52 + 5 → 30 Leniwe wartościowanie (lazy evaluation) Argument funkcji zostanie policzony co najwyżej raz, wtedy gdy będzie potrzebny 12 Leniwe wartościowanie ax2 + 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 r1, r2 zostaną e = -b / (2 * a) obliczone, gdy d ≥ 0 e zostanie obliczone, gdy będą obliczane r1 lub r2 (tylko raz) 13 Leniwe wartościowanie 1/0 OK. Definicja, a nie przypisanie a = 1/0 a + 1 → Błąd const x = 1 const a → 1 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, … take 3 [1.. ] → [1, 2, 3] Lista nieskończona 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; } … i w Haskellu length [ ] = 0 length (x : xs) = 1 + length xs Obliczenie długości listy w C/C++ 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 quicksort quicksort quicksort [ 2, 1, 3 ] [ ’b’, ’a’, ’c’ ] [ ”ala”, ”ma”, ”kota” ] [ (2, ’a’), (1,’b’) ] → → → → [ 1, 2, 3 ] [ ’a’, ’b’, ’c’ ] [ ”ala”, ”kota”, ”ma” ] [ (1, ’b’), (2, ’a’) ] 17 Myślenie imperatywne a funkcyjne a1 b1 a b n 2 2 a b . . i 1 i i an bn Imperatywne: c := 0 for i = 1 to n do c := c + a [ i ] b [ i ] Podatne na błędy Funkcyjne: sum ( zipWith () a b ) [a1, a2, ..., an] zipWith () → [a1 b1, a2 b2, …, an bn] [b1, b2, …, bn] 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