i zrobić val
Transkrypt
i zrobić val
Wykład 10 Obiekty w języku funkcyjnym Definiowanie klas Typy klas Tworzenie egzemplarzy (instancji) klas Używanie metod obiektu Inicjatory Relacje między klasami Agregacja (złożenie klas; relacja _ma_) Dziedziczenie (relacja _jest_) Referencje self (this) i super Wiązanie dynamiczne Wielodziedziczenie Klasy polimorficzne Zdzisław Spławski Programowanie funkcyjne 1 Obiekty w języku funkcyjnym Metody i klasy abstrakcyjne Metody prywatne Typy obiektów Koercja typów Typy otwarte Równość obiektów Obiekty funkcyjne Zdzisław Spławski Programowanie funkcyjne 2 Definiowanie klas Poniższa składnia pokazuje jeden z najprostszych sposobów definiowania klas. class nazwa p1 ... pn = lub class nazwa = fun p1 ... pn -> object object : : zmienne instancyjne zmienne instancyjne : : metody metody : : end end p1 ... pn są parametrami (jedynego) konstruktora klasy. Klasa może oczywiście być bezparametrowa. Zmienne instancyjne są deklarowane następująco: val nazwa = wyrażenie lub val mutable nazwa = wyrażenie Metody należy deklarować zgodnie z poniższą składnią: method nazwa p1 ... pn = wyrażenie Zdzisław Spławski Programowanie funkcyjne 3 Przykład: klasa color # type color = Black | Blue | Green;; type color = Black | Blue | Green # class color = object val mutable color = Black method getColor = color method setColor newColor = color <- newColor method toString = match color with Black -> "Black" | Blue -> "Blue" | Green -> "Green" end;; class color : object val mutable color : color method getColor : color method setColor : color -> unit method toString : string end Zdzisław Spławski Programowanie funkcyjne 4 Typy klas W wygenerowanym typie klasy jest widoczne pole val mutable color : color. Mimo to nie jest ono dostępne na zewnątrz klasy, ponieważ w języku OCaml wszystkie pola są prywatne (lub chronione). Można użyć własnego typu klasy w celu ukrycia tego pola. Za pomocą własnego typu klasy nie można ukryć metod publicznych. Typy klas można porównać do sygnatur dla modułów. Można też zauważyć pewną analogię między typami klas i interfejsami w Javie. # class type colorType = object method getColor : color method setColor : color -> unit method toString : string end;; class type colorType = object method getColor : color method setColor : color -> unit method toString : string end Zdzisław Spławski # class type pointType = object method getX : int method setX : int -> unit method reset : unit method toString : string end;; class type pointType = object method getX : int method reset : unit method setX : int -> unit method toString : string end Programowanie funkcyjne 5 Przykład: klasa color # class color : colorType = object val mutable color = Black method getColor = color method setColor newColor = color <- newColor method toString = match color with Black -> "Black" | Blue -> "Blue" | Green -> "Green" end;; class color : colorType Tworzenie egzemplarzy (instancji) klas Obiekty są tworzone za pomocą operatora new. new klasa wyr1 ... wyrn # let c = new color;; val c : color = <obj> Zdzisław Spławski Programowanie funkcyjne 6 Używanie metod obiektu (wysyłanie komunikatów do obiektu) obiekt#metoda p1 ... pn # # # - c#getColor;; : color = Black c#setColor Green;; : unit = () c#getColor;; : color = Green Dwa obiekty są uważane za równe tylko w przypadku fizycznej tożsamości. # # - c = c;; : bool = true new color = new color;; : bool = false Obiektami można manipulować za pomocą funkcji polimorficznych tak jak innymi wartościami . # let l = [c];; val l : color list = [<obj>] # (List.hd l)#getColor;; - : color = Green Zdzisław Spławski Programowanie funkcyjne 7 Przykład: sparametryzowana klasa point # class point (xInit : int) : pointType = object val mutable x = xInit method getX = x method setX newX = x <- newX method reset = x <- xInit method toString = "x="^string_of_int x end;; class point : int -> pointType # let p = new point 5;; val p : point = <obj> # p#getX;; - : int = 5 # p#setX (-2);; - : unit = () # p#toString;; - : string = "x=-2" # p#reset;; - : unit = () # p#getX;; - : int = 5 Zdzisław Spławski Programowanie funkcyjne 8 Wartościowanie wyrażenia przed utworzeniem obiektu Można użyć konstrukcji let ... in w celu wartościowania wyrażenia przed utworzeniem obiektu. W klasie pointGrid punkty są ustawiane w oczkach siatki, zadanej pierwszym parametrem. # class pointGrid grid xInit : pointType = let origin = (xInit/grid)*grid in object val mutable x = origin method getX = x method setX newX = x <- ((newX)/grid)*grid method reset = x <- origin method toString = "x="^string_of_int x end;; class pointGrid : int -> int -> pointType # let pG = new pointGrid 10 5;; val pG : pointGrid = <obj> # pG#getX;; - : int = 0 # pG#setX 12;; - : unit = () # pG#getX;; - : int = 10 Zdzisław Spławski Programowanie funkcyjne 9 Autoreferencja i inicjatory Można również wartościować pewne wyrażenie natychmiast po utworzeniu obiektu. Takie wyrażenie musi być treścią ukrytej bezargumentowej metody o nazwie initializer. Często zachodzi potrzeba użycia metody klasy w innej metodzie tej samej klasy. W języku OCaml konieczne jest jawne użycie referencji do obiektu (odpowiednika this w C++ i w Javie). W języku OCaml jest to dowolnie wybrany identyfikator (zwykle self). # class pointVerbose (xInit : int) : pointType = object (self) val mutable x = xInit method getX = x method setX newX = x <- newX method reset = x <- xInit method toString = "x="^string_of_int x initializer Printf.printf ">> New point at [%s]\n" self#toString; end;; class pointVerbose : int -> pointType # let pV = new pointVerbose 5;; >> New point at [x=5] val pV : pointVerbose = <obj> Zdzisław Spławski Programowanie funkcyjne 10 Agregacja (złożenie klas; relacja „ma”) # class pixel xInit cInit = object val c = new color val mutable x = xInit method getX = x method getColor = c#getColor method setColor newColor = c#setColor newColor method setX newX = x <- newX method reset = x <- xInit; c#setColor cInit method toString = "x=" ^ string_of_int x ^ ", color=" ^ c#toString initializer c#setColor cInit end;; class pixel : int -> color -> object val c : color val mutable x : int method getColor : color method getX : int method reset : unit method setColor : color -> unit method setX : int -> unit method toString : string end Zdzisław Spławski Programowanie funkcyjne 11 Agregacja c.d. # let px = new pixel 5 Blue;; val px : pixel = <obj> # px#getColor;; - : color = Blue # px#setColor Green;; - : unit = () # px#toString;; - : string = "x=5, color=Green" # px#reset;; - : unit = () # px#toString;; - : string = "x=5, color=Blue" Zdzisław Spławski Programowanie funkcyjne 12 Dziedziczenie (1) inherit nazwa1 p1 ... pn [ as nazwa2 ] # class pixel xInit cInit = object (self) inherit point xInit as super (* umożliwia odwołanie do zastępowanej metody *) val c = new color method getColor = c#getColor method setColor newColor = c#setColor newColor method reset = super#reset; self#setColor cInit method toString = super#toString ^ ", color=" ^ c#toString initializer c#setColor cInit end;; class pixel : int -> color -> object val c : color method getColor : color method getX : int method reset : unit method setColor : color -> unit method setX : int -> unit method toString : string end Zdzisław Spławski Programowanie funkcyjne 13 Dziedziczenie (2) # let px = new pixel 5 Blue;; val px : pixel = <obj> # px#toString;; - : string = "x=5, color=Blue" # px#setColor Green;; - : unit = () # px#toString;; - : string = "x=5, color=Green" # px#reset;; - : unit = () # px#toString;; - : string = "x=5, color=Blue" Zdzisław Spławski Programowanie funkcyjne 14 Wiązanie dynamiczne metod (1) W klasie pixel' została zastąpiona metoda setColor. Odziedziczona metoda reset pozostała bez zmian. # class pixel' xInit cInit = object inherit pixel xInit cInit as super method setColor newColor = c#setColor newColor; print_string "??\n" end;; class pixel' : int -> color -> object val c : color method getColor : color method getX : int method reset : unit method setColor : color -> unit method setX : int -> unit method toString : string end Zdzisław Spławski Programowanie funkcyjne 15 Wiązanie dynamiczne metod (2) Metoda reset jest taka sama w klasach pixel i pixel’, ale jej zachowanie jest inne, ponieważ wykorzystuje ona inne metody setColor (wiązanie dynamiczne, późne wiązanie). Używając terminologii C++ wszystkie metody obiektów OCamla są wirtualne (podobnie jak w Javie). # let px = new pixel 5 Blue;; val px : pixel = <obj> # px#reset;; - : unit = () # let px' = new pixel' 5 Blue;; val px' : pixel' = <obj> # px'#toString;; - : string = "x=5, color=Blue" # px'#setX 8;; - : unit = () # px'#toString;; - : string = "x=8, color=Blue" # px'#reset;; ?? - : unit = () # px'#toString;; - : string = "x=5, color=Blue" Zdzisław Spławski Programowanie funkcyjne 16 Wielodziedziczenie (1) Najłatwiej zdefiniować klasę pixel wykorzystując dziedziczenie z klas point i color. # class pixel xInit cInit = object inherit point xInit as sPoint inherit color as sColor ^^^^^ method reset = sPoint#reset; sColor#setColor cInit initializer sColor#setColor cInit end;; Warning: the following methods are overriden by the inherited class: toString class pixel : int -> color -> object method getColor : color method getX : int method reset : unit method setColor : color -> unit method setX : int -> unit method toString : string end Zdzisław Spławski Programowanie funkcyjne 17 Wielodziedziczenie (2) Zwróć uwagę na ostrzeżenie kompilatora. Obie nadklasy mają metody toString. Która z nich jest używana w klasie pixel ? # let px = new pixel 5 Blue;; val px : pixel = <obj> # px#toString;; - : string = "Blue" Jak widać, jest to metoda z później odziedziczonej klasy color. W klasie pixel poprawna metoda toString musi wykorzystywać metody toString z obu nadklas, co wymagało nazwania tych nadklas w deklaracjach inherit. Uwaga 1: W ten sposób można się odwołać tylko do zastąpionych (przesłoniętych) metod; nie ma możliwości odwołania do przesłoniętych zmiennych. Uwaga 2: W klasie pochodnej metoda zastępująca musi mieć taki sam typ jak metoda zastępowana (nie ma przeciążania metod). Zdzisław Spławski Programowanie funkcyjne 18 Wielodziedziczenie (3) # class pixel xInit cInit = object inherit point xInit as sPoint inherit color as sColor ^^^^^ method reset = sPoint#reset; sColor#setColor cInit method toString = sPoint#toString ^ ", color=" ^ sColor#toString initializer sColor#setColor cInit end;; Warning: the following methods are overriden by the inherited class: toString class pixel : int -> color -> object method getColor : color method getX : int method reset : unit method setColor : color -> unit method setX : int -> unit method toString : string end Zdzisław Spławski Programowanie funkcyjne 19 Wielodziedziczenie (4) Kompilator wyświetla ostrzeżenie, ale teraz metoda toString działa poprawnie. # let px = new pixel 5 Blue;; val px : pixel = <obj> # px#toString;; - : string = "x=5, color=Blue" # px#setX 12;; - : unit = () # px#setColor Green;; - : unit = () # px#toString;; - : string = "x=12, color=Green" # px#reset;; - : unit = () # px#toString;; - : string = "x=5, color=Blue" Zdzisław Spławski Programowanie funkcyjne 20 Klasy polimorficzne Podobnie jak dla polimorficznych typów konkretnych, definiowanych z pomocą type, dla klas polimorficznych trzeba jawnie związać zmienne przebiegające typy. class ['a,'b, …] nazwa p1 ... pn = object … end;; # class ['a] cell (xInit : 'a) = object val mutable x = xInit method get = x method set newX = x <- newX end;; class ['a] cell : 'a -> object val mutable x : 'a method get : 'a method set : 'a -> unit end # let c1 = new cell 3;; val c1 : int cell = <obj> # c1#get;; - : int = 3 # let c2 = new cell (new pixel 3 Black);; val c2 : pixel cell = <obj> # c2#get#toString;; - : string = "x=3, color=Black" Zdzisław Spławski Programowanie funkcyjne 21 Typy klas W dalszych przykładach będzie używana nieco skrócona klasa point. Przypomnimy też klasy color i pixel. # type color = Black | Blue | Green;; type color = Black | Blue | Green # class type colorType = object method getColor : color method setColor : color -> unit method toString : string end;; class type colorType = object method getColor : color method setColor : color -> unit method toString : string end Zdzisław Spławski # class type pointType = object method getX : int method setX : int -> unit method toString : string end;; class type pointType = object method getX : int method setX : int -> unit method toString : string end Programowanie funkcyjne 22 Sparametryzowana klasa color # class color c = object val mutable color = c method getColor = color method setColor newColor = color <- newColor method toString = match color with Black -> "Black" | Blue -> "Blue" | Green -> "Green" end;; class color : color -> object val mutable color : color method getColor : color method setColor : color -> unit method toString : string end Zdzisław Spławski Programowanie funkcyjne 23 Sparametryzowana klasa point # class point xInit = object val mutable x = xInit method getX = x method setX newX = x <- newX method toString = "x="^string_of_int x end;; class point : int -> object val mutable x : int method getX : int method setX : int -> unit method toString : string end Zdzisław Spławski Programowanie funkcyjne 24 Klasa pixel # class pixel xInit cInit = object inherit point xInit as sPoint inherit color cInit as sColor method toString = sPoint#toString ^ ", color=" ^ sColor#toString end;; Characters 77-88: Warning: the following methods are overriden by the inherited class: toString inherit color cInit as sColor ^^^^^^^^^^^ class pixel : int -> color -> object val mutable color : color val mutable x : int method getColor : color method getX : int method setColor : color -> unit method setX : int -> unit method toString : string end Zdzisław Spławski Programowanie funkcyjne 25 Metody i klasy abstrakcyjne Klasy posiadające metody abstrakcyjne (zadeklarowane, ale nie zdefiniowane) są również abstrakcyjne i muszą być jawnie zadeklarowane jako abstrakcyjne. Nie można tworzyć obiektów dla klas abstrakcyjnych. class virtual nazwa p1 ... pn = object ... end method virtual nazwa : typ Jeśli zechcemy dodać do klasy point metodę, drukującą reprezentację tekstową punktu, to można to zrobić bezpośrednio. Ogólniejsze rozwiązanie polega na zdefiniowaniu klasy abstrakcyjnej, która może być wykorzystana w wielodziedziczeniu z każdą klasą, posiadającą metodę toString. Zdzisław Spławski Programowanie funkcyjne 26 Abstrakcyjna klasa printable # class virtual printable ()= object (self) method virtual toString : string method print() = print_string (self#toString); print_newline() end;; class virtual printable : unit -> object method print : unit -> unit method virtual toString : string end # new printable();; Characters 0-13: One cannot create instances of the virtual class printable Zdzisław Spławski Programowanie funkcyjne 27 Klasa pointPrintable # class pointPrintable xInit = object inherit point xInit inherit printable () end;; class pointPrintable : int -> object val mutable x : int method getX : int method print : unit -> unit method setX : int -> unit method toString : string end # let pp = new pointPrintable 4;; val pp : pointPrintable = <obj> # pp#print();; x=4 -: unit = () # pp#toString;; - : string = "x=4" Zdzisław Spławski Programowanie funkcyjne 28 Klasa pixelPrintable # class pixelPrintable xInit cInit= object inherit pixel xInit cInit inherit printable () end;; class pixelPrintable : int -> color -> object val mutable color : color val mutable x : int method getColor : color method getX : int method print : unit -> unit method setColor : color -> unit method setX : int -> unit method toString : string end # let pxp = new pixelPrintable 4 Green;; val pxp : pixelPrintable = <obj> # pxp#print();; x=4, color=Green - : unit = () Zdzisław Spławski Programowanie funkcyjne 29 Metody prywatne (1) Metody i pola prywatne są widoczne w typie klasy, ale nie występują w typach obiektów tej klasy. Metody prywatne mogą być wywoływane w innych metodach tej klasy i są dziedziczone. Odpowiadają one metodom chronionym (protected) w C++ i w Javie. # class pointGrid grid xInit = object (self) inherit point ((xInit/grid)*grid) as super method private leftGrid x = (x/grid)*grid method setX newX = super#setX (self#leftGrid newX) end;; class pointGrid : int -> int -> object val mutable x : int method getX : int method private leftGrid : int -> int method setX : int -> unit method toString : string end Zdzisław Spławski Programowanie funkcyjne 30 Metody prywatne (2) # let pG = new pointGrid 10 5;; val pG : pointGrid = <obj> # pG#getX;; - : int = 0 # pG#setX 12;; - : unit = () # pG#getX;; - : int = 10 # pG#leftGrid 6;; Characters 0-2: pG#leftGrid 6;; ^^ This expression has type pointGrid It has no method leftGrid Zdzisław Spławski Programowanie funkcyjne 31 Metody prywatne (3) Metody prywatne mogą zostać „upublicznione” w podklasie. Istnieje specjalna składnia, która to ułatwia (przykład z prawej strony). # class pointGrid' grid xInit = object inherit pointGrid grid xInit as super method leftGrid = super#leftGrid end;; class pointGrid' : int -> int -> object val mutable x : int method getX : int # class pointGrid'' grid xInit = object inherit pointGrid grid xInit method virtual leftGrid : _ end;; class pointGrid'' : int -> int -> object val mutable x : int method getX : int method leftGrid : int -> int method setX : int -> unit method toString : string end method leftGrid : int -> int method setX : int -> unit method toString : string end Zdzisław Spławski Programowanie funkcyjne 32 Metody prywatne (4) Sygnatura klasy może ukryć metody prywatne. # class pointGridRestricted grid xInit : pointType = object (self) inherit point ((xInit/grid)*grid) as super method private leftGrid x = (x/grid)*grid method setX newX = super#setX (self#leftGrid newX) end;; class pointGridRestricted : int -> int -> pointType # class pointGrid''' grid xInit = object inherit pointGridRestricted grid xInit as super method leftGrid = super#leftGrid end;; Characters 111-116: method leftGrid = super#leftGrid ^^^^^ This expression has no method leftGrid Zdzisław Spławski Programowanie funkcyjne 33 Typy obiektów Typ obiektu jest taki sam, jak nazwa klasy. Jest to jednak tylko skrót notacyjny. # let p = new point 5;; val p : point = <obj> # let px = new pixel 5 Blue;; val px : pixel = <obj> # let l = [p; px];; Characters 12-14: let l = [p; px];; ^^ This expression has type pixel but is here used with type point Only the first object type has a method getColor Zdzisław Spławski Programowanie funkcyjne 34 Koercja typów Typ obiektu to1 jest podtypem to2 jeśli: 1. to1 ma wszystkie metody to2, 2. typ każdej metody to1 znajdującej się w to2 jest podtypem tej metody w to2. Jak wynika z tej definicji, pojęcie podtypu to nie to samo, co dziedziczenie. Np. W klasie pixel możliwe byłoby zdefiniowanie wszystkich wymaganych metod jawnie bez dziedziczenia ich z klasy point. Mimo to, zgodnie z powyższa definicją, pixel byłby podtypem typu point. Koercja typów w języku OCaml jest przeprowadzana zawsze jawnie, zgodnie z poniższą składnią: (nazwa : podtyp :> nadtyp) lub (nazwa :> nadtyp) # let l = [p; (px :> point)];; val l : point list = [<obj>; <obj>] # let pixel2point p = (p : pixel :> point );; val pixel2point : pixel -> point = <fun> # let l1 = [p; pixel2point px];; val l1 : point list = [<obj>; <obj>] Nie jest możliwa koercja odwrotna, tj. rzutowanie w dół, ponieważ wymagałoby to dynamicznego sprawdzania typów (patrz Java). Zdzisław Spławski Programowanie funkcyjne 35 Typy otwarte # let object2string x = x#toString;; val object2string : < toString : 'a; .. > -> 'a = <fun> # object2string p;; - : string = "x=5" # object2string px;; - : string = "x=5, color=Blue" # let toPoint p = (p :> point );; val toPoint : #point -> point = <fun> # let l2 = [p; toPoint px];; val l2 : point list = [<obj>; <obj>] # let f (x : #point) = x#futureMethod;; val f : < futureMethod : 'a; getX : int; setX : int -> unit; toString : string; .. > -> 'a = <fun> Typ metody nie może być typem otwartym. Zdzisław Spławski Programowanie funkcyjne 36 Równość obiektów # p#getX;; - : int = 5 # px#getColor;; - : color = Blue # px#toString;; - : string = "x=5, color=Blue" # let p2 = pixel2point px;; val p2 : point = <obj> # p2#getX;; - : int = 5 # p2#getColor;; Characters 0-2: p2#getColor;; ^^ This expression has type point It has no method getColor # p2#toString;; - : string = "x=5, color=Blue" # p = p2;; (* Dwa obiekty są uważane za równe tylko w przypadku *) Zdzisław=Spławski - : bool false Programowanie (* fizycznej tożsamości *)funkcyjne 37 Obiekty funkcyjne (1) W programowaniu obiektowym stosuje się najczęściej styl imperatywny, w którym metody obiektu mogą modyfikować jego stan. Możliwe jest jednak również programowanie obiektowe w stylu funkcyjnym. Metoda musi wtedy zwracać zmienioną kopię obiektu, na rzecz którego została wywołana. Umożliwia to poniższa konstrukcja, w której identyfikatory oznaczają pola, których wartość uległa zmianie. {<ident1 = wyr1; ... ; identk = wyrk>} # class pointF (xInit : int) = object val x = xInit method getX = x method setX newX = {< x = newX >} end;; class pointF : int -> object ('a) val x : int method getX : int method setX : int -> 'a end 'a oznacza tutaj typ self. Zdzisław Spławski Programowanie funkcyjne 38 Obiekty funkcyjne (2) # let pf = new pointF 5;; val pf : pointF = <obj> # pf#getX;; - : int = 5 # (pf#setX 8)#getX;; - : int = 8 # pf#getX;; - : int = 5 Zdzisław Spławski Programowanie funkcyjne 39 Obiekty funkcyjne (3) # class pointGridF grid xInit = object (self) inherit pointF ((xInit/grid)*grid) as super method private leftGrid x = (x/grid)*grid method setX newX = super#setX (self#leftGrid newX) end;; class pointGridF : int -> int -> object ('a) val x : int method getX : int method private leftGrid : int -> int method setX : int -> 'a (* zwróć uwagę na ten typ *) end Zdzisław Spławski Programowanie funkcyjne 40 Obiekty funkcyjne (4) Jak widać poniżej, metoda setX, odziedziczona z klasy pointF zwraca teraz obiekt klasy pointGridF. Dlatego w klasie pointF typ self był oznaczony przez 'a. Jest to zawsze typ obiektu, na rzecz którego wywoływana jest metoda. Dla obiektu pf był to typ pointF, a dla obiektu pgf jest to typ pointGridF . # let pgf = new pointGridF 10 5;; val pgf : pointGridF = <obj> # pgf#getX;; - : int = 0 # let pgf2 = pgf#setX 12;; val pgf2 : pointGridF = <obj> (* zwróć uwagę na ten typ *) # pgf2#getX;; - : int = 10 # (pgf2#setX 12)#getX;; - : int = 10 Obiekty klasy pointF zachowują się tak samo, jak obiekty klasy badpointF, zdefiniowanej w następnym przykładzie. Jednak metoda setX, odziedziczona z klasy badpointF zwraca zawsze obiekt klasy badpointF zamiast zwracać obiekt odpowiedniej podklasy. Zdzisław Spławski Programowanie funkcyjne 41 Złe obiekty funkcyjne (1) # class badpointF (xInit : int) = object val x = xInit method getX = x method setX newX = new badpointF newX end;; class badpointF : int -> object val x : int method getX : int method setX : int -> badpointF end # let bpf = new badpointF 5;; val bpf : badpointF = <obj> # bpf#getX;; - : int = 5 # (bpf#setX 8)#getX;; - : int = 8 # bpf#getX;; - : int = 5 Zdzisław Spławski Programowanie funkcyjne 42 Złe obiekty funkcyjne (2) # class badpointGridF grid xInit = object (self) inherit badpointF ((xInit/grid)*grid) as super method private leftGrid x = (x/grid)*grid method setX newX = super#setX (self#leftGrid newX) end;; class badpointGridF : int -> int -> object val x : int method getX : int method private leftGrid : int -> int method setX : int -> badpointF (* zwróć uwagę na ten typ *) end Zdzisław Spławski Programowanie funkcyjne 43 Złe obiekty funkcyjne (3) # let bpgf = new badpointGridF 10 5;; val bpgf : badpointGridF = <obj> # bpgf#getX;; - : int = 0 # let bpgf2 = (bpgf#setX 12);; val bpgf2 : badpointF = <obj> (* zwróć uwagę na ten typ *) # bpgf2#getX;; - : int = 10 # (bpgf2#setX 12)#getX;; - : int = 12 Zdzisław Spławski Programowanie funkcyjne 44 Zadania kontrolne 1. Napisz polimorficzną klasę queueF dla kolejki funkcyjnej, reprezentowanej przez parę list (patrz wykład 8, zad.1c). Napisz program do interakcyjnego testowania klasy queueF. 2. Napisz polimorficzną klasę queueM dla kolejki modyfikowalnej, reprezentowanej przez tablicę cykliczną (patrz wykład 8, zad.2). Napisz program do interakcyjnego testowania klasy queueM. Zdzisław Spławski Programowanie funkcyjne 45