Wykład 3 Funkcje wyższych rzędów

Podobne dokumenty
Wykład 3 Funkcje wyższych rzędów

Programowanie funkcyjne. Wykªad 13

Programowanie funkcyjne Wykład 13. Siła wyrazu rachunku lambda

Teoretyczne Podstawy Języków Programowania Wykład 4. Siła wyrazu rachunku λ

Programowanie Funkcyjne. Marcin Kubica Świder,

Wykład 5 Listy leniwe

Wykład 7 Porównanie programowania funkcyjnego i imperatywnego

Wstęp do Programowania potok funkcyjny

R. D. Tennent, The Denotational Semantics of Programming Languages [1976]

Paradygmaty programowania

Programowanie funkcjonalne

Wstęp do programowania

Zaawansowane algorytmy i struktury danych

Wstęp do programowania

Programowanie obiektowe

PoniŜej znajdują się pytania z egzaminów zawodowych teoretycznych. Jest to materiał poglądowy.

Programowanie funkcyjne wprowadzenie Specyfikacje formalne i programy funkcyjne

Podstawowe algorytmy i ich implementacje w C. Wykład 9

Wstęp do Programowania potok funkcyjny

Technologie informacyjne Wykład VII-IX

Programowanie strukturalne. Opis ogólny programu w Turbo Pascalu

5. Algebra działania, grupy, grupy permutacji, pierścienie, ciała, pierścień wielomianów.

Indukcja. Materiały pomocnicze do wykładu. wykładowca: dr Magdalena Kacprzak

Informatyka 1. Wyrażenia i instrukcje, złożoność obliczeniowa

Wstęp do Programowania potok funkcyjny

Obliczenia na stosie. Wykład 9. Obliczenia na stosie. J. Cichoń, P. Kobylański Wstęp do Informatyki i Programowania 266 / 303

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

Wstęp do programowania

Algorytmy i Struktury Danych

Wstęp do Programowania potok funkcyjny

Wstęp do programowania

Technologie Informatyczne Wykład VII

Ćwiczenia z wyliczania wartości funkcji

Logika Stosowana. Wykład 1 - Logika zdaniowa. Marcin Szczuka. Instytut Informatyki UW. Wykład monograficzny, semestr letni 2016/2017

λ 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

Podstawy programowania funkcjonalnego

Wstęp do Programowania potok funkcyjny

Składnia funkcji i Rekurencja w języku Haskell

Pascal - wprowadzenie

Lab 10. Funkcje w argumentach funkcji metoda Newtona. Synonimy nazw typów danych. Struktury. Tablice struktur.

Wstęp do Programowania potok funkcyjny

Obiektowy Caml. Paweł Boguszewski

Języki programowania Haskell

Przykładowe zadania z teorii liczb

1. Nagłówek funkcji: int funkcja(void); wskazuje na to, że ta funkcja. 2. Schemat blokowy przedstawia algorytm obliczania

Szablony funkcji i klas (templates)

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

Poprawność semantyczna

Informatyka I. Wykład 3. Sterowanie wykonaniem programu. Instrukcje warunkowe Instrukcje pętli. Dr inż. Andrzej Czerepicki

Logika i teoria mnogości Wykład 14

Języki i Paradygmaty Programowania

Chcąc wyróżnić jedno z działań, piszemy np. (, ) i mówimy, że działanie wprowadza w STRUKTURĘ ALGEBRAICZNĄ lub, że (, ) jest SYSTEMEM ALGEBRAICZNYM.

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

Zbiory, relacje i funkcje

Wstęp do Programowania potok funkcyjny

Programowanie komputerowe. Zajęcia 3

1 Automaty niedeterministyczne

ALGEBRA Z GEOMETRIĄ PIERŚCIEŃ WIELOMIANÓW

Języki programowania zasady ich tworzenia

Analiza algorytmów zadania podstawowe

Wykład 9 Funktory (moduły sparametryzowane)

2.2. Gramatyki, wyprowadzenia, hierarchia Chomsky'ego

Wykład 6 Programowanie imperatywne w języku OCaml

Programowanie obiektowe

Języki i metody programowania

Jerzy Nawrocki, Wprowadzenie do informatyki

Język ludzki kod maszynowy

Semantyka i Weryfikacja Programów - Laboratorium 3

Podstawy programowania. 1. Operacje arytmetyczne Operacja arytmetyczna jest opisywana za pomocą znaku operacji i jednego lub dwóch wyrażeń.

Metalogika (1) Jerzy Pogonowski. Uniwersytet Opolski. Zakład Logiki Stosowanej UAM

1. Wykład NWD, NWW i algorytm Euklidesa.

Wykład 8. Moduły. Zdzisław Spławski Programowanie funkcyjne 1

Wstęp do programowania. Procedury i funkcje. Piotr Chrząstowski-Wachtel

Redis, skrypty w języku Lua

Wieczorowe Studia Licencjackie Wrocław, Wykład nr 6 (w oparciu o notatki K. Lorysia, z modyfikacjami) Sito Eratostenesa

Zadanie 1 Przygotuj algorytm programu - sortowanie przez wstawianie.

Wstęp do programowania

Matematyka dyskretna. Andrzej Łachwa, UJ, /15

Programowanie w języku C++ Podstawowe paradygmaty programowania

Monoidy wolne. alfabetem. słowem długością słowa monoidem wolnym z alfabetem Twierdzenie 1.

Programowanie w VB Proste algorytmy sortowania

1 Wprowadzenie do algorytmiki

SIMR 2016/2017, Analiza 2, wykład 1, Przestrzeń wektorowa

ALGORYTMY I STRUKTURY DANYCH

P. Urzyczyn: Materia ly do wyk ladu z semantyki. Uproszczony 1 j. ezyk PCF

Rekurencja (rekursja)

Programowanie - wykład 4

Jerzy Nawrocki, Wprowadzenie do informatyki

Funkcje. Deklaracja funkcji. Definicja funkcji. Wykorzystanie funkcji w programie.

Zasada indukcji matematycznej

Jak należy się spodziewać, mamy. Zauważmy jednak, że nie zachodzi równość

Wstęp do Informatyki zadania ze złożoności obliczeniowej z rozwiązaniami

Instrukcje pętli przykłady. Odgadywanie hasła. 1) Program pyta o hasło i podaje adres, gdy hasło poprawne lub komunikat o błędnym haśle.

Wrocław, Wstęp do informatyki i programowania: liczby pierwsze. Wydział Matematyki Politechniki Wrocławskiej.

Informatyka A. Algorytmy

Elżbieta Kula - wprowadzenie do Turbo Pascala i algorytmiki

Wstęp do programowania

Podstawy programowania w języku C

Algorytmy i struktury danych

O pewnych związkach teorii modeli z teorią reprezentacji

Transkrypt:

Wykład 3 Funkcje wyższych rzędów Funkcje jako dane Rozwijanie i zwijanie funkcji Składanie funkcji Funkcjonały dla list Funkcje wyższego rzędu jako struktury sterowania Semantyka denotacyjna prostego języka imperatywnego Rekonstrukcja typu Zdzisław Spławski Programowanie funkcyjne 1

Funkcje jako dane Języki programowania funkcyjnego traktują funkcje tak samo jak obiekty danych innych typów, a więc możliwe są: funkcje, produkujące funkcje jako wyniki; funkcje jako argumenty innych funkcji; struktury danych (np. krotki, listy) z funkcjami jako składowymi. Funkcja operująca na innych funkcjach jest nazywana funkcją wyższego rzędu lub funkcjonałem (ang. functional, stąd functional programming, a po polsku programowanie funkcyjne lub funkcjonalne). Zdzisław Spławski Programowanie funkcyjne 2

Funkcje jako dane Funkcje są często przekazywane jako dane w obliczeniach numerycznych. m Poniższy funkcjonał oblicza sumę 1 f ( i). Dla efektywności wykorzystuje on i= 0 rekursję ogonową. let sigma f m = let rec suma (i,z)= if i=m then z else suma(i+1, z+.(f i)) in suma(0, 0.0) ;; val sigma : (int -> float) -> int -> float = <fun> W połączeniu z funkcjonałami wygodne okazuje się wykorzystanie funkcji 9 2 anonimowych. Możemy obliczyć np. k =0 k bez konieczności oddzielnego definiowania funkcji podnoszenia do kwadratu. sigma (function k -> float(k*k)) 10;; - : float = 285. Zdzisław Spławski Programowanie funkcyjne 3

Funkcje jako dane 3 4 Wykorzystując ten funkcjonałłatwo obliczyć np. i j. = = + i 0 j 0 # sigma (function i -> sigma (function j -> float(i+j)) 5) 4;; -: float = 70. W przypadku częstego sumowania elementów macierzy można łatwo uogólnić m to na 1 n 1 g( i, j), czyli i= 0 j= 0 # let sigma2 g m n = sigma (function i -> sigma (function j -> g(i,j)) n) m;; val sigma2 : (int * int -> float) -> int -> int -> float = <fun> Wartość 3 4 = = i + i 0 j 0 j można teraz wyliczyć prościej: # sigma2 (function (k,l) -> float(k+l)) 4 5;; - : float = 70. Zdzisław Spławski Programowanie funkcyjne 4

Rozwijanie i zwijanie funkcji Na wykładzie 2. była mowa o postaci rozwiniętej (ang. curried) i zwiniętej (ang. uncurried) funkcji. Możemy napisać funkcjonał curry (odp. uncurry), który bierze funkcję w postaci zwiniętej (odp. rozwiniętej) i zwraca tę funkcję w postaci rozwiniętej (odp. zwiniętej). # let curry f x y = f (x, y);; (* Rozwijanie funkcji *) (* let curry = function f -> function x -> function y -> f (x,y);;*) val curry : ('a * 'b -> 'c) -> 'a -> 'b -> 'c = <fun> # let uncurry f (x, y) = f x y;; (* Zwijanie funkcji *) val uncurry : ('a -> 'b -> 'c) -> 'a * 'b -> 'c = <fun> # let plus (x,y) = x+y;; val plus : int * int -> int = <fun> # curry plus 4 5;; - : int = 9 # let add x y = x+ y;; (* lub let add = curry plus;; *) val add : int -> int -> int = <fun> # uncurry add (4,5);; - : int = 9 Zdzisław Spławski Programowanie funkcyjne 5

Składanie funkcji W matematyce często jest używana operacja złożenia (superpozycji) funkcji. Niech będą dane funkcje f :α β oraz g:γ α. Złożenie (f g):γ β jest definiowane (f g) (x) = f (g x). # let ($$) f g = fun x -> f ( g x);; (* Składanie funkcji *) val ( $$ ) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b = <fun> # let sqr x = x*x;; val sqr : int -> int = <fun> # (sqr $$ sqr) 2;; -: int = 16 # let third = List.hd $$ List.tl $$ List.tl;; val third : '_a list -> '_a = <fun> # third [1;2;3;4;5];; -: int = 3 # let next_char = Char.chr $$ (+)1 $$ Char.code;; val next_char : char -> char = <fun> # next_char 'f';; - : char = 'g' Zdzisław Spławski Programowanie funkcyjne 6

Funkcjonały dla list - map Łącząc funkcje wyższych rzędów i parametryczny polimorfizm można pisać bardzo ogólne funkcjonały. Funkcjonał map aplikuje funkcję do każdego elementu listy: map f [x 1 ;... ; x n ]-> [f x 1 ;... ; f x n ] # let rec map f = function [] -> [] x::xs -> (f x) :: map f xs ;; val map : ('a -> 'b) -> 'a list -> 'b list = <fun> # map (function x -> x*x) [1;2;3;4];; -: int list = [1; 4; 9; 16] Ten funkcjonał jest zdefiniowany w module List: # List.map;; -: f:('a -> 'b) -> 'a list -> 'b list = <fun> # List.map String.length ["Litwo";"ojczyzno";"moja"];; - : int list = [5; 8; 4] Zdzisław Spławski Programowanie funkcyjne 7

Funkcjonały dla list - filter Funkcjonał filter aplikuje predykat do każdego elementu listy; jako wynik zwraca listę elementów spełniających ten predykat w oryginalnym porządku. # let rec filter pred = function [] -> [] x::xs -> if pred x then x::filter pred xs else filter pred xs ;; val filter : ('a -> bool) -> 'a list -> 'a list = <fun> Efektywniejszą implementację (z wykorzystaniem rekursji ogonowej ) można znaleźć w module List. # List.filter (function s -> String.length s <= 6) ["Litwo";"ojczyzno";"moja"];; - : string list = ["Litwo"; "moja"] Zdzisław Spławski Programowanie funkcyjne 8

Generowanie liczb pierwszych metodą sita Eratostenesa Utwórz ciąg skończony [2,3,4,5,6,...,n]. Dwa jest liczbą pierwszą. Usuń z ciągu wszystkie wielokrotności dwójki, ponieważ nie mogą być liczbami pierwszymi. Pozostaje ciąg [3,5,7,9,11,...]. Trzy jest liczbą pierwszą. Usuń z ciągu wszystkie wielokrotności trójki, ponieważ nie mogą być liczbami pierwszymi. Pozostaje ciąg [5,7,11,13,17,...]. Pięć jest liczbą pierwszą itd. Na każdym etapie ciąg zawiera liczby mniejsze lub równe n, które nie są podzielne przez żadną z wygenerowanych do tej pory liczb pierwszych, więc pierwsza liczba tego ciągu jest liczbą pierwszą. Powtarzając opisane kroki otrzymamy wszystkie liczby pierwsze z zakresu [2..n]. # let primes to_n = let rec sieve n = if n <= to_n then n::sieve(n+1) else [] and find_primes = function h::t -> h:: find_primes (List.filter (function x -> x mod h <> 0) t) [] -> [] in find_primes(sieve 2);; val primes : int -> int list = <fun> # primes 30;; - : int list = [2; 3; 5; 7; 11; 13; 17; 19; 23; 29] Zdzisław Spławski Programowanie funkcyjne 9

Funkcjonały dla list - insert Funkcjonał insert bierze funkcję poprzedza (w postaci rozwiniętej) zadającą porządek elementów w liście, wstawiany element elem oraz listę uporządkowaną i wstawia elem do listy zgodnie z zadanym porządkiem. # let rec insert poprzedza elem = function [] -> [elem] h::t as l -> if poprzedza elem h then elem::l else h::(insert poprzedza elem t);; val insert : ('a -> 'a -> bool) -> 'a -> 'a list -> 'a list = <fun> Funkcję insert łatwo wyspecjalizować dla zadanego porządku, np. # let insert_le = insert (<=);; val insert_le : '_a -> '_a list -> '_a list = <fun> # insert_le 4 [1;2;3;4;5;6;7;8];; - : int list = [1; 2; 3; 4; 4; 5; 6; 7; 8] Zdzisław Spławski Programowanie funkcyjne 10

Funkcjonały dla list fold_left (accumulate) fold_right (reduce) Funkcjonał fold_left aplikuje dwuargumentową funkcję f do każdego elementu listy (z lewa na prawo) akumulując wynik w acc (efektywnie rekursja ogonowa): fold_left f acc [x 1 ;... ; x n ]-> f(...(f(f acc x 1 )x 2 )...)x n # let rec fold_left f acc = function h::t -> fold_left f (f acc h) t [] -> acc;; val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a = <fun> Dualny funkcjonał fold_right aplikuje dwuargumentową funkcję f do każdego elementu listy (z prawa na lewo) akumulując wynik w acc (zwykła rekursja): fold_right f [x 1 ;... ; x n ] acc -> f x 1 (f x 2 (...(f x n acc)...)) # let rec fold_right f l acc = match l with h::t -> f h (fold_right f t acc) [] -> acc;; val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b = <fun> Moduł List zawiera wiele użytecznych funkcjonałów dla list. Zdzisław Spławski Programowanie funkcyjne 11

fold_left (accumulate) - wykorzystanie # let sumlist = fold_left (+) 0;; (* sumowanie elementów listy *) val sumlist : int list -> int = <fun> # sumlist [4;3;2;1];; - : int = 10 # let prodlist = fold_left ( * ) 1;; (* mnożenie elementów listy *) val prodlist : int list -> int = <fun> # prodlist [4;3;2;1];; -: int = 24 # let flat = fold_left (@) [];; (* spłaszczanie list *) val flat : '_a list list -> '_a list = <fun> # flat [[5;6];[1;2;3]];; -: int list = [5; 6; 1; 2; 3] (* konkatenowanie elementów listy napisów *) # let implode = fold_left (^) "";; val implode : string list -> string = <fun> # implode ["Ala ";"ma ";"kota"];; - : string = "Ala ma kota" Zdzisław Spławski Programowanie funkcyjne 12

Abstrakcja pomaga zauważyć i wyrazić analogie Bez wykorzystania odpowiednio wysokiego poziomu abstrakcji funkcyjnej trudno byłoby zauważyć analogie między funkcjami z poprzedniego slajdu. Struktura funkcji jest identyczna, ponieważ w każdym wypadku mamy do czynienia z monoidem: liczby całkowite, +, 0 liczby całkowite, *, 1 listy, @, [] napisy, ^, "" Operacja monoidu jest łączna, więc w omawianych wyżej funkcjach moglibyśmy użyć funkcjonału fold_right, np. # let imploder l = List.fold_right (^) l "";; val imploder : string list -> string = <fun> # imploder ["Ala ";"ma ";"kota"];; - : string = "Ala ma kota" Zdzisław Spławski Programowanie funkcyjne 13

Przykłady algebr abstrakcyjnych (homogenicznych) Półgrupa S, : S S S a (b c)=(a b) c (łączność) Monoid S,, 1 jest półgrupą z obustronną jednością (elementem neutralnym) 1:S a 1=a 1 a=a Grupa S,,, 1 jest monoidem, w którym każdy element posiada element odwrotny względem binarnej operacji monoidu : S S a a=1 a a=1 Zdzisław Spławski Programowanie funkcyjne 14

Funkcje wyższego rzędu jako struktury sterowania int silnia (int n) { int x,y; x=n; y=1; // utworzenie pary <n,1> while (x!= 0) // n-krotna iteracja { y = x*y; // operacji f takiej, x = x-1; // że f<x,y> = <x-1,x*y> } // return y; // wydzielenie drugiego elementu pary } Wykorzystując nieformalne komentarze, możemy formalnie zapisać tę funkcję w języku OCaml (z rekursją ogonową, co jest naturalne zważywszy, że komentarze dotyczą wersji iteracyjnej). let silnia' n = let rec f(x,y) = if x<>0 then f(x-1,x*y) else (x,y) in snd (f(n,1));; # silnia' 5;; - : int = 120 Zdzisław Spławski Programowanie funkcyjne 15

Składnia i semantyka prostego języka imperatywnego PJI Składnia abstrakcyjna V Zmienna N Liczba B ::= true false B && B B B not B E < E E = E E ::= N V E + E E * E E E -E C ::= skip C; C V := E if B then C else C fi while B do C od Semantykę denotacyjną języka zadaje się definiując funkcje semantyczne, które opisują, jak semantyka wyrażenia może być otrzymana z semantyki składowych tego wyrażenia (semantyka kompozycyjna). Funkcja semantyczna dla instrukcji C jest funkcja częściową «C, zmieniającą wektor stanu programu S (pamięć operacyjną). Dziedzinę S reprezentujemy za pomocą zbioru funkcji σ: Zmienna Z. Załóżmy dla uproszczenia, że funkcje semantyczne dla wyrażeń logicznych B i arytmetycznych E są znane. «B : S {true, false} «E : S Z Zdefiniujemy funkcję semantyczną «C : S Ã S dla instrukcji, a potem (pomijając pewne szczegóły formalne), zaprogramujemy ja w OCamlu. Zdzisław Spławski Programowanie funkcyjne 16

Składnia i semantyka prostego języka imperatywnego PJI Składnia abstrakcyjna C ::= skip C; C V := E if B then C else C fi while B do C od Semantyka denotacyjna «B : S {true, false} «E : S Z «skip σ = σ «C 1 ; C 2 σ '«C 2 («C 1 σ) Powyżej są używane dwa funkcjonały F : [ S Ã S ] [ S Ã S ] i fix : [[ S Ã S ] [ S Ã S ]] [ S Ã S ], znajdujący najmniejszy punkt stały zadanego funkcjonału. Jego istnienie gwarantuje teoria dziedzin. Zdzisław Spławski Programowanie funkcyjne 17

Funkcjonał dla punktu stałego W języku z mocną typizacją i wartościowaniem gorliwym (Ocaml, SML) można zdefiniować funkcjonał, znajdujący punkty stałe funkcji. W językach z mocną typizacją i wartościowaniem leniwym można zdefiniować ogólniejszy funkcjonał fix : ('a 'a) 'a, znajdujący punkty stałe np. list. # let fix f = let rec fixf x = f fixf x in fixf;; val fix : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b = <fun> # let f = fun f n -> if n=0 then 1 else n*f(n-1);; val f : (int -> int) -> int -> int = <fun> # let fact = fix f;; val fact : int -> int = <fun> # fact 3;; - : int = 6 # fact 4;; - : int = 24 Jak widać, najmniejszym punktem stałym funkcjonału f, skonstruowanym przez fix jest funkcja silnia. Zdzisław Spławski Programowanie funkcyjne 18

Funkcje semantyczne dla PJI # let svar v s = List.assoc v s;; val svar : 'a -> ('a * 'b) list -> 'b = <fun> # let sskip s = s;; val sskip : 'a -> 'a = <fun> # let sscolon instr1 instr2 s = instr2 (instr1 s) (* czyli instr2 $$ instr1 *);; val sscolon : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c = <fun> # let sass var expr s = (var, expr)::(list.remove_assoc var s);; val sass : 'a -> 'b -> ('a * 'b) list -> ('a * 'b) list = <fun> # let sif b instr1 instr2 s = if b s then instr1 s else instr2 s;; val sif : ('a -> bool) -> ('a -> 'b) -> ('a -> 'b) -> 'a -> 'b=<fun> # let swhile = fix (fun f b instr s -> if b s then f b instr (instr s) else s);; val swhile : ('_a -> bool) -> ('_a -> '_a) -> '_a -> '_a = <fun>. Zdzisław Spławski Programowanie funkcyjne 19

Semantyka denotacyjna silni Poniżej przedstawiono semantykę denotacyjną silni (cum grano salis :) # let call n = ("n",n)::("x",0)::("y",0)::[];; val call : int -> (string * int) list = <fun> # let sassx1 s = sass "x" (svar "n" s) s;; val sassx1 : (string * 'a) list -> (string * 'a) list = <fun> # let sassy1 s = sass "y" 1 s;; val sassy1 : (string * int) list -> (string * int) list = <fun> # let sloop = let sassy2 s = sass "y" ((svar "x" s)*(svar "y" s)) s and sassx2 s = sass "x" ((svar "x" s)-1) s in swhile (fun m -> (svar "x" m) <> 0) (sscolon sassy2 sassx2);; val sloop : (string * int) list -> (string * int) list = <fun> # let silnia = (svar "y") $$ sloop $$ sassy1 $$ sassx1 $$ call;; val silnia : int -> int = <fun> # silnia 3;; - : int = 6 # silnia 4;; - : int = 24 # silnia 5;; - : int = 120 Zdzisław Spławski Programowanie funkcyjne 20

Rekonstrukcja typu # let f z y x = y (y z x);; val f : 'a -> ('a -> 'b -> 'a) -> 'b -> 'b -> 'a = <fun> Skąd kompilator wziął ten typ? Formalnie typ powstał w wyniku rozwiązania układu równań w pewnej algebrze typów. Na podstawie ilości argumentów widzimy, że musi być f : α β γ δ. Z kontekstu użycia argumentu y:β widzimy, że y jest funkcją i otrzymujemy dwa równania: β=α γ ϕ oraz β=ϕ δ w których przez ϕ oznaczyliśmy typ wyniku funkcji y. Oczywiście β=α (γ ϕ)=ϕ δ, skąd otrzymujemy: α=ϕ oraz δ=γ ϕ=γ α czyli β=α γ α. Na argumenty x i z kontekst nie nakłada żadnych ograniczeń, więc ostatecznie f : α (α γ α) γ γ α. W praktyce do rekonstrukcji typu kompilator wykorzystuje algorytm unifikacji. Zdzisław Spławski Programowanie funkcyjne 21

Zadania kontrolne (1) 1. Podaj typy poniższych funkcji: a) let f1 x = x 1 1;; b) let f2 x y z = x ( y ^ z );; c) let f3 x y z = x y z;; d) let f4 x y = function z -> x::y;; 2. Zdefiniuj funkcje curry3 i uncurry3, przeprowadzające konwersję między zwiniętymi i rozwiniętymi postaciami funkcji od trzech argumentów. Podaj ich typy. 3. Dodajmy do języka PJI poniższe trzy rodzaje pętli, których semantyka znana jest intuicyjnie z języka Pascal. Zdefiniuj semantykę denotacyjną dla tych pętli, a potem zdefiniuj te funkcje semantyczne w OCamlu. a) repeat C until B b) for V:=E to E do C c) for V:=E to E do C 4. Przekształć poniższą rekurencyjną definicję funkcji sumprod, która oblicza jednocześnie sumę i iloczyn listy liczb całkowitych na równoważną definicję nierekurencyjną z jednokrotnym użyciem funkcji fold_left. let rec sumprod = function h::t -> let (s,p)= sumprod t in (h+s,h*p) [] -> (0,1);; Zdzisław Spławski Programowanie funkcyjne 22

Zadania kontrolne (2) 5. Poniższe dwie wersje funkcji quicksort działają niepoprawnie. Dlaczego? let rec quicksort = function [] -> [] [x] -> [x] xs -> let small = List.filter (fun y -> y < List.hd xs ) xs and large = List.filter (fun y -> y >= List.hd xs ) xs in quicksort small @ quicksort large;; let rec quicksort' = function [] -> [] x::xs -> let small = List.filter (fun y -> y < x ) xs and large = List.filter (fun y -> y > x ) xs in quicksort' small @ (x :: quicksort' large);; Zdefiniuj poprawną i efektywniejszą wersję (w której podział listy jest wykonywany w czasie liniowym w jednym przejściu) funkcji quicksort. 6. Zdefiniuj funkcje sortowania a) przez wstawianie z zachowaniem stabilności i złożoności O(n 2 ) insort : ( a-> a->bool) -> a list -> a list. b) przez łączenie (scalanie) z zachowaniem stabilności i złożoności O(n lg n) mergesort : ( a-> a->bool) -> a list -> a list. Pierwszy parametr jest funkcją, sprawdzającą porządek. Zamieść testy sprawdzające stabilność. Zdzisław Spławski Programowanie funkcyjne 23