Opowieść o programowaniu w Adzie



Pobieranie 0.69 Mb.
Strona16/16
Data28.04.2016
Rozmiar0.69 Mb.
1   ...   8   9   10   11   12   13   14   15   16

Rekordy z wyróżnikami




Rekordy z wariantami


Rekordy z wariantami to takie rekordy, których zawartość, to znaczy ilość i rodzaj składowych, różni się w zależności od wartości jednego z pól - wyróżnika. Wyobraźmy sobie na przykład, że chcemy utworzyć bazę danych przechowującą imiona, nazwiska i daty urodzenia osób, a w przypadku, gdy dana osoba posiada prawo jazdy - również jego kategorie. Można to zrobić tworząc dwa odrębne typy rekordowe (osoby z prawem jazdy i bez niego), ale wówczas podczas pisania programów wykorzystujących takie typy napotykamy na wiele istotnych ograniczeń. Nie można umieścić wszystkich osób w jednej tablicy, zapisać do jednego pliku itp... Problem ten może rozwiązać zadeklarowanie typu rekordowego w następujący sposób:
type kategoria is (A, B, C, D, E, T);
type tablica_kategorii is array (kategoria) of boolean;
type osoba (kierowca:boolean) is record

imie, nazwisko: string (1..10):=(others=>' ');

rok_urodzenia: positive;

case kierowca is

when true => prawo_jazdy: tablica_kategorii;

when false => null;

end case;

end record;


Typ rekordowy osoba składa się z wyróżnika (ang. discriminant), traktowanego jako normalne pole rekordu, oraz dwóch części - stałej (ang. invariant part), tzn. pól imię, nazwisko i rok_urodzenia, które występują we wszystkich rekordach tego typu, oraz części wariantowa (ang. variant part), mającej postać znanej nam instrukcji wyboru - case. W zależności od wartości wyróżnika - pola kierowca - rekord zawiera pole o nazwie prawo_jazdy lub nie zawiera nic więcej. W definicji typu rekordowego część wariantowa znajdować się musi po części stałej. Jej tworzeniem rządzą zasady podobne, jak dla instrukcji case - musimy wyczerpać wszystkie możliwe wartości wyróżnika, w przypadku gdy któraś z tych wartości nie powoduje dodania do rekordu nowych składowych używamy słowa kluczowego null. Jeżeli dla kilku wartości dodajemy takie same pola, możemy napisać
when wartość1 | wartość2 | ... => pole : typ_pola;

when wartość1 .. wartość2 => pole : typ_pola;


a także
when others => pole : typ_pola;
czy
when others => null;
"When others" musi być ostatnią pozycją w instrukcji wyboru.

Nie można deklarować pól o takich samych nazwach w różnych miejscach instrukcji wyboru. Poniższa deklaracja

type dni_tyg is (pon, wt, sw, czw, pt, sob, nie);
type jakis_typ (k:dni_tyg) is record

pole1,pole2:integer;

case k is

when sob|nie => pole3 : float;

when pon => pole3 : float;

pole4 : integer;

when others => null;

end case;

end record;
jest nieprawidłowa (pole o nazwie pole3 występuje w dwóch miejscach części wariantowej).

Zmienne należące do zdefiniowanego wcześciej typu osoba deklarujemy określając wartość wyróżnika:


sasiadka : osoba (false); -- notacja pozycyjna

sasiad : osoba (kierowca => true); -- notacja nazywana


Dany rekord (czyli dana zmienna czy stała) zostaje w ten sposób niejako "ograniczona" (ang. constrained) - wyróżnik zachowuje się tutaj jak stała; nie można już zmienic jego wartości, a zatem zmienna sasiadka pozostanie zawsze rekordem złożonym z czterech pól, a sasiad - z pięciu. Powyższy sposób deklaracji przypomina nieco deklarowanie zmiennych należących do anonimowego typu tablicowego. Podobnie jak tam zmienna, dla której określamy wartość wyróżnika, zostaje zaliczona automatycznie do jednego z podtypów typu osoba (tutaj ma on dwa podtypy , dla wartości wyróżnika kierowca odpowiednio true lub false). Oczywiście - znów podobnie jak w przypadku tablic - możemy określić te podtypy w sposób bezpośredni, pisząc:
subtype osoba_bez_prawa_jazdy is osoba(kierowca=>false);

subtype osoba_z_prawem_jazdy is osoba(kierowca=>true);


sasiadka: osoba_bez_prawa_jazdy;

sasiad: osoba_z_prawem_jazdy;


Nadawanie wartości poszczególnym składowym rekordu przebiega w standardowy sposob:
sasiadka.imie:="Kazimiera "

sasiadka.nazwisko:="Kowalska ";


lub przy użyciu agregatu rekordu z notacją pozycyjną czy nazywaną (jak dla rekordów bez wariantów).
sasiadka:=(false, "Kazimiera ", "Kowalska ", 1955);

sasiad:=(kierowca=>true, imie=>"Jan ", nazwisko=>"Kowalski ",

rok_urodzenia=>1954,prawo_jazdy=>(true,true,others=>false));
Zauważmy, że mimo określenia wartości wyróżnika w momencie deklaracji występuje on jeszcze raz w agregacie rekordu (oczywiście jego wartość w agregacie i deklaracji musi być taka sama, w przeciwnym razie wystąpi Constraint_Error). Agregat rekordu może być również użyty podczas deklaracji zmiennej, powodując "określenie" rekordu:
sasiadka : osoba := (false, "Kazimiera ", "Kowalska ", 1955);
W ten sposób również otrzymujemy rekord o stałej wartości wyróżnika.
Wyróżnik rekordu traktowany jest jak zwykłe pole (tutaj - o stałej wartości) i możemy się do niego odwoływać:
if sasiad.kierowca=true then ...
Próby odwołania się do nieistniejących pól rekordu (np. sasiadka.prawo_jazdy) powodują wystąpienie błędu Constraint_Error.
Gdyby powyższy sposób korzystania z rekordów z wyróżnikami był jedynym możliwym, byłoby to bardzo niewygodne. Sąsiadka skazana byłaby na życie bez prawa jazdy - a co miałby zrobić posiadacz bazy danych, gdyby udało jej się uzyskać ten dokument?... Możliwe jest więc deklarowanie rekordów, których wyróżniki mogą zmieniać swoją wartość. Są to rekordy tzw. "nie określone" (ang. unconstrained) - deklarowane bez ustalania wartości wyróżnika. Jest to możliwe dzięki nadaniu wyróżnikowi wartości domyślnej podczas definiowania typu rekordowego (wiadomo wówczas, ile pamięci zarezerwować):
type osoba (kierowca:boolean:=false) is record

imie, nazwisko: string (1..10):=(others=>' ');

rok_urodzenia: positive;

case kierowca is

when true => prawo_jazdy: tablica_kategorii;

when false => null;

end case;

end record;


Jeśli zadeklarujemy teraz zmienną pisząc
teściowa : osoba;
otrzymamy rekord "nie określony", dla którego została przyjęta domyślna wartość pola kierowca - false. Zawartość tego pola możemy jednak wielokrotnie zmieniać, używając agregatu rekordu (użycie tylko pojedynczego pola - wyróżnika - jest nieprawidłowe):
-- prawidłowy sposób zmiany:
tesciowa:=(kierowca=>true, imie=>"Kleopatra ",

nazwisko=> "Kowalska ", rok_urodzenia=> 1930,

prawo_jazdy=>(C|D|T=>true, others=>false));

-- nieprawidłowo:


tesciowa.kierowca :=true;

tesciowa.imie:="Kleopatra ";


Oczywiście można takiemu rekordowi nadac wartość początkową już w chwili deklaracji
ciotka_sasiada : osoba := (kierowca=>true, imie=>"Eleonora ",

nazwisko=>"Kowalska ", rok_urodzenia=>1950,

prawo_jazdy=>(B=>true,others=>false));
a mimo to pozostaje on rekordem "nie określonym", z możliwością zmiany wartości wyróżnika:
ciotka_sasiada := (kierowca=>false, imie=>ciotka_sasiada.imie,

nazwisko=>ciotka_sasiada.nazwisko,

rok_urodzenia=>ciotka_sasiada.rok_urodzenia);

Możliwe jest też nadawanie wartości (i ewentualna zmiana typu wyrożnika) poprzez podstawianie całych rekordów:


narzeczony_Jasi : osoba := (true, "Jan ", "Iksinski ",

(A|B=>true,others=>false));

narzeczony_Kasi : osoba; -- domyślnie - nie ma prawa jazdy

-- ale że wszystko się zmienia...

narzeczony_Kasi := narzeczony_Jasi;

-- nastąpiła nie tylko zmiana narzeczonego,

-- ale i wyróżnika rekordu...
Posiadanie przez wyróżnik wartości domyślnej nie oznacza, że wszystkie deklarowane zmienne tego typu będą "nie określone". Zawsze możliwe jest ustalenie obowiązującej dla danej zmiennej wartości wyróżnika i "ograniczenie" rekordu, podobnie jak w przypadku typów rekordowych nie mających wartości domyślnej wyróżnika:
niemowlak : osoba (kierowca => false);
Reguły te mogą wydawać się trudne do zapamiętania. Istnieje na szczęście atrybut 'Constrained (sposób użycia: zmienna'Constrained) przyjmujący wartość true, gdy dany rekord jest "określony" (constrained - nie można zmienić wartości jego wyróżnika), a false - w przeciwnym przypadku.
if not ktos'constrained then

ktos:=(kierowca=>...,...);

end if;

-- zmiana wartości wyróżnika tylko wtedy, gdy jest to możliwe,



-- mimo ew. ostrzeżeń w czasie kompilacji

-- nie będzie błędu wykonania


Rekordy należące do tego samego typu (niezależnie od tego, czy są ograniczone, czy nie) możemy porównywać ze sobą przy pomocy operatorów = i /= (nawet jeśli mają różne wartości wyróżnika).
Możliwe jest definiowanie typów rekordowych posiadających więcej niż jeden wyróżnik. Rekord może posiadać jednak tylko jedną część wariantową, umieszczoną na końcu struktury rekordu. Kolejne konstrukcje case mogą jedynie być w niej zagnieżdżone. Przy ich tworzeniu obowiązują dotychczasowe zasady:
type kategoria is (A, B, C, D, E, T);

type tablica_kategorii is array (kategoria) of boolean;

type p is (K, M);
-- nieprawidłowa definicja

-- dwie części wariantowe


type osoba1 (plec:p; kierowca:boolean) is record

imie, nazwisko: string (1..10):=(others=>' ');

rok_urodzenia: positive;

case plec is

when K => nazwisko_panienskie: string(1..10):=(others=>' ');

when M =>null;

end case;

case kierowca is

when true => prawo_jazdy: tablica_kategorii;

when false => null;

end case;

end record;


-- nieprawidłowa definicja

-- powtarzające się nazwy pól
type osoba2 (plec:p; kierowca:boolean) is record

imie, nazwisko: string (1..10):=(others=>' ');

rok_urodzenia: positive;

case plec is

when K => nazwisko_panienskie: string(1..10):=(others=>' ');

case kierowca is

when true => prawo_jazdy: tablica_kategorii;

when false => null;

end case;

when M =>

case kierowca is

when true => prawo_jazdy: tablica_kategorii;

when false => null;

end case;

end case;

end record;


-- definicja prawidlowa
type osoba2 (plec:p; kierowca:boolean) is record

imie, nazwisko: string (1..10):=(others=>' ');

rok_urodzenia: positive;

case plec is

when K => nazwisko_panienskie: string(1..10):=(others=>' ');

case kierowca is

when true => prawo_jazdy: tablica_kategorii;

when false => null;

end case;

when M =>

case kierowca is

when true => pj: tablica_kategorii;

when false => null;

end case;

end case;

end record;


Jak widać w sytuacji, gdy wyróżniki są od siebie niezależne, trudno jest w elegancki sposób zdefiniować typ rekordowy - musimy powtarzać pole, a to wymaga użycia w każdym przypadku instrukcji case innej nazwy. Może pomóc tutaj deklaracja dwustopniowa (jak poniżej, zobacz "Inne zastosowania wyróżników").
type os (pl: p) is record

imie, nazwisko: string (1..10):=(others=>' ');

rok_urodzenia: positive;

case pl is

when K => nazwisko_panienskie: string(1..10):=(others=>' ');

when M =>null;

end case;

end record;


type osoba1 (plec:p; kierowca:boolean) is record

dane : os(plec);

case kierowca is

when true => prawo_jazdy: tablica_kategorii;

when false => null;

end case;

end record;
Gdy wartości wyróżnika nie są od siebie zależne, definicja wygląda dużo ładniej:
type rodzaj_psa is (obronny, pasterski, mysliwski, pokojowy);
type pies (rasowy:boolean; medalista:boolean) is record

imie:string(1..10);

wlasciciel:string(1..20);

case rasowy is

when true => rodzaj: rodzaj_psa;

rasa: string(1..10);

case medalista is

when true => ilosc_medali:positive;

when false => null;

end case;

when false=> null;

end case;

end record;
Agregaty rekordów należących do typu pies mogą mieć jedną z czterech postaci:
pies1 : pies := (rasowy=>false, medalista=>false,

imie=>"Azor ", wlasciciel=>"Jan Kowalski ");

pies2 : pies := (rasowy=>false, medalista=>true,

imie=>"Burek ", wlasciciel=>"Jan Kowalski ");

pies3 : pies := (rasowy=>true, medalista=>false,

imie=>"Cezar ", wlasciciel=>"Jan Kowalski ",

rodzaj=>mysliwski, rasa=>"foksterier");

pies4 : pies:= (rasowy=>true, medalista=>true,

imie=>"Funia ", wlasciciel=>"Jan Kowalski ",

rodzaj=>pokojowy, rasa=>"pekinczyk ",

ilosc_medali=>10);
Podobnie jak przedtem, także i w przypadku rekordów o kilku wyróżnikach możemy deklarować rekordy "określone" i "nie określone", jeżeli wyróżnikowi została nadana wartość domyślna. Jeśli wartość domyślną posiada jeden z wyróżników, muszą ją mieć również pozostałe.

Rekordy ze składowymi o zmiennym rozmiarze


Innym sposobem wykorzystania wyróżników rekordu jest określanie za ich pomocą rozmiaru składowych, na przykład długości stringa czy rozmiaru tablicy, jak w poniższym przykładzie:
type dane (dl_nazwy:positive) is record

zaklad_pracy:string(1..dl_nazwy);

rok_zalozenia: positive;

end record;

type osoba is record

imie, nazwisko:string(1..10);

rok_ur:positive;

end record;


type tabela_pracownikow is array (positive range <>) of osoba;

type firma (il_pracownikow:positive) is record

nazwa_firmy : string(1..20);

zatrudnieni : tabela_pracownikow (1..il_pracownikow);

end record;


W obu przypadkach jedno z pól rekordu należy do typu tablicowego o nie określonym rozmiarze (string jest, jak pamiętamy, zdefiniowany jako taka tablica o elementach typu character). Jeżeli wyróżnik nie ma nadanej wartości domyślnej, musimy podać ją w chwili dekaracji zmiennej.
zaklad1 : dane(dl_nazwy=>50);

zaklad2 : dane(20);

zaklad3 : dane := (dl_nazwy=>13, zaklad_pracy=>"WKS Mostostal",

rok_zalozenia=> 1974);


Jak pamiętamy, zmienna należąca do typu tablicowego o nie określonym rozmiarze musi być elementem jakiegoś jego podtypu, określonego przez rozmiar. Zatem zmienne zadeklarowane powyżej są "określone" i nie mogą zmieniać wartości wyróżnika, a więc i rozmiaru swoich składowych. Jeśli natomiast wyróżnik ma wartość domyślną:
type dane (dl_nazwy:positive:=20) is record

zaklad_pracy:string(1..dl_nazwy);

rok_zalozenia: positive;

end record;


to zmienne deklarujemy bądź podobnie jak poprzednio
zaklad1 : dane(dl_nazwy=>50);

zaklad2 : dane(20);


- wówczas są one rekordami "określonymi", a długość nazwy określiliśmy na zawsze (podobnie jak w przypadku rekordów z wariantami), bądź też
zaklad3 : dane; -- domyślna wartość wyróżnika - 20

zaklad4 : dane:= (dl_nazwy=>13, zaklad_pracy=>"WKS Mostostal",

rok_zalozenia=> 1974);
otrzymując rekord "nie określony":
zaklad3 := zaklad4; -- zmiana długości z 10 na 13
Oczywiście wyróżników może być więcej niż jeden. Mogą odnosić się one do tego samego lub do różnych pól rekordu:
type tabela_pracownikow is array (positive range <>) of osoba;

type firma (dl_nazwy,il_pracownikow:positive) is record

nazwa_firmy : string(1..dl_nazwy);

zatrudnieni : tabela_pracownikow (1..il_pracownikow);

end record;
f1 : firma(dl_nazwy=>3, il_pracownikow=>10);

f2 : firma(12,5);


Zdefiniowaliśmy tu dwa typy - tablicowy o nie określonym rozmiarze (tabela_pracownikow) i rekordowy. Niestety nie można utworzyć tylko jednego typu, pisząc
type firma2 (dl_nazwy,il_pracownikow:positive) is record

nazwa_firmy : string(1..dl_nazwy);

zatrudnieni : array (1..il_pracownikow) of osoba;

end record;


ponieważ żadne z pól rekordu nie może być zadeklarowane jako tablica anonimowa.
Wyróżniki rekordu nie mogą występować w deklaracji typu jako elementy jakiegoś wyrażenia. Napisanie na przykład
type firma (dl_nazwy,il_pracownikow:positive) is record

...


zatrudnieni : tabela_pracownikow1 (1..il_pracownikow+1);

end record;


jest niedozwolone.

Inne zastosowania wyróżników.


Poznaliśmy dotychczas następujące zastosowania wyróżników rekordów:

- do tworzenia rekordów z wariantami (wyróżnik określa wówczas zawartość rekordu),

- do tworzenia rekordów, których pola mają rozmiar zależny od wartości wyróżnika.

Istnieją jeszcze trzy inne zastosowania :

- tworzenie rekordów, w których pewne pola zachowują się jak stałe,

- inicjowanie wartości pewnych pól rekordu,

- "określanie" rekordu z wyróżnikiem zagnieżdżonego w danym rekordzie jako jedno z jego pól.

Oto przykłady:


Inicjowanie wartości pola rekordu:
type ograniczenie (predkosc:natural) is record

max_predkosc : natural := predkosc;

end record;
obszar_zabudowany : ograniczenie(60);

...
put (obszar_zabudowany.max_predkosc); -- wypisze 60

obszar_zabudowany.max_predkosc:=50;

put (obszar_zabudowany.max_predkosc); -- wypisze 50


Jak widać, wartość wyróżnika można tutaj zmieniać (nawet jeśli zadeklarowaliśmy rekord jako "określony").
Tworzenie w rekordzie pól zachowujących się jak stałe:
type plec is (M, K);
-- plec i rok urodzenia nie moga sie zmienic, -- waga i wzrost - tak

type czlowiek (p: plec;rok_urodzenia:positive) is record

waga, wzrost: positive;

end record;

Iksinski:czlowiek(p=>M, rok_urodzenia=>1966);

... -- niedozwolone

Iksinski:=(p=>M, rok_urodzenia=>1965, wzrost=>187, waga=>99);
Wyróżnik używany do "określenia" rekordu będącego polem definiowanego rekordu:
type tabela_pracownikow is array (positive range <>) of osoba;

type firma (il_pracownikow:positive) is record

nazwa_firmy : string(1..20);

zatrudnieni : tabela_pracownikow (1..il_pracownikow);

end record;
type pracodawca (ilosc_zatrudnionych:natural) is record

imie,nazwisko : string(1..10);

jego_firma : firma(ilosc_zatrudnionych);

end record;


********************************************************



Algorytmy 1

Pierwsze programy 2

Trochę matematyki 8

Funkcje matematyczne 14

Typy liczbowe i ich zakresy 18

Instrukcja warunkowa 21

Operatory logiczne. Kolejność działań. 24

Typ boolean 26

Typy znakowe 27

Typ wyliczeniowy 30

Podtypy 32

Definiowanie nowych typów liczbowych 36

Atrybuty typów 38

Instrukcja wyboru 44

Instrukcje pętli 48

Typ łańcuchowy 58

Tablice 65

Tablice jednowymiarowe 65

Tablice wielowymiarowe 74

Tablice anonimowe (anonimowy typ tablicowy) 78

Tablice dynamiczne 79

Typy tablicowe bez okeślenia rozmiaru 81

Operacje na tablicach 85

Rekordy 87

Rekordy "zwykłe" (bez wyróżników) 87

Struktury danych "wielokrotnie złożone" 93

Rekordy z wyróżnikami 100

Rekordy z wariantami 100

Rekordy ze składowymi o zmiennym rozmiarze 107

Inne zastosowania wyróżników. 109





1   ...   8   9   10   11   12   13   14   15   16


©absta.pl 2016
wyślij wiadomość

    Strona główna