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

Podobne dokumenty