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

Podobne dokumenty
Wstęp do Programowania potok funkcyjny

Wykład 9 Funktory (moduły sparametryzowane)

Wstęp do Programowania potok funkcyjny

Wykład 7 Porównanie programowania funkcyjnego i imperatywnego

Obiektowy Caml. Paweł Boguszewski

Wstęp do Programowania potok funkcyjny

Wstęp do Programowania potok funkcyjny

Wstęp do Programowania potok funkcyjny


Wykład 11 Podtypy a dziedziczenie

Wstęp do Programowania potok funkcyjny

Struktury danych (I): kolejka, stos itp.

kiedy znowu uzyska sterowanie, to podejmuje obliczenie od miejsca, w którym poprzednio przerwała, i z dotychczasowymi wartościami zmiennych,

E S - uniwersum struktury stosu

Wstęp do Programowania potok funkcyjny

Wykład 5 Listy leniwe

Wykład 5 Wybrane zagadnienia programowania w C++ (c.d.)

Wstęp do Programowania potok funkcyjny

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

Wstęp do programowania

Pakiety są logicznymi zbiorami obiektów takich jak podprogramy, typy, zmienne, kursory, wyjątki.

Programowanie obiektowe

Wstęp do Programowania potok funkcyjny

Wstęp do programowania

Programowanie obiektowe

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

Klasy abstrakcyjne i interfejsy

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Wstęp do Programowania 2

Programowanie obiektowe

KOLEJKA (QUEUE) (lista fifo first in, first out)

Pakiety i interfejsy. Tomasz Borzyszkowski

Język programowania PASCAL

Dynamiczne struktury danych

TEMAT : KLASY DZIEDZICZENIE

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

Semantyka i Weryfikacja Programów - Laboratorium 3

Algorytmy i Struktury Danych.

Laboratorium nr 12. Temat: Struktury, klasy. Zakres laboratorium:

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

Dynamiczny przydział pamięci w języku C. Dynamiczne struktury danych. dr inż. Jarosław Forenc. Metoda 1 (wektor N M-elementowy)

Wykład 6 Programowanie imperatywne w języku OCaml

Algorytmy i Struktury Danych. Co dziś? Drzewo decyzyjne. Wykład IV Sortowania cd. Elementarne struktury danych

2. Klasy cz. 2 - Konstruktor kopiujący. Pola tworzone statycznie i dynamicznie - Funkcje zaprzyjaźnione - Składowe statyczne

Struktury danych: stos, kolejka, lista, drzewo

Programowanie strukturalne. Opis ogólny programu w Turbo Pascalu

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Programowanie w C++ Wykład 6. Katarzyna Grzelak. 1 kwietnia K.Grzelak (Wykład 6) Programowanie w C++ 1 / 43

Programowanie współbieżne Wykład 8 Podstawy programowania obiektowego. Iwona Kochaoska

KOTLIN. Język programowania dla Androida

Programowanie obiektowe - 1.

Język PL/SQL Pakiety podprogramów

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Pascal typy danych. Typy pascalowe. Zmienna i typ. Podział typów danych:

Wykład 3 Składnia języka C# (cz. 2)

Algorytmy i Struktury Danych.

Technologie obiektowe

Temat: Liniowe uporzdkowane struktury danych: stos, kolejka. Specyfikacja, przykładowe implementacje i zastosowania. Struktura słownika.

Typy klasowe (klasy) 1. Programowanie obiektowe. 2. Założenia paradygmatu obiektowego:

Struktura danych. Sposób uporządkowania informacji w komputerze. Na strukturach danych operują algorytmy. Przykładowe struktury danych:

Programowanie obiektowe

Język ludzki kod maszynowy

Wstęp do Programowania potok funkcyjny

Kiedy potrzebne. Struktura (rekord) Struktura w języku C# Tablice struktur. struktura, kolekcja

Wyzwalacze. Anna Fiedorowicz Bazy danych 2

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

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

Wstęp do programowania. Listy. Piotr Chrząstowski-Wachtel

Wykład 4. Klasa List Kolejki Stosy Słowniki

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

Jeśli chcesz łatwo i szybko opanować podstawy C++, sięgnij po tę książkę.

Algorytmy i Struktury Danych.

Paradygmaty programowania

Delphi Laboratorium 3

Semantyka i Weryfikacja Programów - Laboratorium 3

Algorytmy i Struktury Danych. Anna Paszyńska

Język JAVA podstawy. Wykład 3, część 3. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

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

- nawiasy kwadratowe oznaczają, że to lista

Ćwiczenie 1. Kolejki IBM Message Queue (MQ)

Procedury i funkcje składowane

Zaawansowane programowanie w C++ (PCP)

Klasy Obiekty Dziedziczenie i zaawansowane cechy Objective-C

Programowanie obiektowe

Kurs programowania. Wykład 9. Wojciech Macyna

Programowanie obiektowe i zdarzeniowe wykład 4 Kompozycja, kolekcje, wiązanie danych

Podstawy Programowania Obiektowego

Wykład 12. Programowanie serwera MS SQL 2005 w C#

Stos liczb całkowitych

Kurs WWW. Paweł Rajba.

Programowanie obiektowe

Dawid Gierszewski Adam Hanasko

Programowanie RAD Delphi

Wykład 8: klasy cz. 4

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

Język PL/SQL. Rozdział 5. Pakiety podprogramów. Dynamiczny SQL

Wstęp do Programowania potok funkcyjny

Wstęp do programowania 2

Programowanie w języku Python. Grażyna Koba

Programowanie i projektowanie obiektowe

Transkrypt:

Wykład 8 Moduły Abstrakcyjne typy danych, moduły i klasy Sygnatury i struktury Anonimowe sygnatury i struktury Wykorzystanie sygnatur do ukrywania reprezentacji Różne widoki modułów Współdzielenie typów w modułach Współdzielenie typów i moduły wewnętrzne Moduły i oddzielna kompilacja Zdzisław Spławski Programowanie funkcyjne 1

Abstrakcyjny typ danych Abstrakcyjny typ danych (ang. abstract data type = ADT) składa się z dobrze wyspecyfikowanego zbioru elementów oraz zbioru operacji, które mogą być wykonywane na tym zbiorze elementów. Przykłady ATD: stos, kolejka, graf, a także liczby całkowite, liczby rzeczywiste, wartości logiczne. Specyfikacja ADT nie opisuje wewnętrznej reprezentacji zbioru elementów ani sposobu implementacji operacji, która powinna być ukryta (hermetyzacja, ang. encapsulation). Zdzisław Spławski Programowanie funkcyjne 2

Abstrakcyjne typy danych i moduły Podstawę abstrakcyjnych typów danych stanowi rozdzielenie (publicznego) interfejsu i (ukrytej) implementacji. Systemy wspierające stosowanie abstrakcji (w szczególności ADT) powinny posiadać trzy własności: Hermetyzacja (ang. encapsulation) umożliwia ukrycie wnętrza części systemu. Kompozycyjność (ang. compositionality) umożliwia tworzenie nowych elementów systemu przez łączenie istniejących części. Konkretyzacja/wywołanie (ang. instantiation/invocation) umożliwia tworzenie wielu egzemplarzy elementu systemu w oparciu o tę samą definicję. ADT można wygodnie implementować, np. jako moduły lub klasy. Dwa aspekty programowania, związane z modułami: fizyczna dekompozycja programu na pliki, które mogą być oddzielnie kompilowane i wielokrotnie używane; logiczna dekompozycja programu, ułatwiająca projektowanie i zrozumienie programu (wyższy poziom abstrakcji). W języku OCaml można programować używając modułów i klas, co pozwala na porównanie tych mechanizmów i związanych z nimi technik programowania. Zdzisław Spławski Programowanie funkcyjne 3

Cele definiowania ATD Abstrakcja Bezpieczeństwo Modyfikowalność Przydatność do wielokrotnego użycia Zdzisław Spławski Programowanie funkcyjne 4

Algebra abstrakcyjna (uniwersalna) Algebrą abstrakcyjną nazywamy zbiór elementów S (nośnik algebry), na którym są zdefiniowane pewne operacje, posiadające własności zadane przez aksjomaty. Zdzisław Spławski Programowanie funkcyjne 5

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 6

Algebraiczna specyfikacja stosu (heterogeniczna) Sygnatura: empty : -> Stack push : Elem * Stack -> Stack top : Stack -> Elem pop : Stack -> Stack isempty : Stack -> bool Aksjomaty równościowe: s:stack, e: Elem isempty (push (e,s)) = false isempty (empty) = true pop (push(e,s)) = s pop (empty) = empty top (push(e,s)) = e top (empty) = ERROR Zdzisław Spławski Programowanie funkcyjne 7

Stos jako typ konkretny Stos można zaimplementować w języku OCaml jako typ konkretny, przepisując prawie dosłownie powyższą specyfikację algebraiczną. # type 'a stack = EmptyStack Push of 'a * 'a stack;; type 'a stack = EmptyStack Push of 'a * 'a stack # let isempty = function EmptyStack -> true Push _ -> false;; val isempty : 'a stack -> bool = <fun> # let pop = function Push(_,s) -> s EmptyStack -> EmptyStack;; val pop : 'a stack -> 'a stack = <fun> # let top = function Push(e,_) -> e EmptyStack -> failwith "top: EmptyStack";; val top : 'a stack -> 'a = <fun> # let s = Push(3,Push(2,Push(1,EmptyStack)));; val s : int stack = Push (3, Push (2, Push (1, EmptyStack))) Ta implementacja w sposób oczywisty spełnia specyfikację algebraiczną, ale udostępnia klientom reprezentację wewnętrzną stosu i stanowi zbiór nie związanych funkcji, znajdujących się w globalnej przestrzeni nazw. Zdzisław Spławski Programowanie funkcyjne 8

Programowanie małoskalowe i wielkoskalowe - analogie typ ~ sygnatura ( typ modułu, interfejs ) wartość ~ struktura ( moduł, implementacja ) funkcja ~ funktor Zdzisław Spławski Programowanie funkcyjne 9

Specyfikacja składowych wartości sygnatury: val nazwa : typ Składnia Deklaracja sygnatury (interfejsu): module type NAZWA = sig składowe sygnatury Deklaracja struktury (modułu): Nazwa modułu musi się zaczynać z dużej litery. Nazwa sygnatury jest dowolna, ale w OCamlu na mocy konwencji używane są wyłącznie duże litery. module Nazwa = struct definicje typów i wartości Zdzisław Spławski Programowanie funkcyjne 10

Sygnatury i struktury mogą być anonimowe. Składnia sig składowe sygnatury struct definicje typów i wartości Kompilator sam tworzy domyślną sygnaturę struktury, umieszczając w niej definicje typów i typy wartości. Można jednak sprecyzować sygnaturę używając dowolnej z poniższych notacji: module Nazwa : sygnatura = struktura lub module Nazwa = ( struktura : sygnatura ) Dostęp do typów i wartości zdefiniowanych w module można uzyskać używając notacji kropkowej: Nazwa1.nazwa2 Można też moduł otworzyć: open Nazwa Grozi to jednak przesłonięciem własnych identyfikatorów. Zdzisław Spławski Programowanie funkcyjne 11

# module type STACK_FUN = sig type 'a t exception Empty of string val create: 'a t val push: 'a * 'a t -> 'a t val top: 'a t -> 'a val pop: 'a t -> 'a t val isempty: 'a t -> bool ;; module type STACK_FUN = sig type 'a t exception Empty of string val create : 'a t val push : 'a * 'a t -> 'a t val top : 'a t -> 'a val pop : 'a t -> 'a t val isempty : 'a t -> bool Sygnatura stosu funkcyjnego Zdzisław Spławski Programowanie funkcyjne 12

Implementacja stosu funkcyjnego # module Stack : STACK_FUN = struct type 'a t = EmptyStack Push of 'a * 'a t exception Empty of string let create = EmptyStack let push(e,s) = Push(e,s) let top = function Push(e,_) -> e EmptyStack -> raise (Empty "module StackFun: top") let pop = function Push(_,s) -> s EmptyStack -> EmptyStack let isempty = function EmptyStack -> true Push _ -> false ;; module Stack : STACK_FUN Zdzisław Spławski Programowanie funkcyjne 13

Wykorzystanie modułu # let s = Stack.push(2,Stack.push(1,Stack.create));; val s : int Stack.t = <abstr> # Stack.top s;; - : int = 2 # Stack.top (Stack.pop(Stack.pop s));; Exception: Stack.Empty "module StackFun: top". Użycie modułu umożliwiło ukrycie reprezentacji wewnętrznej stosu i sposobu implementacji operacji (hermetyzacja). Identyfikatory, użyte w module są lokalne. Zdzisław Spławski Programowanie funkcyjne 14

Dwa widoki ATD (na przykładzie stosu) ATD Stack push... let create = EmptyStack.... Widok programisty (widoczne są detale implementacji) pop Widok klienta (widoczne są tylko operacje) Zdzisław Spławski Programowanie funkcyjne 15

Moduł bez sygnatury Reprezentację wewnętrzną można ujawnić, jeśli nie podamy sygnatury modułu. # module Stack' = struct type 'a t = EmptyStack Push of 'a * 'a t exception Empty of string let create = EmptyStack let push(e,s) = Push(e,s) let top = function Push(e,_) -> e EmptyStack -> raise (Empty "module Stack': top") let pop = function Push(_,s) -> s EmptyStack -> EmptyStack let isempty = function EmptyStack -> true Push _ -> false ;; module Stack' : sig type 'a t = EmptyStack Push of 'a * 'a t exception Empty of string val create : 'a t val push : 'a * 'a t -> 'a t val top : 'a t -> 'a val pop : 'a t -> 'a t val isempty : 'a t -> bool # let s = Stack'.push(2,Stack'.push(1,Stack'.create));; vals : intstack'.t= Stack'.Push(2, Stack'.Push (1, Stack'.EmptyStack)) Zdzisław Spławski Programowanie funkcyjne 16

Sygnatura stosu modyfikowalnego # module type STACK_MUT = sig type 'a t exception Empty of string val create: unit -> 'a t val push: 'a * 'a t -> unit val top: 'a t -> 'a val pop: 'a t -> unit val isempty: 'a t -> bool ;; module type STACK_MUT = sig type 'a t exception Empty of string val create : unit -> 'a t val push : 'a * 'a t -> unit val top : 'a t -> 'a val pop : 'a t -> unit val isempty : 'a t -> bool Zdzisław Spławski Programowanie funkcyjne 17

Implementacja stosu modyfikowalnego na liście # module StackMutList = struct type 'a t = { mutable l : 'a list } exception Empty of string let create() = { l = [] } let push(e,s) = s.l <- e :: s.l let top s = match s.l with hd::_ -> hd [] -> raise (Empty "module StackMutList: top") let pop s = match s.l with hd::tl -> s.l <- tl [] -> () let isempty s = s.l = [] ;; Zdzisław Spławski Programowanie funkcyjne 18

Stos modyfikowalny na liście - odpowiedź systemu module StackMutList : sig type 'a t = { mutable l : 'a list; } exception Empty of string val create() : unit -> '_a t val push : 'a * 'a t -> unit val top : 'a t -> 'a val pop : 'a t -> unit val isempty : 'a t -> bool # let s = StackMutList.create();; val s : '_a StackMutList.t = {StackMutList.l = []} # StackMutList.push(1,s);; - : unit = () # StackMutList.push(2,s);; - : unit = () # StackMutList.top s;; - : int = 2 # StackMutList.pop s;; - : unit = () # StackMutList.pop s;; - : unit = () # StackMutList.top s;; Exception: StackMutList.Empty "module StackMutList: top". Zdzisław Spławski Programowanie funkcyjne 19

Implementacja stosu modyfikowalnego na tablicy # module StackMutAr = struct type 'a t = { mutable n : int; mutable a : 'a option array } exception Empty of string let size = 5 let create() = { n=0 ; a = Array.create size None } let increase s = s.a <- Array.app s.a (Array.create size None) let push(e,s) = begin if s.n = Array.length s.a then increase s; s.a.(s.n) <- Some e ; s.n <- succ s.n let top s = if s.n=0 then raise (Empty "module StackMutAr: top") else match s.a.(s.n-1) with Some e -> e None -> failwith "module StackMutAr: top (implementation error!!!)" let pop s = if s.n=0 then () else s.n <- pred s.n let isempty s = s.n=0 ;; Zdzisław Spławski Programowanie funkcyjne 20

Stos modyfikowalny na tablicy odpowiedź systemu module StackMutAr : sig type 'a t = { mutable n : int; mutable a : 'a option array; } exception Empty of string val size : int val create : unit -> 'a t val increase : 'a t -> unit val push : 'a * 'a t -> unit val top : 'a t -> 'a val pop : 'a t -> unit val isempty : 'a t -> bool # let s = StackMutAr.create();; val s : '_a StackMutAr.t = {StackMutAr.n = 0; StackMutAr.a = [ None; None; None; None; None ]} # StackMutAr.push(1,s);; - : unit = () # StackMutAr.top s;; - : int = 1 # StackMutAr.pop s;; - : unit = () # StackMutAr.top s;; Exception: StackMutAr.Empty "module StackMutAr: top". Zdzisław Spławski Programowanie funkcyjne 21

Dwa moduły dla stosów (1) Moduły reprezentują typ t z sygnatury w różny sposób. # StackMutList.create();; - : '_a StackMutList.t = {StackMutList.l = []} # StackMutAr.create();; - : '_a StackMutAr.t ={StackMutAr.n = 0; StackMutAr.a = [ None; None; None; None; None ]} Można ukryć reprezentację dla abstrakcyjnego typu danych wykorzystując sygnaturę. # module SML : STACK_MUT = StackMutList;; module SML : STACK_MUT # module SMA : STACK_MUT = StackMutAr;; module SMA : STACK_MUT # let sl = SML.create();; val sl : '_a SML.t = <abstr> # let sa = SMA.create();; val sa : '_a SMA.t = <abstr> Zdzisław Spławski Programowanie funkcyjne 22

Oba moduły implementują ten sam interfejs, ale typy reprezentacji są różne. # SMA.isEmpty sl;; Characters 12-14: SMA.isEmpty sl;; ^^ This expression has type 'a SML.t but is here used with type 'b SMA.t Nawet gdyby typy reprezentacji były takie same, to użycie sygnatury spowodowało ukrycie tej reprezentacji. # module SL1 = (StackMutList : STACK_MUT);; module SL1 : STACK_MUT # module SL2 = (StackMutList : STACK_MUT);; module SL2 : STACK_MUT # let s = SL1.create();; val s : '_a SL1.t = <abstr> # SL2.isEmpty s;; Characters 12-13: SL2.isEmpty s;; Przykład: dwa moduły dla stosów (2) ^ This expression has type 'a SL1.t but is here used with type 'b SL2.t Zdzisław Spławski Programowanie funkcyjne 23

Różne widoki modułów (1) #module M = ( struct type t = int ref let create() = ref 0 let add x = incr x let get x = if!x>0 then (decr x; 1) else failwith "Empty" : sig type t val create : unit -> t val add : t -> unit val get : t -> int ) ;; module M : sig type t val create : unit -> t val add : t -> unit val get : t -> int Zdzisław Spławski Programowanie funkcyjne 24

Różne widoki modułów (2) # module type PRODUCER = sig type t val create : unit -> t val add : t -> unit ;; module type PRODUCER = sig type t val create : unit -> t val add : t -> unit # module type CONSUMER = sig type t val get : t -> int ;; module type CONSUMER = sig type t val get : t -> int # module Producer = (M:PRODUCER) ;; module Producer : PRODUCER # module Consumer = (M:CONSUMER) ;; module Consumer : CONSUMER Niestety, moduły Producer i Consumer nie mogą ze soba współpracować! #let x = Producer.create() in Producer.add x; Consumer.get x;; ^ This expression has type Producer.t but is here used with type Consumer.t Zdzisław Spławski Programowanie funkcyjne 25

Współdzielenie typów w modułach W celu utożsamienia typów Producer.t i Consumer.t trzeba użyć poniższej konstrukcji językowej: NAZWA with type t1 = t2 and... # module Producer = (M:PRODUCER with type t = M.t) ;; module Producer : sig type t = M.t val create : unit -> t val add : t -> unit # module Consumer = (M:CONSUMER with type t = M.t) ;; module Consumer : sig type t = M.t val get : t -> int # let x = Producer.create();; val x : Producer.t = <abstr> # Producer.add x; Producer.add x;; - : unit = () # Consumer.get x;; - : int = 1 # Consumer.get x;; - : int = 1 # Consumer.get x;; Exception: Failure "Empty". Zdzisław Spławski Programowanie funkcyjne 26

Współdzielenie typów i moduły wewnętrzne (1) # module M1 = ( struct type t = int ref module M_hide = struct let create() = ref 0 let add x = incr x let get x = if!x>0 then (decr x; 1) else failwith"empty" module Producer = M_hide module Consumer = M_hide : sig type t module Producer : sig val create : unit -> t val add : t -> unit module Consumer : sig val get : t -> int ) ;; Zdzisław Spławski Programowanie funkcyjne 27

Współdzielenie typów i moduły wewnętrzne (2) module M1 : sig type t module Producer : sig val create : unit -> t val add : t -> unit module Consumer : sig val get : t -> int Można teraz osiągnąć ten sam rezultat, który osiągnęliśmy za pomocą konstrukcji with type, chociaż dostęp do funkcji modułów Producer i Consumer odbywa się za pośrednictwem modułu M: # let x = M1.Producer.create();; val x : M1.t = <abstr> # M1.Producer.add x; - : unit = () # M1.Consumer.get x;; - : int = 1 # M1.Consumer.get x;; Exception: Failure "Empty". Zdzisław Spławski Programowanie funkcyjne 28

Moduły i oddzielna kompilacja Notacja wprowadzona dla sygnatur i modułów odnosiła się do programów monolitycznych, które mogą fizycznie być podzielone na pliki, ale są kompilowane jako całość. Jednostka kompilacji K składa się z dwóch plików: pliku z implementacją K.ml, który jest ciągiem definicji znajdujących się w programach monolitycznych między słowami kluczowymi struct... (ale bez tych słów); pliku z interfejsem K.mli (opcjonalnie), który jest ciągiem specyfikacji znajdujących się w programach monolitycznych między słowami kluczowymi sig... (ale bez tych słów). Inna jednostka kompilacji L może się odwoływać do K jak do struktury w programie monolitycznym, używając notacji kropkowej K.x. Przykład (wykonywać w oknie kom). Pliki źródłowe (są w folderze oddzielnie ): stack.mli, stack.ml, teststack.ml. Kroki kompilacji: ocamlc c stack.mli (tworzy plik stack.cmi) ocamlc c stack.ml (tworzy plik stack.cmo) ocamlc c teststack.ml (tworzy plik teststack.cmo) ocamlc o teststack stack.cmo teststack.cmo (łączenie plików obiektowych, kolejność jest istotna!) Uruchamianie powstałego programu (w kodzie pośrednim, ang. bytecode): ocamlrun teststack Możliwa jest też kompilacja do kodu rodzimego (ang. native code), patrz OCaml manual. Zdzisław Spławski Programowanie funkcyjne 29

Zadania kontrolne Algebraiczna specyfikacja kolejki nieskończonej empty : -> Queue enqueue : Elem * Queue -> Queue first : Queue -> Elem dequeue : Queue -> Queue isempty : Queue -> bool For all q:queue, e1,e2: Elem isempty (enqueue (e1,q)) = false isempty (empty) = true dequeue (enqueue(e1,enqueue(e2,q))) = enqueue(e1,dequeue(enqueue(e2,q))) dequeue (enqueue(e1,empty)) = empty dequeue (empty) = empty first (enqueue(e1,enqueue(e2,q))) = first(enqueue(e2,q)) first (enqueue(e1,empty)) = e1 first (empty) = ERROR Zdzisław Spławski Programowanie funkcyjne 30

Zadania kontrolne 1. Dana jest następująca sygnatura dla kolejek niemodyfikowalnych (czysto funkcyjnych!). module type QUEUE_FUN = sig (* This module implements queues (FIFOs)in a functional way. *) type 'a t (* The type of queues containing elements of type ['a]. *) exception Empty of string (* Raised when [first] is applied to an empty queue. *) val empty: unit -> 'a t (* [empty()] returns a new queue, initially empty. *) val enqueue: 'a * 'a t -> 'a t (* [enqueue (x,q)] adds the element [x] at the of queue [q]. *) val dequeue: 'a t -> 'a t (* [dequeue q] removes the first element in queue [q] *) val first: 'a t -> 'a (* [first q] returns the first element in queue [q] without removing it from the queue, or raises [Empty] if the queue is empty.*) val isempty: 'a t -> bool (* [isempty q] returns [true] if queue [q] is empty, otherwise returns [false]. *) ;; Zdzisław Spławski Programowanie funkcyjne 31

Zadania kontrolne a) Napisz strukturę, zgodna z powyższą sygnaturą, w której kolejka jest reprezentowana przez typ konkretny type 'a t = EmptyQueue Enqueue of 'a * 'a t b) Napisz strukturę, zgodna z powyższą sygnaturą, w której kolejka jest reprezentowana przez listę. c) Reprezentacja z punku a) i b) jest mało efektywna, ponieważ operacja wstawiania do kolejki ma złożoność liniową. W lepszej reprezentacji kolejka jest reprezentowana przez parę list. Para list ([x 1 ; x 2 ;...; x m ], [y 1 ; y 2 ;...; y n ]) reprezentuje kolejkę x 1 x 2... x m y n... y 2 y 1. Pierwsza lista reprezentuje początek kolejki, a druga koniec kolejki. Elementy w drugiej liście są zapamiętane w odwrotnej kolejności, żeby wstawianie było wykonywane w czasie stałym (na początek listy). enqueue(y, q) modyfikuje kolejkę następująco: (xl, [y 1 ; y 2 ;...; y n ]) (xl, [y;y 1 ; y 2 ;...; y n ]). Elementy w pierwszej liście są pamiętane we właściwej kolejności, co umożliwia szybkie usuwanie pierwszego elementu. dequeue(q) modyfikuje kolejkę następująco: ([x 1 ; x 2 ;...; x m ], yl) ([x 2 ;...; x m ], yl). Kiedy pierwsza lista zostaje opróżniona, druga lista jest odwracana i wstawiana w miejsce pierwszej: ([], [y 1 ; y 2 ;...; y n ]) ([y n ;... y 2 ; y 1 ], []). Reprezentacja kolejki jest w postaci normalnej, jeśli nie wygląda tak: ([], [y 1 ; y 2 ;...; y n ]) dla n 1. Wszystkie operacje kolejki mają zwracać reprezentację w postaci normalnej, dzięki czemu pobieranie wartości pierwszego elementu nie spowoduje odwracania listy. Odwracanie drugiej listy po opróżnieniu pierwszej też może się wydawać kosztowne. Jeśli jednak oszacujemy nie koszt pesymistyczny (oddzielnie dla każdej operacji kolejki), ale koszt zamortyzowany (uśredniony dla całego czasu istnienia kolejki), to okaże się, że koszt operacji wstawiania i usuwania z kolejki jest stały. Napisz strukturę, zgodna z powyższą sygnaturą, w której kolejka jest reprezentowana w postaci pary list. Zdzisław Spławski Programowanie funkcyjne 32

Zadania kontrolne 2. Dana jest następująca sygnatura dla kolejek modyfikowalnych (w pliku queue_mut.mli). (* This module implements queues (FIFOs) with in-place modifications. *) type 'a t (* The type of queues containing elements of type ['a]. *) exception Empty of string (* Raised when [first q] is applied to an empty queue [q]. *) exception Full of string (* Raised when [enqueue (x,q)] is applied to a full queue [q]. *) val empty: int -> 'a t (* [empty n] returns a new queue of length [n], initially empty. *) val enqueue: 'a * 'a t -> unit (* [enqueue (x,q)] adds the element [x] at the of a queue [q]. *) val dequeue: 'a t -> unit (* [dequeue q] removes the first element in queue [q] *) val first: 'a t -> 'a (* [first q] returns the first element in queue [q] without removing it from the queue, or raises [Empty] if the queue is empty.*) val isempty: 'a t -> bool (* [isempty q] returns [true] if queue [q] is empty, otherwise returns [false]. *) val isfull: 'a t -> bool (* [isfull q] returns [true] if queue [q] is full, otherwise returns [false]. *) Zdzisław Spławski Programowanie funkcyjne 33

Zadania kontrolne Napisz strukturę, zgodną z powyższą sygnaturą, w której kolejka jest reprezentowana przez tablicę cykliczną. Wykorzystaj mechanizm oddzielnej kompilacji! Nie zapomnij o testach! Program testowy powinien wypisać menu, zawierające wszystkie operacje kolejki i wykonać wybraną operację. Kolejka reprezentowana przez tablicę cykliczną kolejka pusta f r kolejka pełna f f f r a a a b c b r r Zdzisław Spławski Programowanie funkcyjne 34