Opowiesc o programowaniu w Adzie

Transkrypt

Opowiesc o programowaniu w Adzie
Opowie
o programowaniu w Adzie
"Jak to zrobi , eby on (komputer) zrobił to, co ja chc ?!?" i "Dlaczego?" zadawane mniej lub
bardziej rozpaczliwym tonem to pytania bardzo cz sto towarzysz ce pocz tkom nauki programowania.
Niniejszy rozdział nie stanowi opisu j zyka - wiele istotnych informacji zostało pomini tych - jest
natomiast, jak wskazuje tytuł, opowie ci o programowaniu i j zyku adresowan do osób stykaj cych
si z tym zagadnieniem po raz pierwszy. A celem jest - aby po przeczytaniu pytania postawione na
pocz tku były zadawane tonem nieco mniej rozpaczliwym...
Algorytmy
Niezb dnym elementem nauki programowania jest umiej tno poprawnego sformułowania
rozwi zania problemu, czyli stworzenia odpowiedniego algorytmu. Algorytm składa si z ci gu
pojedynczych, prostszych operacji ustawionych w kolejno ci wykonywania. Powinien rozwi zywa
dany problem i ko czy swoje działanie po wykonaniu sko czonej ilo ci kroków. Z algorytmami
spotykamy si w yciu codziennym - mo e to by na przykład opis monta u jakiego urz dzenia czy
mebli doł czany jako instrukcja, czy przepis w ksi ce kucharskiej:
1.
we 4 jajka, szklank m ki, szklank cukru, proszek do pieczenia
2.
utrzyj ółtka z cukrem
3.
ubij pian z białek
..... itd
lub algorytm rozwi zywania równania kwadratowego:
1.
2.
3.
4.
5.
6.
7.
podaj współczynniki trójmianu a,b,c
sprawd , czy a /= 0. Je eli a = 0, to stwierdzamy, e to nie jest trójmian i przechodzimy
do punktu 7
oblicz wyró nik trójmianu delta = b^2-4ac
je eli delta < 0, to równanie nie ma rozwi zania w zbiorze liczb rzeczywistych,
przechodzimy do punktu 7
je eli delta = 0, to równanie ma jeden pierwiastek podwójny x = -b/(2a), przechodzimy
do punktu 7
je eli delta > 0, to równanie ma dwa pierwiastki x1 = (-b - pierwiastek(delta)) / (2a),
x2 = (-b + pierwiastek(delta)) / (2a), przechodzimy do punktu 7
zako czenie
albo znajdowanie najmniejszej liczby w ci gu n-elementowym
1.
2.
3.
4.
5.
6.
7.
8.
podaj n oraz a1, a2, ... , an
min := a1
m := 2
porównaj am z min. Je eli am < min to min := am
m := m+1
je eli m <= n, to id do punktu 4
wypisz min
koniec
Cz sto rozwi zuj c bardziej skomplikowane zagadnienia rozkładamy je na kilka prostszych,
"mniejszych" problemów, te problemy - na jeszcze mniejsze itd - a dojdziemy do etapu, gdy dla
ka dego zagadnienia łatwo nam ju utworzy odpowiedni algorytm. Jest to tak zwane projektowanie
programu z góry w dół, czyli top-down design.
Kolejnym etapem pisania programu jest zapisanie utworzonego algorytmu za pomoc instrukcji
danego j zyka programowania. U nas b dzie to Ada95.
Pierwsze programy
Pisanie programów rozpoczniemy od rzeczy najprostszych, na przykład od wypisania przez
komputer jakiego tekstu na ekranie:
-- pierwszy program w Adzie
-- wypisywanie "powitania" na ekranie
with ada.text_io;
procedure pr1 is
begin
ada.text_io.put("
Dzien dobry!!!
ada.text_io.new_line;
ada.text_io.put("
Milej nauki!
end pr1;
Program nosi nazw pr1. Jego wła ciwa tre
");
");
zawarta jest pomi dzy liniami
procedure pr1 is
a
end pr1;
Linia
with ada.text_io
stanowi tzw. specyfikacj kontekstu. Co to takiego? Otó poszczególne polecenia (funkcje i procedury,
b d ce wła ciwie małymi programami) zebrane s dla wygody w wi ksze, ł cz ce si tematycznie
grupy - pakiety, których mo e by bardzo wiele - zarówno standardowych, doł czanych do danej wersji
j zyka, jak i tworzonych przez samego programist . Tak zwana klauzula with stanowi informacj , które
z nich maj by widoczne dla programu. Tak wi c nasz program "widzi" tylko funkcje i procedury
zawarte w pakiecie o nazwie ada.text_io. Nie s one jednak dost pne bezpo rednio - nazw ka dej
z nich musimy poprzedzi nazw pakietu, z którego ona pochodzi, oddzielon kropk (np.
ada.text_io.put).
Bior c pod uwag to, co powiedzieli my, mo na patrz c na nazw ada.text_io postawi
sobie pytanie, sk d w takim razie kropka pomi dzy ada i text_io - czy by text_io pochodził
z ada? Stwiedzenie to nie jest pozbawione racji. W Adzie istnieje mo liwo tworzenia jednostek "dzieci" (child units). Nazwa jednostki - rodzica oddzielana jest wówczas kropk od nazwy jednostki dziecka.
Zale no
ta mo e by
kilkustopniowa (np. ada.text_io.integer_io,
ada.numerics.generic_elementary_functions). O mechanizmie child units dowiemy si
wi cej w dalszej cz ci ksi ki. Na razie u ywa b dziemy pełnych nazw pakietów - dzieci, czyli nazw
w postaci rodzic.dziecko, jak w powy szym programie.
Pakiet ada.text_io ma drug , równowa n nazw - text_io, nadan mu za pomoc tzw.
przemianowywania. Mo emy wi c u ywa w programach nazwy krótszej b d dłu szej, nale y jednak
robi to konsekwentnie. Program pr1 mo e mie posta
with text_io;
procedure pr1 is
begin
text_io.put("
Dzien dobry!!!
text_io.new_line;
text_io.put("
Milej nauki!
end pr1;
ale niedopuszczalne jest napisanie na przykład
81
");
");
with ada.text_io;
procedure pr1 is
begin
text_io.put("
end pr1;
Dzien dobry!!!
");
gdy klauzul with udost pniamy zasoby pakietu ada.text_io, a nie text_io.
Jeszcze słowo o nazewnictwie. Nasz program nazwali my pr1, u ywany w nim pakiet nosi
nazw text_io. W Adzie nazwa - czy to programu, czy pakietu, zmiennej, funkcji, procedury (co to
jest - dowiemy si pó niej, w ka dym razie ka da nadawana nazwa) musi spełnia pewne warunki.
Mo e by dowolnej długo ci, ale mo e zawiera tylko litery, cyfry i znaki podkre lenia, musi zaczyna
si od litery i nie mo e ko czy si znakiem podkre lenia ani zawiera dwóch znaków podkre lenia
nast puj cych bezpo rednio po sobie. Oczywi cie nie mo e by adnym ze słów kluczowych (s to
takie słowa jak np. begin, end, ich spis znajduje si w drugim rozdziale tej ksi ki). Wielkie i małe
litery nie s przez Ad rozró niane (tak wi c Pierwszy_Program i pierwszy_program to ta
sama nazwa). Oczywi cie dobrze jest, gdy nadawana nazwa z czym si kojarzy - ułatwia to czytanie
programu i nanoszenie ewentualnych poprawek nawet po upływie dłu szego czasu (mo na nazwa
zmienn np. xxppbrk12 zamiast podatek, ale kto pó niej odgadnie, co si pod ni kryje?...) Np.
nazwa text_io pochodzi od text input/output - a wi c nietrudno si domy li , do czego słu
zgromadzone w pakiecie "narz dzia".
Wró my do naszego programu. Pochodz ca z powy szego pakietu procedura put powoduje
wypisanie na ekranie tekstu b d cego jej argumentem. Argumenty (inaczej parametry) funkcji
i procedur umieszczamy w nawiasach. Ada.text_io.put wymaga jednego argumentu b d cego
tekstem. Jako tekst traktowany jest ci g znaków umieszczony w cudzysłowach, np.
"Ala ma kota"
"procedure"
"1996"
s tekstami, natomiast
procedure
to ju nie tekst, lecz słowo kluczowe, za
1996
jest liczb .
W programie znajduje si równie instrukcja new_line (tak e pochodz ca z pakietu
ada.text_io), która powoduje przej cie kursora do nast pnego wiersza - raz, gdy jest u ywana bez
parametru, lub kilkakrotnie, je li ma posta
new_line(n);
(n jest liczb naturaln )
co w praktyce oznacza wypisanie n pustych linii.
Tak wi c wynikiem wykonywania kolejnych linii programu b d odpowiednio:
Dzien dobry!!!_
(kursor na ko cu tekstu)
Dzien dobry!!!
_
(kursor w nast pnej linii)
Dzien dobry!!!
81
Milej nauki!_
Zauwa my, e wykonane zostały instrukcje zawarte w tzw. cz ci wykonywalnej programu, a
wi c linie zawarte pomi dzy słowem begin a end pr1. Cz
wykonywalna programu musi
zawiera zawsze przynajmniej jedna instrukcj , nawet je eli miałaby to by "nic nie robi ca" instrukcja
null. Oprócz cz ci wykonywalnej program mo e zawiera tak e cz
deklaracyjn , zawart
pomi dzy lini procedure ... is a begin. Umieszczamy w niej np. deklaracje zmiennych
(b dzie o tym mowa w dalszej cz ci tego rozdziału). Wcze niej - przed lini procedure ... is
znajdowa si mo e specyfikacja kontekstu. Oprócz klauzuli with w jej skład wchodzi mo e tak e
klauzula use powoduj ca, e zasoby danego pakietu s widziane bezpo rednio (zobaczymy to w
nast pnym programie). Ponadto w dowolnym miejscu programu mo emy umieszcza komentarze, a
wi c teksty słu ce tylko jako informacja dla osoby czytaj cej program, nie maj ce jednak wpływu na
jego działanie. Komentarze poprzedzane s dwoma kolejno nast puj cymi my lnikami i obejmuj tekst
od tych e my lników do ko ca linii.
A oto program stanowi cy modyfikacj programu pr1. Dzi ki zastosowaniu klauzuli use nie
musimy ju w cz ci wykonywalnej u ywa nazw pakietów, z których pochodz procedury i funkcje.
------
modyfikacja programu pr1
dzieki umieszczeniu w programie linii
"use ada.text_io"
nie musimy uzywac nazw pakietow, z ktorych pochodza
procedury i funkcje
with ada.text_io;
use ada.text_io;
procedure pr2 is
begin
put("
Dzien dobry!!!
new_line;
put("
Milej nauki!
end pr2;
");
");
W niektórych przypadkach mo emy mie do czynienia nie z jednym, lecz z kilkoma pakietami,
u ywanymi w jednym programie, w których wyst puj procedury i funkcje o tych samych nazwach.
Przykładem tego mog by - u ywane w poni szym programie - put wypisuj ce tekst, zawarte w
pakiecie text_io oraz put wypisuj ce liczby całkowite, zawarte w pakiecie int_io.
------
Program demonstrujacy dzialania matematyczne:
dodawanie, odejmowanie, mnozenie, dzielenie
i potegowanie.
Dzialania wykonujemy na liczbach calkowitych
(uwaga na wynik dzielenia!)
with ada.text_io;
-- tym razem bez "use" zeby zobaczyc, co jest skad
procedure pr3 is
package int_io is new ada.text_io.integer_io(integer);
a,b : integer;
begin
ada.text_io.put_line("Podaj dwie liczby calkowite");
ada.text_io.put("liczba a : ");
int_io.get(a);
ada.text_io.put("liczba b : ");
81
int_io.get(b);
ada.text_io.new_line(4);
ada.text_io.put("a+b = "); int_io.put(a+b);
ada.text_io.new_line;
ada.text_io.put("a-b = ");int_io.put(a-b);
ada.text_io.new_line;
ada.text_io.put("a*b = ");int_io.put(a*b);
ada.text_io.new_line;
ada.text_io.put("a/b = ");int_io.put(a/b);
ada.text_io.new_line;
ada.text_io.put("a*a*a = ");int_io.put(a**3);
ada.text_io.new_line;
ada.text_io.put("a mod b = ");int_io.put(a mod b);
ada.text_io.new_line;
ada.text_io.put("a rem b = ");int_io.put(a rem b);
ada.text_io.new_line;
ada.text_io.put("abs b = ");int_io.put(abs b);
end pr3;
Pakiet int_io pojawił sie w programie w nie spotykany dotychczas sposób, bo dopiero w
cz ci deklaracyjnej. Dlaczego? Otó nie wszystkie pakiety s od razu w postaci "gotowej do u ycia".
Je eli s , mo emy napisa with nazwa_pakietu i korzysta z ich zasobów. Czasami jednak maj
one posta ogólniejsz , jak gdyby "szkieletu", jakich ogólnych, do uniwersalnych ram, które
przybieraja posta pakietu zawieraj cego mo liwe do u ycia funkcje i procedury dopiero po dokonaniu
tzw. konkretyzacji. Takie pakiety - "szkielety" nosz nazw pakietów rodzajowych. W naszym
przykładzie u ywamy pakietu rodzajowego o nazwie ada.text_io.integer_io. Zawiera on
procedury wej cia/wyj cia (czyli np. wprowadzania z klawiatury i wyprowadzania na ekran) dla liczb
dowolnego typu całkowitego. Konkretyzacji dokonujemy poleceniem
package int_io is new ada.text_io.integer_io (integer);
(w wolnym przekładzie pakiet int_io jest nowym (pakietem) ada.text_io.integer_io dla
integerów), które powoduje utworzenie pakietu (nazwali my go int_io) zawieraj cego operacje
wej cia/wyj cia takie same, jak text_io.integer_io, ale ju dla konkretnego typu - typu
integer. Istnieje
równie
gotowy pakiet
b d cy tak
wła nie konkretyzacj ada.integer_text_io. Podobny do pr3 program u ywaj cy takiego gotowego pakietu bedzie
miał posta
with ada.text_io,ada.integer_text_io;
procedure pr4 is
a,b:integer;
begin
ada.text_io.put_line("Podaj dwie liczby calkowite");
ada.text_io.put("liczba a : ");
ada.integer_text_io.get(a);
ada.text_io.put("liczba b : ");
ada.integer_text_io.get(b);
ada.text_io.new_line(4);
ada.text_io.put("a+b = ");
ada.integer_text_io.put(a+b);
-- ... i tak dalej
end pr4;
W programie pr3 nie zastosowali my klauzuli use, wi c ka d procedure poprzedzamy nazw
pakietu, z którego ona pochodzi. Unikamy w ten sposób niejednoznaczno ci - mimo tych samych nazw
procedur kompilator "wie", która z nich (z którego pakietu pochodz c ) chcemy zastosowa . Nie jest to
jednak konieczne w przypadku, gdy zastosujemy klauzul use, a procedury - mimo i maj te same
81
nazwy - operuj na argumentach ró nego typu b d na ró nej ilo ci agrumentów. Ada "rozpozna" na tej
podstawie odpowiedni procedur (odpowiedni pakiet). I tak na przykład ada.text_io.put
operuje na tek cie, za int_io.put - na liczbach typu integer. Wła ciwie istnieje kilka sposobów
u ycia procedury put, z których na razie poznamy dwa:
put ( liczba_całkowita );
oraz
put ( item => liczba_całkowita, width => szeroko
);
W pierwszym przypadku po prostu wypisujemy liczb (tu - typu integer) u ywaj c domy lnych
ustawie , w drugim natomiast programista ma mo liwo okre lenia, ile kolumn - przynajmniej - ma
zajmowa wypisywana liczba. Odpowiada za to parametr width (ang. szeroko ). Je eli wypisywana
liczba mie ci si w zaplanowanej ilo ci kolumn, to umieszczana jest z prawej strony tej przestrzeni, za
reszta wypełniana jest spacjami. W przeciwnym wypadku u ywana jest minimalna ilo kolumn
potrzebnych do wypisania danej liczby. Tak wi c aby zapewni sobie maksymalne wykorzystanie
miejsca, korzystnie jest u y parametru width o warto ci 0. Ilustruje to poni szy przykład.
Wykonanie instrukcji
ada.text_io.put("Ala ma ");
int_io.put(7);
ada.text_io.put(" lat.");
da wynik
Ala ma
7 lat.
natomiast instrukcje
ada.text_io.put("Ala ma ");
int_io.put(item => 7, width = > 0);
ada.text_io.put(" lat.");
spowoduj wypisanie tekstu
Ala ma 7 lat.
Item i width s nazwami parametrów procedury int_io.put. Przy wywołaniu procedury
mo na si nimi posługiwa lub nie - dozwolone jest zarówno napisanie
int_io.put ( item => 7, width => 0 );
(jest to tzw. notacja nazywana), jak i
int_io.put (7,0);
(jest to tzw. notacja pozycyjna). W drugim przypadku nale y jednak pami ta o zachowaniu
prawidłowej kolejno ci argumentów. Je eli argumenty "nazywamy", tak jak w pierwszym przypadku,
to moga one wyst powa w dowolnej kolejno ci. Mo na ł czy oba sposoby, np.
int_io.put ( 7, width => 0 );
ale wówczas notacja pozycyjna musi wyst powa przed nazywan .
Troch matematyki
Wró my do programu pr3. Od dłu szego czasu wspominamy o liczbach typu integer. Sk d
jednak wiemy, e działamy na liczbach jakiego typu i jakie to powoduje konsekwencje?
Za pomoc linii
81
a,b : integer;
umieszczonej w cz ci deklaracyjnej programu zadeklarowali my zmienne o nazwach a i a jako b d ce
typu integer. Deklaracja zmiennych powoduje dla ka dej zmiennej zarezerwowanie w pami ci
komputera obszaru o ci le okre lonym, wymaganym dla danego typu rozmiarze. Zmiennym tym
nadajemy nast pnie konkretne warto ci, które podajemy w trakcie wykonywania instrukcji
int_io.get(nazwa_zmiennej). Liczby te nie mog zawiera kropki dziesi tnej, dozwolony jest
natomiast znak podkre lenia w roli separatora (5_345_566 jest bardziej czytelne ni 5345566). Na
podanych liczbach wykonujemy działania: + (dodawanie), - (odejmowanie), * (mno enie), /
(dzielenie), ** (pot gowanie), abs (warto bezwzgl dna), rem i mod. Mo emy stosowa tak e
jednoargumentowe operatory + i - daj ce liczb o okre lonym znaku. Wynik działa na liczbach typu
integer jest równie tego samego typu. Zauwa my, e dzieje si tak nawet w przypadku dzielenia.
Jest to w tym wypadku dzielenie całkowite (np. 5/2 daje wynik 2). Operacje rem i mod daj w
wyniku liczby spełniaj ce odpowiednio równo ci:
A = (A/B)*B + ( A rem B )
( |A rem B| < |B|, A rem B ma ten sam znak, co A)
oraz
A = B * N + ( A mod B )
( |A mod B| < |B|,
dla pewnego N całkowitego
A mod B ma ten sam znak, co B).
Operacja rem (od remainder) to reszta z dzielenia, operacja mod (czyt. modulo) jest podobna. Oto
przykładowe wyniki działa :
i rem j
i mod j
i
j
12
5
2
2
12
-5
2
-3
-12
5
-2
3
-12
-5
-2
-2
Jak wida , je li A i B s tych samych znaków, to wyniki operacji rem i mod s takie same (reszta z
dzielenia |A| przez |B|, ze znakiem takim, jak A i B). Ró nica wyst puje dopiero dla argumentów
przeciwnych znaków. Wówczas wynik działania jest ró nic mi dzy liczb A a s siaduj c z ni
wielokrotno ci B, przy czym wielokrotno t wybieramy dla mod bardziej "na zewn trz", czyli dalej
od zera, a dla rem - bli ej zera.
W przypadku zapisywania bardziej zło onych wyra e kolejno działa (ich priorytet) jest
nast puj ca: najpierw abs i **, nast pnie *, /, rem i mod, a na ko cu + i -. Tworz c wyra enia
mo emy równie u ywa nawiasów "(" i ")" , czyli okr głych.
Je eli potrzebne s nam ułamki, musimy u y typu float (zmiennoprzecinkowego). Liczby
tego typu posiadaj kropk dziesi tn . (7.1, -12.34, 4.0). Mog by równie zapisywane w
postaci wykładniczej, np. -1.35330E+02 czy
4.23567E-04, co oznacza -135.33 i
0.000423567 (litera E - du a lub mała - i nast puj ca po niej liczba s odpowiednikiem pomno enia
przez 10 podniesione do pot gi liczba_napisana_po_E ). Na liczbach typu float wykonujemy takie
same działania, jak na liczbach całkowitych. Zabronione jest jednak u ywanie ich jako wykładnika
pot gi. Napisanie
3.2 ** 1.1
81
spowoduje zgłoszenie bł du (zob. jednak podrozdział Funkcje matematyczne).
W celu wykonywania operacji wej cia/wyj cia na liczbach typu float musimy, podobnie jak w
poprzednim przypadku, skonkretyzowa odpowiedni pakiet:
package float_io is new ada.text_io.float_io(float);
lub u y pakietu ada.float_text_io.
Ada nie pozwala na ł czenie w jednym wyra eniu ró nych typów. Napisanie np.
1.2 + 4.0 * 2 + 7
spowoduje zgłoszenie bł du, gdy mno ymy i sumujemy ze sob liczby typów float i integer.
Prawidłowy zapis tego wyra enia ma posta :
1.2 + 4.0 * 2.0 + 7.0
Problem ł czenia ró nych typów w jednym wyra eniu mo emy rozwi za równie tak, jak w
poni szym programie:
-- program demonstrujacy konwersje typow
-- i konkretyzacje pakietow potrzebnych do operacji
-- wejscia/wyjscia dla liczb typu float i integer
with ada.text_io;
use ada.text_io;
procedure pr5 is
package int_io is new ada.text_io.integer_io(integer);
package flt_io is new ada.text_io.float_io(float);
x,y:integer;
z,wynik:float;
begin
put("Podaj pierwsza liczbe calkowita :");
int_io.get(x);
put("Podaj druga liczbe calkowita
:");
int_io.get(y);
new_line;
put("Podaj liczbe rzeczywista
:");
flt_io.get(z);
new_line(4);
wynik:=float(x)*float(y)*z;
put("Oto iloczyn tych trzech liczb : ");
flt_io.put(wynik,fore=>4,aft=>5,exp=>0);
end pr5;
Powy szy przykład ilustruje sposób dokonywania konwersji typów - zamiany danych pewnego
typu na inny typ instrukcj postaci
typ_
np.
dany ( warto
integer (5.0)
float (5)
integer (4.1)
integer(4.6)
_zamieniana )
-----
daje
daje
daje
daje
5
5.0
4
5
81
Zwró my jeszcze uwag na posta instrukcji put. Dla liczb typu float mo e on przybra
posta
put (liczba, fore => ilo _cyfr_przed_kropk ,
aft => ilo _cyfr_po_kropce,
exp => ilo _cyfr_w_wykładniku );
Nadanie parametrowi exp warto ci 0 powoduje, i
wykładniczej, lecz w "zwykłej", jako ułamek dziesi tny.
liczby s
wyprowadzane nie w postaci
Jak sama nazwa wskazuje, zmienna mo e zmienia swoj warto
programu. Konkretn warto mo emy jej nada wykonuj c instrukcj
w trakcie wykonywania
get (nazwa_zmiennej)
lub stosuj c instrukcj podstawienia := , tak jak to widzimy w programach pr3 i pr4. Na przykład
w programie pr4 mamy lini
wynik:=float(x)*float(y)*z;
gdzie za pomoc instrukcji := zmiennej wynik nadajemy warto obliczon po prawej stronie.
Ponadto je li potrzebujemy nada naszej zmiennej warto pocz tkow , mo emy to uczyni ju w
cz ci deklaracyjnej programu, równocze nie z deklaracj zmiennej, pisz c
nazwa_zmiennej : typ := warto
;
czyli np.
x : float := 12.1;.
Oto program przykładowy:
-- przyklad, w ktorym nadajemy zmiennej
-- wartosc poczatkowa
-- obliczenie wagi zaladowanego samochodu
with ada.text_io;
use ada.text_io;
procedure pr6 is
package int_io is new ada.text_io.integer_io(integer);
use int_io;
masa_samochodu:integer:=520;
waga_osob,waga_bagazu:integer;
begin
put("Obliczamy mase zaladowanego 'malucha'...");
new_line(2);
put("Podaj mase pasazerow - zaokraglona
do pelnych kilogramow ");
get(waga_osob);
new_line;
put("Podaj mase wlozonego bagazu,
takze w pelnych kilogramach ");
get(waga_bagazu);
masa_samochodu:=masa_samochodu+waga_osob+waga_bagazu;
new_line(3);
put("Zaladowany 'maluch' wazy ");
put(masa_samochodu,0);
81
put(" kilogramow");
end pr6;
Oprócz zmiennych - zmieniaj cych swoj warto - mo emy deklarowa tak e stałe, które
nie mog zmienia swej warto ci podczas podczas wykonywania programu. Stałej nadajemy warto
ju w momencie jej deklarowania:
nazwa_stałej : constant typ := warto
np.
_stałej ;
y : constant integer:=12;
Poniewa , jak wiemy, stała nie zmienia swojej warto ci podczas działania programu, nie mo e
wyst powa z lewej strony instrukcji podstawienia. U ycie stałej ilustruje nast puj cy przykład:
-- program demonstrujacy uzycie stalych
with ada.text_io;
use ada.text_io;
procedure pr7 is
package flt_io is new ada.text_io.float_io(float);
use flt_io;
procent_podatku : constant float:=0.21;
procent_premii : constant float:=0.20;
premia,placa,podatek,do_wyplaty:float;
begin
put("Podaj place zasadnicza brutto : ");
get(placa);
new_line(3);
put_line("
WYPLATA");
new_line;
premia:=placa*procent_premii;
podatek:=procent_podatku*(placa+premia);
do_wyplaty:=placa+premia-podatek;
put(" placa zasadnicza : ");
put(placa,fore=>5,aft=>2,exp=>0);
new_line;
put(" premia
: ");
put(premia,fore=>5,aft=>2,exp=>0);
new_line;
put(" potracony podatek : ");
put(podatek,fore=>5,aft=>2,exp=>0);
new_line;
new_line;
put(" do wyplaty
: ");
put(do_wyplaty,fore=>5,aft=>2,exp=>0);
end pr7;
Mo na by postawi pytanie, po co deklarowa stałe, je eli mo na po prostu w odpowiednim miejscu
w programie pomno y przez 0.21 czy 0.20 - i ju . Oczywi cie - mo na, jednak stosowanie stałych
ułatwia na przykład wprowadzanie zmian w razie konieczno ci modyfikacji programu - przerabiamy
wtedy tylko cz
deklaracyjn , nie musz c przegl da całej tre ci.
Funkcje matematyczne
Wykonywanie działa na liczbach wi e si cz sto z konieczno ci u ycia ró nego rodzaju
funkcji matematycznych, jak na przykład pierwiastek, logarytm czy funkcje trygonometryczne. Dost p
81
do nich uzyskujemy poprzez konkretyzacj pakietu rodzajowego ada.numerics.generic_
elementary_functions dla typu liczbowego, dla którego b dziemy u ywa tych funkcji.
Konkretyzacja
dla
typu
float
jest
ju
"gotowa"
jest
to
pakiet
ada.numerics.elementary_functions. Zdefiniowane s w nim nast puj ce funkcje:
sqrt(x) (pierwiastek kwadratowy), log(x) (logarytm naturalny x), log(x,p) (logarytm x przy
podstawie p), exp(x) (e do pot gi x), funkcje trygonometryczne - sin, cos, tan, cot, a tak e
arcsin, arccos, arctan, arccot, sinh, cosh, tanh, coth, arcsinh, arccosh,
arctanh, arccoth - wszystkie operuj ce na argumentach typu float, oraz pot ga o podstawie i
wykładniku typu float. W jego pakiecie rodzicielskim - ada.numerics zadeklarowane s ponadto
dwie stałe - e i pi. Oto przykładowe programy:
-------
uzycie funkcji matematycznych
pierwszy program z tej serii
w trojkacie o podanych trzech bokach
obliczamy wysokosc
with ada.text_io,ada.numerics.generic_elementary_functions;
use ada.text_io;
procedure pr8 is
package flt_io is new ada.text_io.float_io(float);
use flt_io;
package fun_mat is new
ada.numerics.generic_elementary_functions(float);
a,b,c,h,p,pole:float;
begin
put_line("Podaj boki trojkata : ");
put("a : ");get(a);new_line;
put("b : ");get(b);new_line;
put("c : ");get(c);new_line(2);
put_line("
UWAGA !!! ");
put("Nie sprawdzam, czy taki trojkat moze w ogole istniec!");
new_line(3);
-- obliczamy pole trojkata za wzoru Herona
p:=(a+b+c)/2.0;
pole:=fun_mat.sqrt(p*(p-a)*(p-b)*(p-c));
-- wykorzystujemy obliczone pole do znalezienia wysokosci
put("wysokosc opuszczona na bok a : ");
h:=2.0*pole/a;
put(h,5,3,0);
new_line;
put("wysokosc opuszczona na bok b : ");
h:=2.0*pole/b;
put(h,5,3,0);
new_line;
put("wysokosc opuszczona na bok c : ");
h:=2.0*pole/c;
put(h,5,3,0);
end pr8;
Kolejny przykład:
-- uzycie funkcji matematycznych
--- drugi program z tej serii
81
--- podajemy przyprostokatne trojkata
-- obliczamy dlugosc przeciwprostokatnej
with ada.text_io,ada.numerics.generic_elementary_functions;
use ada.text_io;
procedure pr9 is
package flt_io is new ada.text_io.float_io(float);
use flt_io;
package fun_mat is new
ada.numerics.generic_elementary_functions(float);
a,b,c : float;
begin
put_line("Podaj dlugosci przyprostokatnych trojkata : ");
put("a : ");get(a);new_line;
put("b : ");get(b);new_line;
new_line(3);
c:=fun_mat.sqrt(a**2+b**2);
put("przeciwprostokatna c : ");
put(c,5,3,0);
end pr9;
I jeszcze jeden przyklad - tym razem funkcje trygonometryczne:
--------
Program sprawdzajacy, czy tzw. jedynka trygonometryczna
rzeczywiscie daje 1.
Pojawiaja sie tu funkcje matematyczne, a wiec
konkretyzacja pakietu rodzajowego
ada.numerics.generic_elementary_functions
oraz typ float.
Trzeci program z serii "uzycie funkcji matematycznych".
with text_io,ada.numerics.generic_elementary_functions;
use text_io;
procedure pr10 is
package fun_mat is new
ada.numerics.generic_elementary_functions(float);
use fun_mat;
package flt_io is new text_io.float_io(float);
use flt_io;
x,jedynka:float;
begin
put_line("Podaj dowolna liczbe rzeczywista x");
put_line("obliczymy sin(x)^2 + cos(x)^2 ");
put("x = ");
get(x);
jedynka:=sin(x)**2+cos(x)**2;
new_line(3);
put_line("obliczamy tzw. jedynke trygonometryczna.....");
put("otrzymalismy ");put(jedynka,exp=>0);
new_line;
set_col(10);
put("....no i co Ty na to?...");
81
end pr10;
W programie pr10 pojawiła si po raz pierwszy instrukcja
set_col(n);
powoduj ca ustawienie kursora w kolumnie o podanym numerze. Stanowi ona cz
pakietu
ada.text_io.
Czwarty przykład dotycz cy funkcji matematycznych ilustruje u ycie stałych zadeklarowanych
w pakiecie ada.numerics:
--------
program z uzyciem stalych ada.numerics.pi
i ada.numerics.e.
Uzywamy pakietow ada.numerics.elementary_functions
oraz ada.float_text_io zamiast konkretyzacji
pakietow rodzajowych w czesci deklaracyjnej programu.
Czwarty program z serii funkcje matematyczne
with ada.text_io,ada.numerics,ada.numerics.elementary_functions;
with ada.float_text_io;
use ada.text_io,ada.float_text_io;
use ada.numerics,ada.numerics.elementary_functions;
procedure pr11 is
begin
new_line(2);
put("liczba pi wynosi ");
put(ada.numerics.pi);
new_line;
put("liczba e wynosi ");
put(ada.numerics.e);
new_line;
put("logarytm naturalny liczby e : ");
put(log(e));
new_line;
put("logarytm naturalny liczby 10 : ");
put(log(10.0));
new_line;
put("logarytm przy podstawie 10 liczby 10 : " );
put(log(10.0,10.0));
new_line;
put("2 ^ 3.1 = ");
put(2.0**3.1);
end pr11;
Typy liczbowe i ich zakresy
Dotychczas zapoznali my si z typami integer i float. Nie s to jedyne predefiniowane
typy liczbowe. Dost pne s na przykład typy natural i positive (s to wła ciwie podtypy typu
a), a tak e (w wi kszo ci implementacji) typy całkowite i zmiennoprzecinkowe o szerszych b d
w szych zakresach short_short_integer, short_integer, long_integer,
long_long_integer, short_float, long_float i long_long_ float. Jakie s ich
zakresy? Do tej pory nie znamy zreszt równie zakresów typów integer i float.
dan
informacj uzyskamy wykonuj c poni szy program:
-- program przedstawia typy liczbowe (podstawowe)
-- oraz ich zakresy
-- pojawiaja sie atrybuty typow
with ada.text_io;
81
use ada.text_io;
procedure pr12 is
package
package
package
package
package
package
package
flo_io is new ada.text_io.float_io(long_float);
fl_io is new ada.text_io.float_io(float);
floa_io is new ada.text_io.float_io(long_long_float);
f_io is new ada.text_io.float_io(short_float);
int_io is new ada.text_io.integer_io(integer);
inte_io is new ada.text_io.integer_io(long_integer);
integ_io is new
ada.text_io.integer_io(long_long_integer);
package in_io is new ada.text_io.integer_io(short_integer);
package i_io is new
ada.text_io.integer_io(short_short_integer);
begin
put_line("Mamy nastepujace typy liczbowe : ");
set_col(5);
put_line("1. short_float - typ zmiennoprzecinkowy
o zakresie : ");
set_col(20);
f_io.put(short_float'first);
put(" .. ");f_io.put(short_float'last);
new_line;
set_col(5);
put_line("2. float - typ zmiennoprzecinkowy o zakresie : ");
set_col(20);
fl_io.put(float'first);put(" .. ");fl_io.put(float'last);
new_line;
set_col(5);
put_line("3. long_float - typ zmiennoprzecinkowy
o zakresie : ");
set_col(20);
flo_io.put(long_float'first);
put(" .. ");flo_io.put(long_float'last);
new_line;
set_col(5);
put_line("4. long_long_float - typ zmiennoprzecinkowy
o zakresie : ");
set_col(20);
floa_io.put(long_long_float'first);
put(" .. ");floa_io.put(long_long_float'last);
new_line;
set_col(5);
put_line("5. short_short_integer - typ calkowity
o zakresie : ");
set_col(20);
i_io.put(short_short_integer'first);
put(" .. ");i_io.put(short_short_integer'last);
new_line;
set_col(5);
put_line("6. short_integer - typ calkowity o zakresie : ");
set_col(20);
in_io.put(short_integer'first);
put(" .. ");in_io.put(short_integer'last);
new_line;
set_col(5);
81
put_line("7. integer - typ calkowity o zakresie : ");
set_col(20);
int_io.put(integer'first);
put(" .. ");int_io.put(integer'last);
new_line;
set_col(5);
put_line("8. long_integer - typ calkowity o zakresie : ");
set_col(20);
inte_io.put(long_integer'first);
put(" .. ");inte_io.put(long_integer'last);
new_line;
set_col(5);
put_line("9. long_long_integer - typ calkowity
o zakresie : ");
set_col(20);
integ_io.put(long_long_integer'first);
put(" .. ");integ_io.put(long_long_integer'last);
new_line;
put_line("oraz nastepujace podtypy typu integer :");
set_col(5);
put("1. positive - typ calkowity o zakresie : ");
int_io.put(positive'first);
put(" .. ");int_io.put(positive'last);
new_line;
set_col(5);
put("2. natural - typ calkowity o zakresie
int_io.put(natural'first);
put(" .. ");int_io.put(natural'last);
new_line;
: ");
end pr12;
W celu otrzymania najmniejszej i najwi kszej liczby danego typu u yli my tzw. atrybutów
typów - typ'first i typ'last wypisuj cych pierwszy (najmniejszy) i ostatni (najwi kszy)
element nale cy do danego typu. Jak wida , podtyp positive obejmuje liczby od 1 do
integer'last, a natural - od 0 do integer'last. Istniej jeszcze inne atrybuty typów, które
poznamy nieco pó niej. Zauwa my ponadto, e w celu wyprowadzania liczb dokonali my w cz ci
deklaracyjnej programu du ej ilo ci konkretyzacji. Dla typów całkowitych (dla ka dego oddzielnie)
konkretyzujemy pakiet ada.text_io.integer_io, a dla typów zmiennoprzecinkowych ada.text_io.float_io. Zauwa my te , e positive i natural nie wymagały odr bnego pakietu,
zadowalaj c si pakietem przeznaczonym dla ich typu bazowego - integer.
Instrukcja warunkowa
Podczas pisania programu musimy czasami uzale ni sposób jego działania od pewnych
okoliczno ci. Słu y do tego tzw. instrukcja warunkowa o składni:
[1]
if warunek then
instrukcje
end if;
[2]. if warunek then
instrukcje_1
else
instrukcje_2
81
end if;
[3]. if warunek_1 then
instrukcje_1
elsif warunek_2 then
instrukcje_2
elsif warunek_3 then
instrukcje_3
........
elsif warunek_n then
instrukcje_n
else
instrukcje_dalsze
end if;
Instrukcja if w przypadku [1] powoduje, i razie spełnienia warunku wykonywany jest ci g
polece wewn trz tej instrukcji, a wi c przed słowami end if. W przeciwnym przypadku instrukcje
te s pomijane. Polecenie [2] powoduje, e w sytuacji, gdy warunek jest spełniony, wykonywane s
instrukcje_1, a gdy nie jest - instrukcje_2. Przypadek [3] jest jak gdyby poł czeniem wielu
instrukcji typu [1] i instrukcji [2]. Stanowi uproszczon wersj ci gu instrukcji
if warunek_1 then
instrukcje_1
else
if warunek_2 then
instrukcje_2
else
if warunek_3 then
instrukcje_3
else
........
if warunek_n then
instrukcje_n
else
instrukcje_dalsze
end if;
........
end if;
end if;
end if;
Je eli zachodzi warunek_1 - wykonuj sie instrukcje_1, je li nie - sprawdzany jest
warunek_2. Je li zachodzi, wykonywane wykonywane s instrukcje_2, je li nie - sprawdzany
jest kolejny warunek itd. Je li nie zachodzi aden z warunków, wykonywane s instrukcje nast puj ce
po else.
Oto przykłady zastosowania poznanych instrukcji:
------
wykorzystanie instrukcji warunkowej
program przykladowy
podajemy dwie liczby
wyprowadzany jest ich iloczyn i iloraz, jezeli
mozna go obliczyc
with ada.text_io;
use ada.text_io;
procedure pr13 is
package flt_io is new ada.text_io.float_io(float);
use flt_io;
a,b:float;
81
begin
put("Podaj dowolna liczbe - a = ");
get(a);
put("Podaj nastepna liczbe - b = ");
get(b);
new_line(2);
put("a*b = ");put(a*b,exp=>0);
new_line;
if b/=0.0 then
put("a/b = ");put(a/b,exp=>0);
end if;
end pr13;
I kolejny przykład:
-- kolejny przyklad programu z wykorzystaniem
-- instrukcji warunkowej
-- rozwiazywanie rownania kwadratowego
with ada.text_io,ada.numerics.generic_elementary_functions;
use ada.text_io;
procedure pr14 is
package flt_io is new ada.text_io.float_io(float);
package fun_mat is new
ada.numerics.generic_elementary_functions(float);
use flt_io,fun_mat;
a,b,c,delt : float;
begin
put_line("Rownanie kwadratowe ma postac a*x^2 + bx + c = 0");
new_line;
put_line("Podaj wspolczynniki : ");
put("a = "); get(a);
put("b = "); get(b);
put("c = "); get(c);
new_line(2);
if a=0.0 then
put_line("To nie jest rownanie kwadratowe !!!");
if b/=0.0 then
put("Rozwiazanie podanego rownania
liniowego : x = ");
put(-c/b,exp=>0);
elsif b=0.0 and c/=0.0 then
put("Podane rownanie jest sprzeczne");
else
put("Podane rownanie jest tozsamosciowe");
end if;
else
delt:=b**2-4.0*a*c;
if delt<0.0 then
put("Podane rownanie nie ma pierwiastkow
rzeczywistych");
elsif delt=0.0 then
put("Rownanie ma jeden pierwiastek
rzeczywisty x = ");
put(-b/(2.0*a),exp=>0);
else
put_line("Rownanie ma dwa pierwiastki
rzeczywiste");
81
set_col(5);
put("x1 = ");
put((-b-sqrt(delt))/(2.0*a),exp=>0);
new_line;
set_col(5);
put("x2 = ");
put((-b+sqrt(delt))/(2.0*a),exp=>0);
end if;
end if;
end pr14;
Operatory logiczne. Kolejno
działa .
Konstruowanie warunków logicznych wymaga odpowiednich operatorów. S to na przykład <,
>, =, <= (mniejsze lub równe), >= (wi ksze lub równe), /= (ró ne) oraz operatory logiczne - not
(zaprzeczenie), A (koniunkcja - "i"), or (alternatywa - "lub"), xor (tzw. exclusive or - "albo"), and
then i or else. Niektóre z nich widzieli my juz w zamieszczonych powy ej programach. Niech W1
i W2 oznaczaj warunki. Wówczas
W1 and W2 daje prawd , gdy zachodz oba warunki
W1 or W2
daje prawd , gdy zachodzi przynajmniej jeden z warunków
W1 xor W2
daje prawd , gdy zachodzi dokładnie jeden z warunków
not W1
daje prawd , gdy warunek W1 nie zachodzi.
Operatory and then i or else podobne s w swoim działaniu do and i or, z t jednak
ró nic , e w poprzednich przypadkach obliczana była zawsze warto logiczna obu warunków.
Natomiast napisanie
W1 and then W2
powoduje obliczenie warto ci W2 tylko wtedy, gdy W1 zachodzi, za
W1 or else W2
powoduje obliczenie warto ci W2 tylko wtedy, gdy W1 nie zachodzi.
Operatory te s niejednokrotnie bardzo u yteczne. Jako przykład posłu y mo e poni sza
sytuacja:
Napisanie
a/=0 and b/a>c
spowoduje zgłoszenie bł du podczas wykonywania programu, je eli podamy a=0, poniewa
sprawdzona b dzie warto logiczna warunku b/a>c, a przez 0 dzieli nie wolno. Natomiast
a/=0 and then
b/a>c
spowoduje obliczenie warto ci logicznej warunku b/a>c tylko wtedy, gdy a/=0, a wi c bł d podczas
dzielenia nie wyst pi.
Działania - logiczne i matematyczne - wykonywane s w nast puj cej kolejno ci:
**
*
+
+
=
and
abs
/
/=
or
not
rem mod
<
<=
xor
(jednoargumentowe)
(dwuargumentowe)
>
>=
81
(operatory wymienione na pocz tku, a wi c **, abs i not maja najwy szy priorytet).
Z konstruowaniem warunków logicznych zwi zany jest tak e test członkostwa - sprawdzenie,
czy dana warto nale y do podanego zakresu. Mo emy oczywi cie dokona tego za pomoc
poznanych dotychczas operatorów, ale mo emy te u y słowa kluczowego in. Zamiast sprawdza np.
warunek
liczba >= 2 and liczba <=8
mo emy napisa po prostu
liczba in 2..8
a zamiast
liczba < 2 or liczba > 8
liczba not in 2..8
Typ boolean
Z zagadnieniem prawdy i fałszu zwi zany jest tak e typ logiczny - boolean, przyjmuj cy dwie
warto ci - false i true. W celu wprowadzania i wyprowadzania warto ci tego typu musimy znów
skonkretyzowac odpowiedni pakiet - tym razem ada.text_io.enumeration_io:
package boolean_io is new ada.text_io.enumeration_io(boolean);
Oto przykład zastowania:
-- tabelka logiczna
-- przyklad uzycia zmiennych logicznych
-- a przy okazji - opis dzialania operatorow
with ada.text_io;
use ada.text_io;
procedure pr15 is
package b_io is new ada.text_io.enumeration_io(boolean);
p,q:boolean;
begin
put_line("
new_line(3);
put(" |
p
TABELKA WARTOSCI LOGICZNYCH");
|
q
|
not p
| p and q | p or q |
p xor q | ");
new_line;
p:=true;
q:=true;
put(" | ");b_io.put(p,width=>5);
put(" | ");b_io.put(q,width=>5);
put(" | ");b_io.put(not p,width=>7);
put(" | ");b_io.put(p and q,width=>7);
put(" | ");b_io.put(p or q,width=>6);
put(" | ");b_io.put(p xor q,width=>7);
put(" |");
new_line;
q:=false;
put(" | ");b_io.put(p,width=>5);
81
put(" | ");b_io.put(q,width=>5);
put(" | ");b_io.put(not p,width=>7);
put(" | ");b_io.put(p and q,width=>7);
put(" | ");b_io.put(p or q,width=>6);
put(" | ");b_io.put(p xor q,width=>7);
put(" |");
new_line;
p:=false;
q:=true;
put(" | ");b_io.put(p,width=>5);
put(" | ");b_io.put(q,width=>5);
put(" | ");b_io.put(not p,width=>7);
put(" | ");b_io.put(p and q,width=>7);
put(" | ");b_io.put(p or q,width=>6);
put(" | ");b_io.put(p xor q,width=>7);
put(" |");
new_line;
q:=false;
put(" | ");b_io.put(p,width=>5);
put(" | ");b_io.put(q,width=>5);
put(" | ");b_io.put(not p,width=>7);
put(" | ");b_io.put(p and q,width=>7);
put(" | ");b_io.put(p or q,width=>6);
put(" | ");b_io.put(p xor q,width=>7);
put(" |");
new_line;
end pr15;
Wynikiem wykonania powy szego programu b dzie nast puj ca tabelka:
TABELKA WARTOSCI LOGICZNYCH
p
TRUE
TRUE
FALSE
FALSE
q
TRUE
FALSE
TRUE
FALSE
not p
FALSE
FALSE
TRUE
TRUE
p and q
TRUE
FALSE
FALSE
FALSE
p or q
TRUE
TRUE|
TRUE
FALSE
p xor q
FALSE
TRUE
TRUE|
FALSE
Typy znakowe
Kolejnymi typami - ju nie liczbowymi, lecz znakowymi - s character zawieraj cy znaki ze
zbioru znaków o nazwie ISO 8859-1, czyli latin1, oraz wide_character ze znakami ze zbioru
ISO 10646 BMP. Typ character zawiera 256 znaków - drukowalnych i niedrukowalnych , za
wide_character - 65536 znaków. 256 pocz tkowych znaków wide_character jest takie samo,
jak w typie character. Znaki drukowalne obu typów ujmujemy w apostrofy (np. 'a', '$',
'+'). Mo liwo u ywania znaków niedrukowalnych (kontrolnych) nale cych do typu character,
takich jak znak ko ca
linii czy d wi k,
zapewnia nam pakiet
o nazwie
ada.characters.latin_1. Poszczególne znaki nale ce do tego typu otrzymały tam swoje
nazwy (zostały zadeklarowane jako stałe), i przez te nazwy mo emy si do nich odwoływa . Oto
fragmenty tego pakietu:
------------------------- Control Characters ------------------------NUL
SOH
: constant Character := Character'Val (0);
: constant Character := Character'Val (1);
81
STX
ETX
EOT
ENQ
ACK
BEL
BS
HT
LF
VT
FF
CR
SO
SI
: constant Character := Character'Val (2);
: constant Character := Character'Val (3);
: constant Character := Character'Val (4);
: constant Character := Character'Val (5);
: constant Character := Character'Val (6);
: constant Character := Character'Val (7);
: constant Character := Character'Val (8);
: constant Character := Character'Val (9);
: constant Character := Character'Val (10);
: constant Character := Character'Val (11);
: constant Character := Character'Val (12);
: constant Character := Character'Val (13);
: constant Character := Character'Val (14);
: constant Character := Character'Val (15);
(....)
Space
: constant Character := ' '; -- Character'Val(32)
Exclamation
: constant Character := '!'; -- Character'Val(33)
Quotation
: constant Character := '"'; -- Character'Val(34)
Number_Sign
: constant Character := '#'; -- Character'Val(35)
Dollar_Sign
: constant Character := '$'; -- Character'Val(36)
Percent_Sign
: constant Character := '%'; -- Character'Val(37)
Ampersand
: constant Character := '&'; -- Character'Val(38)
Apostrophe
: constant Character := '''; -- Character'Val(39)
Left_Parenthesis : constant Character := '('; -- Character'Val(40)
Right_Parenthesis : constant Character := ')'; -- Character'Val(41)
Asterisk
: constant Character := '*'; -- Character'Val(42)
Plus_Sign
: constant Character := '+'; -- Character'Val(43)
i tak dalej. Znaku o nazwie BEL powoduj cego d wi k mo emy wi c u ywa pisz c w programie
ada.characters.latin_1.bel, tak jak to wida ni ej:
-- program demonstrujacy uzycie znakow ASCII
with ada.text_io,ada.characters.latin_1;
use ada.text_io;
procedure pr16 is
znak:character:='d';
begin
new_line(7);
set_col(25);
-- tak wyprowadzamy znaki kontrolne ...
put("!!! Pora na kolacje !!!");
put(ada.characters.latin_1.bel);
new_line(2);
-- ... a tak znaki drukowalne
put(ada.characters.latin_1.asterisk);
new_line;
put('a');
new_line;
put(znak);
new_line;
put('a');
81
end pr16;
Oczywi cie do ka dego ze znaków mo na byłoby si odwoła tak, jak to widzieli my w pakiecie
ada.characters.latin_1, pisz c character'val(n), ale jest to sposób nieco mniej
naturalny. Co oznacza owo tajemnicze val, dowiemy si ju wkrótce. Na razie zauwa my jeszcze, e za
wyprowadzanie znaków typu character odpowiada pakiet ada.text_io. Na przykład do
wyprowadzenia znaku u ywana jest procedura put. Nie jest to jednak to samo put, którego
uzywali my dotychczas - pakiet text_io zawiera odr bne procedury dla pojedynczych znaków i dla
ich ci gów. Nie mo emy na przykład u ywa dla znaków procedury put_line - istnieje tylko dla
ci gu znaków, natomiast dla pojedynczego znaku nie jest zdefiniowana. Za wyprowadzanie znaków
typu wide_character odpowiedzialny jest z kolei pakiet ada.wide_text_io. Znaki typu
wide_character nie mog pojawia si w identyfikatorach, czyli w nazwach procedur, zmiennych i
stałych, natomiast mog pojawia si w wyprowadzanych tekstach i komentarzach.
Typ wyliczeniowy
Character, wide_character i boolean s przykładami predefiniowanych typów
wyliczeniowych. Typ wyliczeniowy to - jak sama nazwa wskazuje - taki typ, którego elementy mo na
wyliczy . Jego elementy ustawione s w okre lonej kolejno ci, mo na wi c znale element pierwszy
(najmniejszy), ostatni (najwi kszy), a dla danego elementu - poprzedzaj cy go i nast pny po nim, o ile
takie istniej . Zdefiniowana jest tak e relacja mniejszo ci - element mniejszy to taki, który stoi
wcze niej w deklaracji typu. Tak na przykład typ boolean okre lony jest nast puj co:
type boolean is (false, true);
wi c mamy false<true. (Dokładniej mówi c, elementy typu wyliczeniowego mo emy porównywa
na wszelkie mo liwe sposoby, a wi c u ywa operatorów >, <, <=, >=, =, /=).
Typy wyliczeniowe mo emy definiowa sami, pisz c w cz ci deklaracyjnej programu
type nazwa_typu is (element_1, ..., element_n);
Elementy typu wyliczeniowego nazywamy literałami wyliczeniowymi. Mog by nimi identyfikatory
lub znaki (uj te w apostrofy) np.
-- identyfikatory
type dni is (pon, wt, sr, czw, pt, so, nie);
-- znaki
type liczby_rzymskie is ( 'I', 'V', 'X', 'L', 'C', 'D', 'M');
-- znaki i identyfikatory
type xxx is (r2, 'a', xxxd12);
W celu wprowadzania i wyprowadzania warto ci typu wyliczeniowego musimy skonkretyzowa
pakiet ada.text_io.enumeration_io dla danego typu, np.
package dni_io is new ada.text_io.enumeration_io(dni);
package rzymskie_io is new
ada.text_io.enumeration_io(liczby_rzymskie);
Zauwa my, e konkretyzacji trzeba dokona nawet wtedy, gdy elementami typu wyliczeniowego s
same znaki (wyprowadzane i wprowadzane s wtedy znaki uj te w apostrofy).
Oto przykład deklaracji i u ycia takiego typu:
-- przyklad uzycia typow wyliczeniowych
-- wprowadzanie i wyprowadzanie elementow tego typu
with ada.text_io;
use ada.text_io;
81
procedure pr17 is
type pierwiastki is (H, He, Li, Be, B, C, N, O, F, Ne,
Na, Mg, Al);
-- pierwiastki sa uporzadkowane rosnaco wg mas atomowych
package chemia_io is new
ada.text_io.enumeration_io(pierwiastki);
p1,p2:pierwiastki;
begin
put_line("Nasza lista obejmuje nastepujace pierwiastki
chemiczne :");
put_line("Al, B, Be, C, F, H, He, Li, N, Na, Ne, Mg, O");
new_line;
put("podaj nazwy dwoch pierwiastkow, a dowiesz sie, ktory ma
wieksza mase atomowa");
new_line;
put(" 1 : ");chemia_io.get(p1);
put(" 2 : ");chemia_io.get(p2);
if p1>p2 then
chemia_io.put(p1);
put(" ma wieksza mase atomowa niz ");chemia_io.put(p2);
elsif p1<p2 then
chemia_io.put(p1);
put(" ma mniejsza mase atomowa niz");chemia_io.put(p2);
else
put("podane pierwiastki maja rowne masy atomowe");
end if;
end pr17;
Wykonuj c program zobaczymy, e symbole pierwiastków wyprowadzane s troch
nieprawidłowo, bo wielkimi literami. W wypadku literałów wyliczeniowych Ada, podobnie jak w
wypadku identyfikatorów, nie rozró nia rozmiarów liter. Za sposób wyprowadzania natomiast
odpowiada parametr procedury chemia_io.put (tzn. procedury put dla konkretnego typu
wyliczeniowego) o nazwie Set. Mo e on przyjmowa dwie warto ci - Upper_Case (ustawion jako
domy ln - wtedy elementy typu wyprowadzane s wielkimi literami) i Lower_Case. Procedura put
ma jeszcze jeden parametr - width, odpowiadaj cy za szeroko wyprowadzanego tekstu, lecz
poniewa ma on domy lnie warto 0, wi c wyprowadzany element typu zajmuje tylko tyle miejsca, ile
musi.
Podtypy
Je eli mówimy o definiowaniu własnych typów, wypada wspomnie tak e o definiowaniu
podtypów. Podtyp nie jest nowym typem, stanowi jedynie podzbiór warto ci pewnego ju znanego typu
(nazywanego tutaj typem bazowym). Definiujemy go pisz c w cz ci deklaracyjnej programu
subtype nazwa_podtypu is typ_bazowy range
ograniczenie_dolne .. ograniczenie_górne;
gdzie ograniczenie_dolne i ograniczenie_górne nale
do typu bazowego, np.
subtype dni_robocze is dni range pon .. pt;
subtype wiek_ludzki is integer range 0..120;
Granice zakresu mog by tak e obliczane:
granica_gorna:=12*5+3;
81
subtype
ograniczony_integer is
integer range 1 .. granica_gorna;
n:integer:=4;
subtype wiekszy_integer is integer range 1 .. 12*n+3;
Podanie jako element_k lub element_n elementów spoza zakresu typu bazowego spowoduje
podczas wykonywania programu zgłoszenie bł du constraint_error - przekroczenia zakresu.
Mo na oczywi cie definiowa podtypy tak, aby obejmowały cały zakres typu bazowego, np.
subtype calkowite is integer;
co powoduje, e calkowite jest wła ciwie inn nazw typu integer; a tak e podtypy b d ce
podtypami pewnego podtypu, np.
subtype dni_robocze is dni range pon .. pt;
subtype srodek_tygodnia is dni_robocze range wt .. czw;
Podtyp nie jest nowym typem, a wi c dziedziczy wła ciwo ci swojego typu bazowego. I tak na
przykład je eli odpowiedni
konkretyzacj
pakietu ada.text_io.enumeration_io
zapewnili my wykonywanie operacji wej cia/wyj cia dla elementów typu dni, to nie musimy ju
dokonywac kolejnej konkretyzacji dla typów dni_robocze ani srodek_tygodnia - odpowiada
za to b dzie ten sam pakiet, co w przypadku typu dni. Jak wiadomo, nie wolno w jednym wyra eniu
ł czy ze sob elementów nale cych do ró nych typów. Jednak ł czenie w jednym wyra eniu
elementów nale cych do ró nych podtypów tego samego typu bazowego jest dozwolone.
Nazw podtypów mo emy u ywa zamiast zakresów w te cie członkostwa. Zamiast pisa np. w
instrukcji warunkowej
wiek in 0..120
mo emy napisa
wiek in wiek_ludzki.
Oprócz poznanej definicji podtypu mo emy definiowa podtypy w sposób niejawny. Nie maj
one wówczas nazwy, stanowia jedynie zaw enie zakresu pewnego typu. Dokonujemy tego
równocze nie z deklaracj zmiennej, np.
wiek_dziecka: integer range 0..18;
Zmienna o nazwie wiek_dziecka mo e przyjmowa
przedziału - od 0 do 18. Napisanie np.
warto ci typu integer, ale z okre lonego
wiek_dziecka:=19;
spowoduje zgłoszenie bł du constraint_error.
W Adzie istniej predefiniowane podtypy typu integer - positive i natural. Okre lone
s one nast puj co:
subtype positive is integer range 1..integer'last;
subtype positive is integer tange 0..integrer'last;
Wspominali my ju o nich przy okazji omawiania zakresów typów liczbowych.
A oto przykładowy program:
-- program przedstawiajacy definiowanie podtypow
81
with ada.text_io;
use ada.text_io;
procedure pr18 is
type szczyty_tatrzanskie is ( Bobrowiec, Giewont, Kasprowy,
Wolowiec, Bystra, Swinica, Krywan,
Rysy, Lomnica, Gerlach);
subtype Tatry_Zachodnie is szczyty_tatrzanskie
range Bobrowiec .. Bystra;
subtype Tatry_Wysokie is szczyty_tatrzanskie
range Swinica .. Gerlach ;
package szczyty_io is new
ada.text_io.enumeration_io (szczyty_tatrzanskie);
use szczyty_io;
package int_io is new ada.text_io.integer_io(integer);
use int_io;
najwyzszy_Tatry: constant szczyty_tatrzanskie:=Gerlach;
najwyzszy_Polska: constant szczyty_tatrzanskie:=Rysy;
najwyzszy_Tatry_Wysokie:constant Tatry_Wysokie
:=najwyzszy_Tatry;
najwyzszy_Tatry_Zachodnie: constant Tatry_Zachodnie :=Bystra;
gora1,gora2:szczyty_tatrzanskie;
punkty:integer range 0..10;
begin
punkty:=0;
set_col(20);
put_line("*** SZCZYTY TATRZANSKIE ***");
new_line;
set_col(5);
put_line("Bobrowiec, Bystra, Gerlach, Giewont,
Kasprowy, Krywan, ");
set_col(15);
put_line("Lomnica, Rysy, Swinica, Wolowiec");
new_line;
put_line("Test znajomosci Tatr - mozesz podawac tylko
powyzsze szczyty");
put("Podaj nazwe najwyzszego szczytu Tatr : ");
get(gora1);
if gora1=najwyzszy_Tatry then punkty:=punkty+1;end if;
put("Podaj nazwe najwyzszego szczytu w Polsce : ");
get(gora1);
if gora1=najwyzszy_Polska then punkty:=punkty+1;end if;
put_line("Podaj nazwy dwoch szczytow w Tatrach Zachodnich
- najpierw nizszy ");
put("
1 : ");get(gora1);
put("
2 : ");get(gora2);
if gora1 in Tatry_Zachodnie then punkty:=punkty+1;end if;
if gora2 in Tatry_Zachodnie'first..Tatry_Zachodnie'last then
punkty:=punkty+1;
end if;
if gora1<gora2 then punkty:=punkty+1;end if;
put_line("Podaj nazwy dwoch szczytow w Tatrach Wysokich
- najpierw nizszy ");
81
put("
1 : ");get(gora1);
put("
2 : ");get(gora2);
if gora1 in Tatry_Wysokie then punkty:=punkty+1;end if;
if gora2 in Tatry_Wysokie then punkty:=punkty+1;end if;
if gora1<gora2 then punkty:=punkty+1;end if;
put("Podaj nazwe najwyzszego szczytu Tatr Zachodnich : ");
get(gora1);
if gora1=najwyzszy_Tatry_Zachodnie then punkty:=punkty+1;
end if;
put("Podaj nazwe najwyzszego szczytu Tatr Wysokich : ");
get(gora1);
if gora1=najwyzszy_Tatry_Wysokie then punkty:=punkty+1;end if;
new_line;
put(ascii.bel);
if punkty>7 then
put_line("Gratulacje - Tatry znasz bardzo dobrze...
albo dobrze czytasz mapy");
put("Zdobyles ");put(punkty,width=>0);put(" pkt");
else
put("Nie znasz geografii Tatr - przykro mi ...");
put("Zdobyles tylko ");put(punkty,width=>0);put(" pkt");
end if;
end pr18;
Definiowanie nowych typów liczbowych
Ada oferuje mo liwo
definiowania własnych, nowych typów liczbowych, zarówno
całkowitych, jak i zmiennoprzecinkowych (rzeczywistych). Typ całkowity definiujemy poleceniem
postaci
type typ_całkowity is range
ograniczenie_dolne .. ograniczenie_górne;
za typ zmiennoprzecinkowy poleceniem
type typ_zmiennoprzecinkowy is digits ilo
_cyfr_znacz cych;
lub
type typ_zmiennoprzecinkowy is digits ilo _cyfr_znacz cych
range ograniczenie_dolne .. ograniczenie_górne;
gdzie ograniczenie_dolne i ograniczenie_górne s odpowiednio liczbami całkowitymi
lub zmiennoprzecinkowymi. Ilo _cyfr_znacz cych stanowi ograniczenie dokładno ci. Na
przykład w typie zdefiniowanym
type do_trzech is digits 3;
liczby 2.123 i 2.124 s nierozró nialne - widziane jako 2.12 (2.123 i 2.128 b d jednak rozró niane ze
wzgl du na zaokr glenie). Zauwa my, e wszystkie z poni szych literałów rzeczywistych maj po trzy
cyfry znacz ce:
3.11 2.22E8
0.0000456
12.6E-8
Oto przykłady definicji typów liczbowych:
type male_calkowite is range -10 .. 10;
81
7650000.0
type sekundy_po_polnocy is range 0..86400;
type do_trzech is digits 3;
type male_do_trzech is digits 3 range 0.0 .. 2.0;
Polecenie definiuj ce nowy typ jest nieco podobne do deklaracji podtypu (zauwa my, e nie
okre lamy tu typu bazowego), powoduje jednak zupełnie inne skutki. W wyniku jego wykonania
tworzony jest nowy typ (male_calkowite, sekundy_po_polnocy, do_trzech). Nie mo emy
wi c go ł czy w wyra eniach z elementami innego typu, nie istnieje te pakiet odpowiadaj cy za
wykonywanie operacji wej cia/wyj cia na jego elementach. Po co wi c wła ciwie takie typy, skoro
mamy z nimi same kłopoty? Po pierwsze - wykorzystywane s one na przykład w sytuacjach, kiedy
chcemy zapobiec omyłkowemu ł czeniu warto ci jednego typu, lecz o ró nym znaczeniu. W poni szym
przykładzie wykonujemy działania na danych całkowitych, oznaczaj cych wiek i wzrost. Gdyby my
zadeklarowali wiek i wzrost np. jako podtypy typu integer, kompilator pozwoliłby nam na
zsumowanie czy porównanie wieku i wzrostu, co oczywi cie doprowadziłoby do bł dnych wyników,
których przyczyny musieliby my dopiero szuka . Poniewa jednak s one nowymi typami, kompilator
sam znajdzie wszystkie miejsca, w których pomylili my wiek ze wzrostem. Po drugie za uniezale niamy si w ten sposób od implementacji Ady. W ró nych implementacjach typy standardowe
mog mie ró ny rozmiar - w jednych np. integer mo e by 32-bitowy, w innych krótszy; w jednych
mo e by zdefiniowany typ long_integer, w innych nie... Tak wi c próbuj c zdefiniowa pewien
podtyp - na przykład, tak jak wy ej, sekundy_po_polnocy, mo emy nie by w stanie okre li
jednoznacznie odpowiedniego dla ka dej implementacji typu bazowego. Wprowadzaj c zamiast
podtypu nowy typ pozwalamy kompilatorowi zdecydowa , w jaki sposób elementy tego typu b d
reprezentowane.
Zauwa my, e w celu wypisywania warto ci nale cych do typów zdefiniowanych w ponizszym
programie skonkretyzowali my pakiet ada.text_io.integer_io - s to przecie tak e typy
całkowite. Podobnie nale ałoby skonkretyzowa pakiet ada.text_io.float_io w przypadku
zdefiniowania nowego typu zmiennoprzecinkowego.
-- program ktorego nie mozna skompilowac
--- zdefiniowanie nowych typow calkowitych
-- nie pozwala na popelnienie kilku bledow
with ada.text_io;
use ada.text_io;
procedure pr19 is
-- definiujemy nowe typy calkowite
type wiek is range 0..120;
type wzrost is range 0..250;
-- konkretyzujemy dla nich odpowiedni pakiet
package wi_io is new ada.text_io.integer_io(wiek);
package wz_io is new ada.text_io.integer_io(wzrost);
osoba1_wi:wiek:=15;
osoba2_wi:wiek:=16;
osoba1_wz:wzrost:=158;
osoba2_wz:wzrost:=164;
sr_wz:wzrost;
sr_wi:wiek;
begin
put("sredni wzrost : ");
sr_wz:=(osoba1_wi+osoba2_wz)/2;
wz_io.put(sr_wz);
new_line;
put("sredni wiek
: ");
sr_wi:=(osoba1_wz+osoba2_wi)/2;
wi_io.put(sr_wi);
81
-- wystapi blad
-- pomylilismy _wz i _wi
-- wystapi blad - jw.
new_line;
if osoba1_wz>osoba2_wi then
-- wystapi blad - jw.
put_line("osoba1 jest wyzsza niz osoba2");
else
put_line("osoba1 nie jest wyzsza niz osoba2");
end if;
end pr19;
Atrybuty typów
Wspomnieli my wcze niej o atrybutach typów. Dotychczas poznali my dwa spo ród nich t'first i t'last, gdzie t oznacza typ, zwracaj ce najmniejsz i najwi ksz warto w danym
typie (b d podtypie). Oprócz tego istniej nast puj ce atrybuty: t'pos, t'val, t'pred, t'succ,
t'image, t'value, t'base, t'max, t'min. Wszystkie one, oprócz t'first, t'last i
t'base wymagaj parametrów. Wszystkie, oprócz pos i val, okre lone s dla typów rzeczywistych i
dyskretnych (tzn. całkowitych lub wyliczeniowych). Pos i val okre lone s tylko dla typów
dyskretnych. Oto przykład ilustruj cy u ycie atrybutów:
--program demonstrujacy dzialanie atrybutow typow
with ada.text_io;
use ada.text_io;
procedure pr20 is
type dni is (pon, wt, sr, czw, pt, so, nie);
subtype dni_robocze is dni range pon..pt;
subtype srodek_tygodnia is dni_robocze range wt..czw;
subtype do_stu is integer range 0..100;
package dni_io is new ada.text_io.enumeration_io(dni);
use dni_io;
package int_io is new ada.text_io.integer_io(integer);
use int_io;
package flt_io is new ada.text_io.float_io(float);
use flt_io;
x:constant do_stu:=do_stu'first;
begin
-- atrybuty FIRST i LAST
put("dni'first : ");
put(dni'first);
-- wypisze PON
set_col(40);
put("dni'last : ");
put(dni'last);
-- wypisze NIE
new_line;
put("dni_robocze'first : ");
put(dni_robocze'first);
-- wypisze PON
set_col(40);
put("dni_robocze'last : ");
put(dni_robocze'last);
-- wypisze PT
new_line;
put("integer'first : ");
put(integer'first,0);
-- wypisze najmniejszy integer
-- atrybuty PRED i SUCC
new_line(2);
put("dni'pred(wt) : ");
81
put(dni'pred(wt));
-- wypisze
set_col(40);
put("dni'succ(wt) : ");
put(dni'succ(wt));
-- wypisze
new_line;
put("dni robocze'pred(pt) : ");
put(dni_robocze'pred(pt));
-- wypisze
set_col(40);
put("dni robocze'pred(nie) : ");
put(dni_robocze'pred(nie));
-- wypisze
new_line;
put("do_stu'pred(10) : ");
put(do_stu'pred(10),0);
-- wypisze
set_col(40);
put("integer'pred(x) (x: do_stu := 0) : ");
put(integer'pred(x),0);
-- wypisze
new_line;
put("dni'robocze'succ(pt) : ");
put(dni_robocze'succ(pt));
-- wypisze
set_col(40);
put("do_stu'pred(x) (x: do_stu := 0) : ");
put(do_stu'pred(x));
-- wypisze
new_line;
put("float'pred(0.0) : ");
put(float'pred(0.0));
set_col(40);
put("float'succ(0.0) : ");
put(float'succ(0.0));
new_line(2);
PON
SR
CZW
SO
9
-1
SO
-1
-- atrybuty POS i VAL
put("dni'pos(pon) : ");
put(dni'pos(pon),0);
set_col(40);
put("dni'val(0) : ");
put(dni'val(0),0);
new_line;
put("srodek_tygodnia'pos(wt) : ");
put(srodek_tygodnia'pos(wt),0);
set_col(40);
put("srodek_tygodnia'val(1) : ");
put(srodek_tygodnia'val(1));
new_line;
put("srodek_tygodnia'val(0) : ");
put(srodek_tygodnia'val(0));
set_col(40);
put("integer'pos(-7) : ");
put(integer'pos(-7),0);
new_line(2);
-- wypisze 0
-- wypisze PON
-- wypisze 1
-- wypisze WT
-- wypisze PON
-- wypisze -7
-- atrybuty IMAGE i VALUE
put("integer'image(12) : ");
put(integer'image(12));
set_col(40);
put("do_stu'image(10) : ");
put(do_stu'image(10));
new_line;
put("dni'image(wt) : ");
put(dni'image(wt));
set_col(40);
put("dni_robocze'image(so) : ");
put(dni_robocze'image(so));
new_line;
put("integer'value(""12"") : ");
81
-- wypisze 12 (tekst)
-- wypisze 10 (tekst)
-- wypisze WT (tekst)
-- wypisze SO (tekst)
put(integer'value("12"),0);
-- wypisze 12 (liczb )
set_col(40);
put("integer'image(x) (x : do_stu :=0) : ");
put(integer'image(x));
-- wypisze 0 (tekst)
new_line;
put("float'value(""12"") : ");
put(float'value("12"));
-- wypisze 1.20000E+01
set_col(40);
put("float'image(12.2) : ");
put(float'image(12.2));
-- wypisze 1.22000E+001
new_line(2);
-- atrybut BASE
put("integer'base'first : ");
put(integer'base'first);
-- wypisze to, co integer'first
set_col(40);
put("do_stu'base'first : ");
put(do_stu'base'first);
--wypisze to, co integer'first
new_line;
put("dni_robocze'base'last : ");
put(dni_robocze'base'last);
-- wypisze NIE
set_col(40);
put("srodek_tygodnia'base'last : ");
put(srodek_tygodnia'base'last);
-- wypisze NIE
new_line(2);
--atrybuty MIN i MAX
put("dni'min(so,pon) : ");
put(dni'min(so,pon));
set_col(40);
put("dni_robocze'min(so,pon) : ");
put(dni_robocze'min(so,pon));
new_line;
put("dni_robocze'min(so,nie) : ");
put(dni_robocze'min(so,nie));
-- wypisze PON
-- wypisze PON
-- wypisze SO
end pr20;
Atrybuty t'pred(element) i t'succ(element) zwracaj elementy, które w danym
typie poprzedzaj element i nast puj po nim (ang. predecessor i successor). Tak wi c
dni'pred(wt) da czw, integer'pred(-7) da -8 itd. Nie mo na jednak próbowa okre li
elementu poprzedzaj cego pierwszy element ani elementu nast puj cego po ostatnim elemencie typu.
Nie istnieje mechanizm "chodzenia w kółko", czyli podawania np. jako elementu pierwszego jako
nast pnego po ostatnim. Próba wykonania powy szych operacji spowoduje wyst pienie bł du
constraint_error podczas wykonywania programu. Zauwa my, e atrybuty pred i succ
okre lone s tak e dla typów zmiennoprzecinkowych, a wi c dla liczb rzeczywistych, dla których
przecie nie istnieje "liczba nast pna po danej". To prawda, ale komputer jest w stanie reprezentowa
w sposób dokładny jedynie sko czon ilo liczb danego typu - ogranicza go na przykład rozmiar typu
- a wi c w tym wypadku jako liczb nast pn b d poprzedni rozumie si najbli sz liczb mo liw
do uzyskania (tzw. liczb maszynow ) wi ksz lub mniejsz od danej. W przypadku podtypów atrybuty
pred i succ operuj na typie bazowym danego podtypu, dlatego mo liwe jest napisanie
dni_robocze'pred(nie) - otrzymamy so - chocia ani so, ani nie nie nale do podtypu
dni_robocze.
Atrybuty t'pos(element) i t'val(numer) wykonuj
operacje odwrotne t'pos(element) zwraca pozycj (ang. position), na której stoi dany element w typie t, za
t'val(numer) zwraca element (warto - ang. value) stoj cy w typie t na pozycji o numerze
numer. Atrybuty te s okre lone tylko dla typów dyskretnych. Elementy ka dego typu wyliczeniowego
s ponumerowane - pierwszy element stoi na pozycji 0, nast pny 1 itd. Tak wi c np. dni'pos(pon)
81
daje 0, dni'val(4) da pt. Natomiast w typach całkowitych pozycja jest równa warto ci liczby
(integer'pos(-3) da -3, integer'pos(0) da 0). W przypadku podtypów atrybuty pos i val
operuj na typie bazowym. Srodek_tygodnia'pos(wt) daje 1, cho wt jest najmniejszym
elementem w tym podtypie - zwracana jest jednak pozycja wt w typie bazowym - dni. Podobnie
mo emy napisa
srodek_
tygodnia'val(0) lub srodek_tygodnia'pos(pon)
otrzymuj c pon lub 0, mimo e element ten le y poza zakresem podtypu.
Atrybuty t'image(element) i t'value(ci g_znaków) równie maj przeciwstawne
działanie. T'image(element) powoduje przekształcenie elementu w jego reprezentacj w postaci
ci gu znaków, czyli ła cucha (jest to przekształcenie na typ string, ale czego wi cej o tym typie
dowiemy si pó niej). T'value(ci g_znaków) zamienia ci g_znaków, czyli element typu
string, na element typu T, odpowiadaj cy danemu napisowi. I tak na przykład
integer'image(12) daje "12" - ci g znaków, za integer'value("12") da 12 - liczb .
Je eli operujemy na elementach typu wyliczeniowego, to odpowiadaj cy im ci g znaków zostanie
wypisany wielkimi literami. W przypadku liczb typu zmiennoprzecinkowego otrzymany ła cuch b dzie
przedstawiał liczb zapisan w formie wykładniczej, ze spacj lub minusem na pocz tku i jedn cyfr
przed kropk dziesi tn .
Ze wzgl du na obecno
typu wide_character w Adzie istniej tak e atrybuty
wide_image i wide_value, wykonuj cym takie same operacje jak image i value, ale na
ła cuchach typu wide_string zło onych ze znaków typu wide_character. Wyprowadzanie
takich napisów wymaga umieszczenia w klauzuli with pakietu ada.wide_text_io.
Atrybut t'base odwołuje si do typu bazowego danego podtypu (lub typu, ale wówczas jest
on sam dla siebie typem bazowym). Tak wi c na przykład napisanie positive'base'first jest
równoznaczne z napisaniem integer'first, a srodek_tygodnia'base'last - z napisaniem
dni'last. Atrybut ten mo e by wykorzystywany tak e w te cie członkostwa - mo emy napisa na
przykład
x in do_stu'base
co jest równowa ne z napisaniem
x in integer.
Atrybuty t'min i t'max wymagaj dwóch parametrów nale cych do typu t. Zwracaj one
odpowiednio mniejsz b d wi ksz z podanych warto ci (integer'max(10,2) daje 10,
dni'min(pon,pt) daje pon).
A oto program przykładowy z zastosowaniem atrybutów:
-- program dokonuje tlumaczenia podanej
-- nazwy dnia tygodnia
-- z jez. polskiego na angielski
with ada.text_io;
use ada.text_io;
procedure pr21 is
type dni is (poniedzialek, wtorek, sroda, czwartek, piatek,
sobota, niedziela);
type days is (Monday, Tuesday, Wednesday, Thursday, Friday,
Saturday, Sunday);
package dni_io is new ada.text_io.enumeration_io(dni);
package days_io is new ada.text_io.enumeration_io(days);
d:dni;
begin
put_line("Podaj nazwe dnia po polsku - otrzymasz nazwe dnia
po angielsku ");
81
new_line;
put("nazwa polska
: ");
dni_io.get(d);
put("nazwa angielska : ");
days_io.put(days'val(dni'pos(d)));
end pr21;
Instrukcja wyboru
Z typami wyliczeniowymi oraz tworzeniem podtypów zwi zana jest kolejna instrukcja
powoduj ca, e program działa w ró ny sposób w zale no ci od warto ci pewnego parametru instrukcja wyboru. Ma ona posta
[1]
case selektor is
when lista_mo liwo ci_1
when lista_mo liwo ci_2
..........
when lista_mo liwo ci_n
end case;
=> instrukcje_1
=> instrukcje_2
=> instrukcje_n
lub te
[2]
case selektor is
when lista_mo liwo ci_1
when lista_mo liwo ci_2
..........
when lista_mo liwo ci_n
when others
end case;
=> instrukcje_1
=> instrukcje_2
=> instrukcje_n
=> instrukcje_dalsze
Selektor musi by zmienn typu dyskretnego (czyli całkowitego lub wyliczeniowego). Je eli
przybierze on warto
znajduj c
si
na li cie_mo liwo ci_1, wykonywane s
instrukcje_1, je eli na li cie_mo liwo ci_2, wykonywane s instrukcje_2 itd. Je eli
warto selektora nie znajduje si na adnej z list, a instrukcja ma posta [2], to wykonywane s
instrukcje nast puj ce po when others. Instrukcja case musi zawiera odniesienie do ka dej z
mo liwych (teoretycznie) warto ci selektora, a wi c do wszystkich elementów typu (lub podtypu), do
którego on nale y. U yteczne jest przy tym napisanie when others => lub zdefiniowanie podtypu
obejmuj cego wszystkie rzeczywi cie przyjmowane przez selektor warto ci. Przypu my na przykład,
e zmienna b d ca selektorem jest liczb całkowit (np. typu integer) i oznacza numer dnia
tygodnia, a wi c przyjmuje warto ci od 1 do 7. Instrukcja wyboru musi obsłu y wszystkie te
przypadki. Je li napiszemy
case selektor is
when 1 => ...
when 2 => ....
...........
when 7=> ....
end case;
to podczas kompilowania programu wyst pi bł d. Nie obsłu yli my cało ci typu integer, a selektor
teoretycznie mo e przyj ka d warto całkowit . W celu rozwi zania tego problemu mo emy doda
w powy szej instrukcji jeszcze jedn , ostatni mo liwo
when others => null;
- wówczas w razie przyj cia przez selektor innej warto ci po prostu nie robimy nic, lub zadeklarowa
podtyp typu integer
subtype nr_dnia is integer range 1..7;
selektor:nr_dnia;
81
ograniczaj c zakres typu selektora do siedmiu rzeczywi cie mo liwych warto ci. Wówczas
wystarczaj ca jest instrukcja w postaci napisanej na pocz tku.
tak:
A jak mo e wygl da lista_mo liwo ci? Je eli jest ona jednoelementowa, na przykład
when 1 =>
when 'a' =>
when pon =>
-- pon jest elementem typu wyliczeniowego
Je eli lista ma mie wi cej elementów, mo emy ja zapisa w postaci
when 1| 2 | 7 =>
co oznacza, e zapisane dalej instrukcje wykonujemy, gdy selektor przyjmie warto
postaci
1, 2 lub 7, albo w
when 1..4 =>
Podane instrukcje b d wówczas wykonywane w przypadku przyj cia przez selektor warto ci z
powy szego przedziału . W razie potrzeby mo na ł czyc obie formy zapisu, np.
when 1 | 2 | 5..7 =>
Oto przykłady zastosowania instrukcji case:
-- przyklad uzycia instrukcji wyboru
-- program oblicza - w zaleznosci od zyczenia -- pole trojkata, prostokata badz kola
with ada.text_io,ada.numerics;
use ada.text_io;
procedure pr22 is
package int_io is new ada.text_io.integer_io(integer);
package flt_io is new ada.text_io.float_io(float);
use int_io, flt_io;
subtype mozliwosc is integer range 1..3;
nr:mozliwosc;
a,b:float;
begin
put_line(" Program oblicza pola nastepujacych figur :");
set_col(10);put_line("1 : trojkat ");
set_col(10);put_line("2 : prostokat");
set_col(10);put_line("3 : kolo");
new_line;
put("Podaj numer wybranej figury : "); get(nr);
new_line;
case nr is
when 1 => put("podaj dlugosc podstawy : ");
get(a);
put("podaj dlugosc wysokosci opuszczonej na te
podstawe : ");
get(b);
new_line;
put("pole trojkata wynosi ");
81
put(a*b/2.0,exp=>0);
put(" j.kw");
when 2 => put_line("podaj dlugosci bokow : ");
put("a : ");get(a);
put("b : ");get(b);
new_line;
put("pole prostokata wynosi ");
put(a*b,exp=>0);
put(" j.kw");
when 3 => put("podaj promien kola : ");
get(a);
new_line;
put("pole kola wynosi ");
put(ada.numerics.pi*a**2,exp=>0);
put(" j.kw");
end case;
end pr22;
Kolejny przykład - tym razem z selektorem nale cym do typu wyliczeniowego:
-- kolejny przyklad uzycia instrukcji wyboru
--- wazymy caly samochod
-- otrzymujemy wage ladunku
with ada.text_io;
use ada.text_io;
procedure pr23 is
type ciezarowka is (zuk, star_5t, jelcz_14t, steyer, inne);
package pod_sam is new ada.text_io.enumeration_io(ciezarowka);
use pod_sam;
package int_io is new ada.text_io.integer_io(integer);
use int_io;
waga_zuk : constant integer:=3000;
waga_star_5t : constant integer:=4000;
waga_jelcz_14t : constant integer:=10000;
waga_steyer : constant integer:=14000;
sam:ciezarowka;
mc,m:integer;
begin
new_line;
set_col(5);
put_line(" *** WAGA DLA SAMOCHODOW CIEZAROWYCH ***");
new_line;
put_line("znane masy : zuk, star_5t, jelcz_14t, steyer ");
put_line("wazony samochod (wybierz z listy lub wpisz ""inne"" ");
set_col(20);put("> ");
get(sam);
case sam is
when zuk
when star_5t
when jelcz_14t
when steyer
when inne
=>
=>
=>
=>
=>
m:=waga_zuk;
m:=waga_star_5t;
m:=waga_jelcz_14t;
m:=waga_steyer;
put("podaj mase wlasna samochodu (w kg) : ");
get(m);
81
end case;
put("podaj mase calosci : ");get(mc);
new_line;
put("masa ladunku wynosi : "); put(mc-m,0);
end pr23;
Instrukcje p tli
Stworzyli my program do obsługi wagi dla ci arówek. No dobrze - mo emy zapyta - ale czy
koniecznie osoba pracuj ca przy tej wadze musi uruchamia program od nowa, aby zwa yc kolejny
samochód? Czy nie mo na byłoby przerobi tego programu tak, aby po otrzymaniu wyniku znów
pojawiała si plansza "menu", gdzie wybieramy mark wa onego pojazdu, a działanie programu
zaka czamy przez wpisanie np. zera? Oczywi cie jest to mo liwe. W tym celu musimy zapozna si z
kolejnym rodzajem instrukcji, a mianowicie z instrukcjami p tli. Jest ich kilka - p tla "prosta", p tla
while i p tla for. Najmniej skomplikowana jest pierwsza z nich o składni
loop
instrukcje
end loop;
Instrukcje umieszczone wewn trz p tli b d w tym wypadku wykonywane niesko czenie wiele razy. Po
doj ciu do linii end loop nast puje powrót do pocz tku p tli, czyli do linii zawieraj cej słowo
loop, kolejne wykonanie itd. Teoretycznie program zawieraj cy tak p tl mógłby działa
nieprzerwanie. Na przykład program
with ada.text_io;
use ada.text_io;
procedure pr24 is
begin
loop
put("Ada ");
end loop;
end pr24;
b dzie zapisywał ekrany słowem "Ada", dopóki nie przerwiemy jego działania klawiszami Ctrl-Break
lub Ctrl-C. Je eli jednak wsród instrukcji wewn trz p tli znajdzie si słowo
exit;
lub
exit warunek;
to po doj ciu do tej linii (w przypadku pierwszym), lub po doj ciu do tej linii i stwierdzeniu, e
warunek zachodzi, nast pi wyj cie z p tli i wykonanie pierwszej z instrukcji nast puj cych po end
loop.
-- modyfikacja programu pr22
--- mozliwosc wielokrotnych obliczen
with ada.text_io,ada.numerics;
use ada.text_io;
procedure pr25 is
package int_io is new ada.text_io.integer_io(integer);
package flt_io is new ada.text_io.float_io(float);
use int_io, flt_io;
81
subtype mozliwosc is integer range 1..3;
nr:mozliwosc;
a,b:float;
odp:character;
begin
loop
put_line(" Program oblicza pola nastepujacych figur :");
set_col(10);put_line("1 : trojkat ");
set_col(10);put_line("2 : prostokat");
set_col(10);put_line("3 : kolo");
new_line;
put("Podaj numer wybranej figury : "); get(nr);
new_line;
case nr is
when 1 => put("podaj dlugosc podstawy : ");
get(a);
put("podaj dlugosc wysokosci opuszczonej na te
podstawe : ");
get(b);
new_line;
put("pole trojkata wynosi ");
put(a*b/2.0,exp=>0);
put(" j.kw");
when 2 => put_line("podaj dlugosci bokow : ");
put("a : ");get(a);
put("b : ");get(b);
new_line;
put("pole prostokata wynosi ");
put(a*b,exp=>0);
put(" j.kw");
when 3 => put("podaj promien kola : ");
get(a);
new_line;
put("pole kola wynosi ");
put(ada.numerics.pi*a**2,exp=>0);
put(" j.kw");
end case;
new_line(2);
put("czy liczymy dalej? (n - koniec) ");get(odp);
exit when odp='n';
new_line(5);
end loop;
end pr25;
Dzi ki zastosowaniu powy szej p tli nie musimy wielokrotnie uruchamia programu, je li
chcemy obliczyc pola kilku figur. Program pyta, czy chcemy jeszcze co obliczy , i je eli wci niemy
klawisz n, zaka cza działanie. Wci ni cie ka dego innego klawisza powoduje powtórne wykonanie
programu. W podobny sposób mo emy zmodyfikowa program pr23, co b dzie rozwi zaniem
przedstawionego na pocz tku problemu.
Kolejn p tl jest p tla while postaci
81
while warunek loop
instrukcje
end loop;
która jest wła ciwie podobna do prostej p tli zawieraj cej instrukcj exit. Instrukcje wewn trz p tli
wykonywane s tak długo, jak długo zachodzi warunek. Po raz pierwszy warunek sprawdzany jest
jeszcze przed wej ciem do p tli, a wi c istnieje mo liwo , e instrukcje wewn trz niej nie b d w
ogóle wykonane. Tak na przykład instrukcje wewn trz poni szej p tli
get(a);
while a<100 loop
.....
a:=a+2;
...
end loop;
mog nie byc wykonane, je li podamy a wi ksze b d równe 100.
Oto przykład wykorzystania tej instrukcji - program obliczaj cy, ile kolejnych liczb całkowitych
dodatnich nale y zsumowa , aby ich suma była wi ksza b d równa podanej liczbie:
------
przyklad wykorzystania petli while
obliczamy, ile kolejnych liczb
calkowitych dodatnich nalezy dodac,
aby ich suma byla wieksza lub rowna
podanej liczbie
with ada.text_io;
use ada.text_io;
procedure pr26 is
package int_io is new ada.text_io.integer_io(integer);
use int_io;
liczba,suma:integer;
n:natural;
begin
loop
put("Podaj liczbe calkowita dodatnia : ");
get(liczba);
exit when liczba>0;
put("To nie jest liczba dodatnia!!! ");
end loop;
suma:=0;n:=0;
while suma<liczba loop
n:=n+1;
suma:=suma+n;
end loop;
put("Zsumowalismy ");put(n,0);
if n mod 10 in 2..4 then
put(" kolejne liczby i otrzymalismy ");
else
put(" kolejnych liczb i otrzymalismy ");
end if;
put(suma,0);
end pr26;
A je eli chcemy, aby instrukcje wewn trz p tli wykonały si pewn , ci le okre lon ilo
W tym celu mo emy wykorzysta p tl for o składni:
[1]
for licznik_p tli in zakres loop
81
razy?
instrukcje
end loop;
albo
[2]
for licznik_p tli in reverse zakres loop
instrukcje
end loop;
Program pr27 ilustruje u ycie obu postaci tej p tli:
with ada.text_io;
use ada.text_io;
-- przyklad dzialania petli for
procedure pr27 is
package int_io is new ada.text_io.integer_io(integer);
use int_io;
begin
for i in 1..5 loop
put("Petla wykonuje sie po raz ");
put(i,0);
new_line;
end loop;
new_line(2);
for i in reverse 1..5 loop
put("Petla wykonuje sie dla liczby ");
put(i,0);
new_line;
end loop;
end pr27;
Wynikiem jego wykonania b dzie napisanie
Petla
Petla
Petla
Petla
Petla
wykonuje
wykonuje
wykonuje
wykonuje
wykonuje
sie
sie
sie
sie
sie
po
po
po
po
po
Petla
Petla
Petla
Petla
Petla
wykonuje
wykonuje
wykonuje
wykonuje
wykonuje
sie
sie
sie
sie
sie
dla
dla
dla
dla
dla
raz
raz
raz
raz
raz
1
2
3
4
5.
liczby
liczby
liczby
liczby
liczby
5
4
3
2
1.
Jak wida , w p tli postaci [1] licznik przyj ł na pocz tku warto 1 - dolne ograniczenie zakresu.
Ka de nast pne wykonanie p tli powoduje automatyczne zwi kszenie warto ci licznika o 1. Dzieje si
tak dopóki licznik nie osi gnie warto ci 5 - górnego ograniczenia zakresu. P tla w postaci [2] działa w
bardzo podobny sposób, ale jakby "od tyłu" - na pocz tku licznik przyjmuje warto c górnego
ograniczenia zakresu, przy ka dym przej ciu p tli jest zmniejszany o 1 a do osi gni cia warto ci
dolnego ograniczenia zakresu. Licznika p tli nie musimy (i nie mo emy) deklarowa ani nadawa mu
pocz tkowej ani kolejnych warto ci (pisz c na przykład wewn trz p tli i:=i+1), a je eli w programie
zadeklarowali my i u yli my zmiennej o tej samej nazwie, to zostanie ona przesłoni ta przez licznik
p tli, a wi c b dzie wewn trz p tli niemo liwa do u ycia. Nie mo emy sami zmieniac warto ci licznika
- napisanie np.
81
for i in 1..10 loop
.....
i:=5;
end loop;
jest niedozwolone (licznik jest traktowany podobnie jak stała). Jego warto
rozpatrywana na pocz tku p tli, zatem napisanie
i zgodno
z zakresem jest
for i in 2..1 loop
put("Napis");
end loop;
spowoduje, e napis taki nigdy si nie uka e, gdy licznik przyjmie na pocz tek warto 2, a wi c od
razu przekroczy górne ograniczenie zakresu (tzn. 1). Licznik p tli mo e przyjmowac warto ci nale ce
do typu dyskretnego. Nie mo emy napisa na przykład
for i in 1.2 .. 4.5 loop
mo emy natomiast - dla zadeklarowanego wcze niej typu wyliczeniowego dni napisa
for i in pon..czw loop
albo
for i in dni loop
Wida wi c, e w roli zakresu mo e wyst pi nazwa typu b d podtypu.
Zakres licznika p tli nie musi byc ustalony raz na zawsze - mo e si zmienia , jednak jego
warto musi by znana ju przed wej ciem do p tli. Mo na napisa na przykład
get(n);
for i in 1..n loop
...
end loop;
lub te
x:=150;
for j in 1..x loop
...
end loop;
czy
for k in 100..integer'last loop
...
end loop;
nie ma sensu natomiast
x:=3;
for i in 1..x loop
...
x:=20;
end loop;
Nie spowoduje to zgłoszenia bł du, ale p tla zostanie wykonana tylko trzy razy - zakres licznika p tli
został ustalony na pocz tku, przed wej ciem do niej.
Oto kilka przykładów zastosowania p tli:
-- przyklad zastosowania petli for
81
-- kilkakrotne narysowanie "mordki"
with ada.text_io;
use ada.text_io;
procedure pr28 is
package int_io is new ada.text_io.integer_io(integer);
use int_io;
n:integer;
begin
put("Ile rysunkow chcesz wykonac? ");get(n);
for i in 1..n loop
put(i,0);put_line(" : ");
put_line("\\\//");
put_line(" 0 0 ");
put_line(" ~_ ");
new_line;
end loop;
end pr28;
i modyfikacja programu pr15:
-- tabelka logiczna
-- modyfikacja programu pr15
-- przyklad uzycia petli
with ada.text_io;
use ada.text_io;
procedure pr29 is
package b_io is new ada.text_io.enumeration_io(boolean);
p,q:boolean;
begin
put_line("
new_line(3);
put(" |
p
new_line;
TABELKA WARTOSCI LOGICZNYCH");
|
q
|
not p
| p and q | p or q | p xor q |");
for p in boolean loop
for q in boolean loop
put(" | ");b_io.put(p,width=>5);
put(" | ");b_io.put(q,width=>5);
put(" | ");b_io.put(not p,width=>7);
put(" | ");b_io.put(p and q,width=>7);
put(" | ");b_io.put(p or q,width=>6);
put(" | ");b_io.put(p xor q,width=>7);
put(" |");
new_line;
end loop;
end loop;
end pr29;
Wró my jeszcze do przesłaniania przez licznik p tli warto ci zmiennych lub stałych
zadeklarowanych w programie, a maj cych t sam nazw . Czy to oznacza, e nie mo emy w ogóle
81
skorzysta z przesłoni tej zmiennej? Na szcz cie nie. Musimy jedynie poprzedzic j nazw procedury,
z której pochodzi, i kropk , jak w poni szym programie:
-- program demonstrujacy przeslanianie
-- zmiennych przez licznik petli
with ada.text_io;
use ada.text_io;
procedure pr30 is
package int_io is new ada.text_io.integer_io(integer);
use int_io;
i:integer:=8;
begin
put(i,0);
new_line;
for i in 1..3 loop
put(i,0);
new_line;
put(pr30.i,0);
new_line;
end loop;
-- wypisze 8
-- wypisze 1, 2 lub 3
-- wypisze 8
end pr30;
P tle mo emy tak e w sobie zagnie d a (czyli umieszcza jedn p tl w drugiej). Przykład
tego widzieli my ju w programie pr29. Je eli jednak przypadkiem nazwiemy liczniki obu p tli
jednakowo, to licznik p tli wewn trznej przesłoni licznik p tli zewn trznej, na przykład
-- program demonstrujacy przeslanianie licznika petli
-- przez drugi licznik (petli zagniezdzonej)
-- o tej samej nazwie
with ada.text_io;
use ada.text_io;
procedure pr31 is
package b_io is new ada.text_io.enumeration_io(boolean);
use b_io;
package int_io is new ada.text_io.integer_io(integer);
use int_io;
begin
for i in 1..3 loop
for i in boolean loop
put(i);
new_line;
end loop;
put(i,0);
new_line;
end loop;
end pr31;
-- wypisze wartosc logiczna
-- wypisze liczbe
Je eli za chcemy w wewn trznej p tli wypisa liczb b d c warto ci licznika p tli zewn trznej, to
musimy najpierw nada nazw p tli zewn trznej (lub obu p tlom) przez napisanie
nazwa_p tli:
....... loop
........ end loop nazwa_p tli;
81
Od tej pory mo emy odwoływa si do potrzebnej warto ci pisz c
nazwa_p tli.nazwa_licznika_p tli
Przykład takiego nazywania p tli znajduje si w poni szym programie
-- program demonstrujacy nazywanie petli
-- oraz jego skutki
with ada.text_io;
use ada.text_io;
procedure pr32 is
package b_io is new ada.text_io.enumeration_io(boolean);
use b_io;
package int_io is new ada.text_io.integer_io(integer);
use int_io;
begin
petla_duza:
for i in 1..3 loop
for i in boolean loop
put(i,0);
-- wypisze wartosc logiczna
new_line;
put(petla_duza.i,0); -- wypisze liczbe
new_line;
end loop;
put(i,0);
-- wypisze liczbe
new_line;
end loop petla_duza;
end pr32;
****************************************************************
Typ ła cuchowy
Zapoznali my si ju z typami znakowymi character i wide_character. W praktyce jednak
cz ciej ni pojedynczych znaków u ywamy ich ci gów. Ci giem takim jest np. imi , nazwisko...
Przydatny byłby wi c typ pozwalaj cy na przechowanie danych tego rodzaju w postaci jednej
zmiennej, a nie np. wielu pojedynczych zmiennych typu character. Powy sze oczekiwania spełniaj
dwa zdefiniowane w Adzie typy: string obejmuj cy ci gi znaków (inaczej ła cuchy) zło one z
elementów typu character oraz wide_string obejmuj cy ci gi znaków nale cych do typu
wide_character. Zmienne tych typów deklarujemy nast puj co:
nazwisko : string(1..20);
imie : string(5..21);
PESEL : string(1..11);
jakis_wide_string : wide_string(1..10);
czyli, mówi c ogólnie, piszemy:
zmienna_ła cuchowa :
string (dolne_ograniczenie_indeksu .. górne_ograniczenie_indeksu);
lub
zmienna_ła cuchowa : wide_string
(dolne_ograniczenie_indeksu .. górne_ograniczenie_indeksu);
81
gdzie ograniczenia indeksu musz byc liczbami typu positive, przy czym ograniczenie dolne
powinno by mniejsze od ograniczenia górnego (je eli nie jest, deklarowany jest ła cuch pusty).
Jako ła cuch traktowany jest dowolny ci g znaków uj ty w apostrofy, np.
"Ada95"
"1134"
"A+B=C"
"Jan Kowalski"
""
Ostatni z ła cuchów jest ła cuchem pustym (zapisany został jako dwa nast puj ce po sobie
cudzysłowy, mi dzy którymi nie stoi aden znak). Je eli natomiast chcemy, aby znak cudzysłowu był
elementem ła cucha, musimy oznaczy go przez dwa cudzysłowy nastepuj ce po sobie, np.
"Adam Mickiewicz jest autorem ""Pana Tadeusza"".".
Wyst puj ce w deklaracji ograniczenia indeksu okre laj długo
jego elementów. Obrazowo zmienn zadeklarowan
imie : string (1..8):="Karolina";
mo emy przedstawi
'K'
1
'a'
2
'r'
3
'o'
4
'l'
5
'i'
6
ła cucha oraz sposób numerowania
'n'
7
'a'
8
Chc c nada zmiennej imie now warto musimy pami ta , e wolno nam podstawi jedynie ła cuch
o długo ci takiej, jak podana w deklaracji. Zatem nie mo emy napisac na przykład
imie := "Anna";
imie := "Klementyna";
ale mo emy
imie := "Polikarp";
imie := "Anna
";
O zadeklarowanych rozmiarach ła cucha mog nas poinformowac odpowiednie atrybuty. I tak:
zmienna_ła cuchowa'first
zmienna_ła cuchowa'last
zmienna_ła cuchowa'range
zmienna_ła cuchowa'length
zwraca liczb b d c dolnym ograniczeniem indeksu
ła cucha
zwraca liczb b d c górnym ograniczeniem indeksu
ła cucha
zwraca zakres indeksu ła cucha (ograniczenie_dolne ..
ograniczenie_górne) - jest odpowiednikiem napisania
zmienna_ła cuchowa'first ..
zmienna_ła cuchowa'last
zwraca długo danego ła cucha (zadeklarowan ilo
znaków, czyli ograniczenie_górne minus
ograniczenie_dolne).
Maj c ła cuch mo emy wykonywac działania na jego elementach - znakach, oraz na fragmentach podła cuchach. Przy wyodr bnianiu ich z ła cucha wykorzystujemy fakt istnienia indeksu
(ponumerowania znaków). Do znaku stoj cego w ła cuchu odwołujemy si pisz c
zmienna_ła cuchowa (pozycja_w_ła cuchu),
natomiast do podła cucha
zmienna_ła cuchowa
(pozycja_znaku_pocz tkowego .. pozycja_znaku_ko cowego).
Daje nam to mo liwo
zamiany czy wypisywania fragmentów ła cucha. Maj c na przykład
imie : string (1..8) := "Karolina";
i podstawiaj c
81
imie(1):='C';
imie(8):= 'e';
dostaniemy ła cuch "Caroline", natomiast podstawiaj c
imie(3 ..5):="cia"
zmienimy ła cuch wyj ciowy na "Karolcia".
Nale y zwróci uwag na fakt, i napisanie imie(1) oraz imie(1..1) oznacza dwie ró ne rzeczy.
Pierwsze - to litera 'K', czyli znak (element typu character), drugie natomiast - "K" - ła cuch o
długo ci jeden (nale cy do typu string).
Je eli przy wybieraniu fragmentów ła cucha (zarówno znaków, jak i podła cuchów) przekroczymy
dolne b d górne ograniczemie indeksu, to zgłoszony b dzie bł d - Constraint_Error.
Mo liwe jest ł czenie ła cuchów ze sob . Operacja ta nosi miano konkatenacji. Mo emy np. napisa
s1 : string (1..3) := "Jan";
s2 : string (1..5) := "Nowak";
s3 : string (1..9) := s1&' '&s2;
co spowoduje nadanie ła cuchowi s3 warto ci "Jan Nowak".
Do ł czenia ła cuchów słu y operator &. Zezwala on na dodawanie do siebie nie tylko ła cuchów, ale i
znaków (w powy szym przykładzie doł czyli my spacj ), jednak wynik zawsze jest typu string:
"Jan"
"Jan"
'a' &
'+' &
& "Kowalski"
& ‘K’
'b'
"12"
daje
daje
daje
daje
"JanKowalski"
"JanK"
"ab"
"+12".
Ła cuch b d cy wynikiem konkatenacji ma długo równ sumie długo ci jego składników (tzn.
ł czonych ła cuchów), mo na go podstawi jedynie pod zmienn ła cuchow odpowiedniej długo ci.
Natomiast numeracja znaków w poszczególnych ła cuchach nie ma adnego znaczenia:
l1 : string (1..3)
:= "Ala";
l2 : string (10 .. 11) := "ma";
l3 : string(2 .. 5)
:= "kota";
l4 : string(121 .. 132):= l1 & ' ' & l2 & ' ' & l3;
Za wprowadzanie i wyprowadzanie znaków odpowiadaj
ada.text_io. Procedura put wypisuje ła cuch lub jego cz
imie : string(1..8) := "Karolina";
...
put (imie);
-- wypisze
put(imie(1..5));
-- wypisze
procedury umieszczone w pakiecie
Karolina
Karol
natomiast do wczytania zmiennej ła cuchowej u ywamy procedur get lub get_line. Napisanie
get(zmienna_ła cuchowa)
wymaga podania ła cucha o długo ci dokładnie takiej, jak zadeklarowana zmienna, natomiast
get_line(zmienna_ła cuchowa, zmienna_całkowita)
pozwala na podanie ła cucha o długo ci mniejszej b d równej długo ci zadeklarowanej.
Wprowadzanie trwa do naci ni cia klawisza <Enter> (lub do momentu, gdy wprowadzany ci g znaków
osi gnie zadeklarowan długo ). Pod zmienn _całkowit podstawiana jest rzeczywista długo
wprowadzonego ła cucha.
81
Ła cuchy mo na porównywa ze sob u ywaj c operatorów <, <=, >, >=, =, /=. Operacja ta jest
wykonywana zarówno dla ła cuchów o jednakowej, jak i o ró nej długo ci, a porównywane s kolejne
ich znaki . Prawdziwe s nast puj ce zale no ci:
string'("Jan") <= string'("Jaroslaw")
wide_string'("Ewa") >wide_string'("Anna")
string'("Jan") < string'("Janusz")
Jak wida , w przypadku porównywania ła cuchów o których nie wiemy, do jakiego typu nale ,
konieczne jest okre lenie tego typu w celu unikni cia niejednoznaczno ci.
Warto pami ta równie o mo liwo ci zamiany liczby (lub danej innego typu) na typ string i
odwrotnie. Słu do tego (omawiane juz w podrozdziale “Atrybuty typów”) atrybut T’image,
zamieniaj cy element typu T na odpowiadaj cy mu ła cuch, oraz T’value, zamieniaj cy ła cuch na
element nale cy do typu T.
-- program wypisuje pewne dane osobowe
-- na podst. podanego numeru PESEL
with ada.text_io;
use ada.text_io;
procedure pr33 is
PESEL : string(1..11);
begin
put("Podaj swoj numer PESEL > ");
get(PESEL);
new_line;
put_line("Powiem Ci, czego dowiedzialem sie o Tobie:");
put("
...urodziles sie " & pesel(5..6) & '.' & pesel(3..4) &
'.' & "19" & pesel(1..2) & " roku" );
new_line;
put("
...jestes ");
if integer'value(pesel(10..10)) mod 2 = 0 then
put("kobieta");
else
put("mezczyzna");
end if;
end pr33;
-----
przyklady operacji na stringu
na przyklad dopelnianie
koncowki lancucha spacjami
oraz zamiana malych liter na duze
with ada.text_io,ada.integer_text_io;
use ada.text_io,ada.integer_text_io;
procedure pr34 is
imie, nazwisko : string(1..40);
d1,w1,poz : integer;
begin
put_line("Program wypisze ozdobna wizytowke ");
-- wczytanie imienia i umieszczenie go
-- w srodku lancucha
81
put("Podaj imie : ");
get_line(imie,d1);
w1:=(imie'length-d1)/2;
imie(w1+1..w1+d1):=imie(1..d1);
for i in 1..w1 loop
imie(i):=' ';
end loop;
for i in w1+d1+1 .. imie'length loop
imie(i):=' ';
end loop;
--wczytanie nazwiska i umieszczanie go
-- w srodku lancucha
put("Podaj nazwisko : ");
get_line(nazwisko,d1);
w1:=(nazwisko'length-d1)/2;
nazwisko(w1+1..w1+d1):=nazwisko(1..d1);
for i in 1..w1 loop
nazwisko(i):=' ';
end loop;
for i in w1+d1+1 .. nazwisko'length loop
nazwisko(i):=' ';
end loop;
-- zamiana malych liter na duze
for i in 1..nazwisko'length loop
poz:=character'pos(nazwisko(i));
if poz>=character'pos('a') and poz<=character'pos('z') then
nazwisko(i):=character'val(poz-32);
end if;
end loop;
-- wypisanie wizytowki
for i in 1..42 loop put('*');end loop;
new_line;
put('*'& imie & '*');
new_line;
put('*' & nazwisko & '*');
new_line;
for i in 1..42 loop put('*');end loop;
end pr34;
Zwró my uwag na wyst puj cy w programie pr33 zapis ła cucha o długo ci przekraczajacej długo
jednej linii:
put ("To jest baaaaaaaaaaaaaaaaaaaaaaaaaaaaaardzo dlugi lancuch " &
"a moze nawet jeszcze dluuuuuuuuuuuuuuuuuuuuuuzszy lancuch " &
"i jak widac jego zapis nie zmiescil si w jednej linijce " &
"ale i tak na ekranie bedzie wygladal inaczej niz tutaj...");
Dotychczas u ywali my ła cuchów o konkretnej, ustalonej długo ci. Niejednokrotnie jednak przydatna
byłaby mo liwo c u ycia ła cuchów, których długo
zostanie okre lona dopiero w trakcie
wykonywania programu. Istnieje kilka sposobów rozwi zania tego problemu. Jednym z nich jest
zadeklarowanie zmiennej ła cuchowej jako nale cej do typu string (bez podawania ogranicze
indeksu) i przypisanie jej danej warto ci, np.
x : string := integer’image(N);
gdzie N jest zmienn typu całkowitego o wcze niej przypisanej warto ci (zob. rozdział “Podprogramy”
w dalszej cz ci ksi ki). Zmienna ła cuchowa x w niejawny sposób przyjmuje długo tak , jak
ła cuch b d cy zapisem liczby N. Niestety, długo ci tej nie mo na ju zmieni w trakcie działania
programu:
81
N : integer := 10;
x : string := integer’image (N);
-- x przyjmie warto " 10"
-- (wiod ca spacja! - liczba jest dodatnia)
....
N := 120;
x:= integer’image(N);
Ostatnia instrukcja spowoduje wyst pienie bł du Constraint_Error (próbujemy wstawi ła cuch
czteroznakowy - " 120" do zmienne ła cuchowej mog cej si składa z co najwy ej trzech znaków).
Przykład ten mo e zrazu wydawa si dziwny, ale zobacz rozdział “Bloki” w dalszej cz ci ksi ki.
Inn - mniej eleganck - metod pozwalaj c na u ywanie ła cuchów o “okre lanej w czasie
wykonania programu długo ci” jest wstawianie wprowadzanego ci gu znaków do zmiennej
ła cuchowej o okre lonej długo ci i dopełnianie reszty spacjami (ich ilo przeliczana jest podczas
wykonywania programu). Podobn operacj widzieli my w programie pr34.
-- przyklady wypisywania lancucha
-- jak wypisac ladnie, a jak brzydko
with ada.text_io;
use ada.text_io;
procedure pr35 is
str:string(1..60);
n:integer;
begin
put("Podaj napis (do 60 znakow) >");
get_line(str,n);
new_line;
put_line("Lancuch od 1-go do " &
integer'image(n) &"-go znaku: ");
put_line(str(1..n));
put_line("Caly lancuch: ");
put_line(str);
put_line("Teraz dopelniam koncowke spacjami... ");
for i in n+1..str'last loop
str(i):=' ';
end loop;
put_line("I ponownie caly lancuch - zakoncze go [*]: ");
put(str);put_line("[*]");
put("Prawda, ze ladniejszy?...");
end pr35;
W powy szym programie wypisywanie podła cucha str(1..n) i - za drugim razem - str daje (na
ekranie) poniek d ten sam efekt, jednak w drugim przypadku po wypełnieniu reszty ła cucha spacjami
nie musimy pami ta ilo ci wprowadzonych znaków, co w dłu szych programach nie jest bez
znaczenia. Bez dopisania spacji ko cówka ła cucha str wypełniona b dzie przypadkowymi znakami
stanowi cymi interpretacj aktualnej zawarto ci odpowiednich komórek pami ci. Po napisaniu
put(str);
te znaki równie zostałyby wy wietlone na ekranie.
Omówili my tutaj jedynie podstawowe operacje na typie string. Rozszerzenie przedstawionych
mo liwo ci oferuj pakiety Ada.Strings i Ada.Vstrings (zob. rozdział... )
@?
Tablice
W poprzednim rozdziale ustawiali my znaki na ponumerowanych miejscach tworz c ła cuchy. Obecnie
poznamy podobn zło on struktur danych - tablic . Tablica jest obiektem zło onym z wielu
81
elementów (tzw. składowych) nale cych do tego samego typu. Do całej tej struktury odwołujemy si
za pomoc pojedynczego identyfikatora. Mo emy równie odwoływa si do poszczególnych jej
składników.
Tablice jednowymiarowe
Tablice mog byc jedno- i wielowymiarowe. Na pocz tek zajmiemy si tablicami jednowymiarowymi.
Mog by one zadeklarowane jako tzw. anonimowy typ tablicowy
zmienna : array (okre lenie_indeksu) of typ_składowych;
lub jako typ tablicowy posiadaj cy własn nazw :
type typ_tablicowy is array (okreslenie_indeksu) of typ_składowych;
co pozwala nast pnie na deklarowanie zmiennych lub stałych nale cych do typu_tablicowego.
Okre lenie_indeksu mo e mie jedn z postaci:
dolne_ograniczenie_zakresu .. górne_ograniczenie_zakresu
typ_indeksu
typ_indeksu range
dolne_ograniczenie_zakresu .. górne_ograniczenie_zakresu
przy czym indeks - niezale nie od sposobu jego zadeklarowania - musi nale e do typu dyskretnego
(je li typ nie jest podany bezpo rednio, przyjmowana jest warto
uniwersalna, np.
Universal_Integer).
Oto przykłady deklaracji tablic:
ciag1 : array (1..10) of integer;
ciag : array (integer range 1..10) of float;
n: integer := 7;
type xx is array (n .. 10+n) of float;
x1: xx;
type miesiace is (styczen, luty, marzec, kwiecien, maj, czerwiec,
lipiec, sierpien, wrzesien, pazdziernik, listopad, grudzien);
opady_Polska_97 : constant array (miesiace) of float:=(others=>0.0);
type opady is array (miesiace) of float;
opady_Warszawa_98, opady_Lodz_98 : opady;
subtype miesiace_wakacyjne is miesiace range lipiec..wrzesien;
type miejsca is (morze, gory, jeziora, wlasny_dom);
wakacje : array (miesiace_letnie) of miejsca;
type ciag_liczbowy is array (integer range 0..100) of integer;
ciag_A, ciag_B : ciag_liczbowy;
Do elementów tablicy odwołujemy si poprzez nazw zmiennej tablicowej i warto
umieszczon w nawiasach okr głych. Indeks mo e by wyra eniem, stał lub zmienn .
indeksu
ciag_A(1) := 12;
put(opady_Lodz_98(styczen));
put(opady_Polska_97(wrzesien));
k:=7;
put(ciag_A(1+k));
Tablica mo e zosta wypełniona poprzez kolejne przypisywanie warto ci poszczególnym jej
elementom, jak w przedstawionym przykładzie:
for i in miesiace loop
opady_Warszawa_98(i):=0.0;
end loop;
81
lub przez u ycie tzw. agregatu tablicy (ang. array aggregate), tzn. warto ci przypisywanej całej tablicy
od razu, a zło onej z warto ci poszczególnych jej składowych, np.
wakacje := (morze, gory, jeziora);
Jest to tzw. notacja pozycyjna (ang. positional notation). Poszczególne liczby z agregatu tablicy
przypisywane s kolejnym składowym. Stanowi to odpowiednik ci gu instrukcji
wakacje (lipiec)
:= morze;
wakacje (sierpien) := gory;
wakacje (wrzesien) := jeziora;
Oczywi cie ilo elementów w agregacie tablicy musi by równa ilo ci elementów w samej tablicy.
Oprócz notacji pozycyjnej mo emy u ywa notacji nazywanej (ang. named notation), w której
okre lamy wyra nie, na które miejsce w tablicy ma by wstawiona dana warto :
wakacje := (lipiec => morze, wrzesien=>jeziora, sierpien => gory);
Unikamy w ten sposób pomyłek (porz dek podstawiania nie ma znaczenia), a poza tym łatwo jest
odczyta , jak warto przypisali my poszcególnym elementom tablicy. Notacja pozycyjna zezwala
ponadto na uzywanie słowa others pozwalaj cego na nadanie warto ci wszystkim elementom,
którym nie została ona nadana bezpo rednio:
wakacje : (lipiec => morze, others => wlasny_dom);
Podstawienie takie oznacza, e w lipcu byli my nad morzem, za pozostałe miesi ce wakacji
sp dzili my w domu... U ywaj c tego sposobu mo na równie przypisa jednakow warto wszystkim
elementom tablicy:
wakacje := (others => wlasny_dom);
Mo na to równie zrobi pisz c
wakacje := (miesiace_wakacyjne => wlasny_dom);
albo
wakacje := (lipiec.. wrzesien => wlasny_dom);
Notacja pozycyjna oferuje jeszce kilka mo liwo ci:
ciag_A := (1..3 => 2, others => 0);
(elementy o indeksie 1, 2 i 3 otrzymaj warto
2, pozostałe - warto
0),
ciag_A := (2|4|6|8|10 => 1, others => 0);
(wyrazy ci gu o numerach parzystych otrzymuj warto
1, pozostałe - 0).
Notacj pozycyjn i nazywan mo na ł czy tylko w jednym przypadku: gdy w agregacie tablicy
wymienimy kolejno pewn ilo warto ci składowych (w notacji pozycyjnej), a nast pnie pozostałym
elementom (maj cym wy sze indeksy) nadamy tak sam warto u ywaj c others (notacja
nazywana):
ciag_A := (1, 12, 0, 3, others => 9);
Zauwa my, e słowo others, je li wyst puje, musi by ostatni pozycj w agregacie tablicy. Nale y
równie pami ta , e elementami agregatu tablicy nie musz byc konkretne warto ci, jak w powy szych
przykładach, lecz równie zmienne, stałe i wyra enia.
81
Z tablic mo emy "wydobywa " nie tylko pojedyncze elementy, ale równie podtablice - fragmenty
tablicy wyj ciowej (podobnie jak podła cuchy z ła cucha). Tablice i ich fragmenty wolno nam (o ile
nale do tego samego typu) podstawia , porównywa przy u yciu operatorów = i /=, a tak e ł czy
(operatorem jest wtedy &, podobnie jak dla typu string). Oczywi cie podstawiaj c czy ł cz c
fragmenty tablic musimy zwraca uwag na ich rozmiary, w przeciwnym wypadku otrzymamy
Constraint_Error. Je eli składowe tablic s typu dyskretnego, dozwolone jest równie
porównywanie ich przy u yciu operatorów <, <=, >, >=.
-- rozne eksperymenty na ciagach:
-- porownywanie, podstawianie...
with ada.text_io,ada.integer_text_io,ada.float_text_io;
use ada.text_io, ada.integer_text_io,ada.float_text_io;
procedure pr36 is
type dni is (pon, wt, sr, czw, pt, sob, nie);
type
type
type
type
ciag_dluzszy is array(1..10) of integer;
ciag_krotszy is array(1..5) of integer;
ciag_rzeczywisty is array(1..5) of float;
ciag_wyliczeniowy is array(1..10) of dni;
cd : ciag_dluzszy;
ck1,ck2 : ciag_krotszy;
cr1,cr2: ciag_rzeczywisty;
cw1, cw2: ciag_wyliczeniowy;
begin
cd := (2|4|6|8|10 => 1, others => 0);
ck1:=(others=>7);
ck2:=(others=>7);
cr1:=(others=>3.2);
cr2:=(others=>4.1);
cw1:=(others=>pt);
cw2:=cw1;
-- ck1(1..2):=cd(3..4);
-- nie da sie zrobic takiego
-- podstawienia (ciagi roznego typu)
ck1(1):=cd(3);
-- tak sie da (elementy w ciagach sa
-- tego samego typu)
ck1(1..3):=ck2(2..4);
-- tak sie da (ten sam typ tablicowy)
-- tak mozna porownac tablice tego
-- samego typu
if ck1=ck2 then
put_line("tablice - ""krotsze ciagi calkowite"" - rowne ");
else
put_line("tablice - ""krotsze ciagi calkowite"" - rozne");
end if;
if cr1=cr2 then
put_line("tablice - ""ciagi rzeczywiste"" - rowne ");
else
put_line("tablice - ""ciagi rzeczywiste"" - rozne");
end if;
if cw1=cw2 then
put_line("tablice - ciagi dni - rowne ");
else
81
put_line("tablice
end if;
- ciagi dni - rozne");
-- te tablice nie sa tego samego typu,
-- zatem nie mozna ich porownac
-- if ck1(1..2)=cd(1..2) then put("kawalki rowne"); end if;
-- takie porownywanie mozna zrobic
-- tylko dla tablic o skladowych dyskretnych
if ck1>ck2 then
put_line("tablica liczb calkowitych ck1 jest ""wieksza""");
else
put_line("tablica liczb cakowitych ck1 nie jest ""wieksza""");
end if;
if cw1>cw2 then
put_line("tablica dni cw1 jest ""wieksza""");
else
put_line("tablica dni cw1 nie jest ""wieksza""");
end if;
-- niewykonalne - typ float nie jest dyskretny
--if cr1>cr2 then
-put_line("tablica liczb rzeczywistych cr1 jest ""wieksza""");
--else
-put_line("tablica
liczb
rzeczywistych
cr1
nie
jest
""wieksza""");
--end if;
cr2:=cr1(1..2) & cr2(2..4);
cw1:=cw1(1..5) & cw2(6..10);
-- wykonalne
--cw1:=cw1(2..4) & cw2(3..6); -- niewykonalne - niezgodne rozmiary
--ck1:=ck2(1..2) & cd(3..5); -- niewykonalne - niezgodne typy tablic
end pr36;
Dla tablic okre lone s nast puj ce atrybuty:
zmienna_tablicowa'first
zmienna_tablicowa'last
zmienna_tablicowa'range
zmienna_tablicowa'length
zwracaj cy najmniejsz warto indeksu wdanej tablicy
zwracaj cy najwi ksz warto indeksu wdanej tablicy
zwracaj cy zakres indeksu tablicy (odpowiednik
tablica'first .. tablica'last)
zwracaj cy długo tablicy (ilo jej elementów).
Oto przykład zastosowania atrybutów w praktyce. Chc c działa na ci gu innej długo ci wystarczy
jedynie zmieni deklaracj typu ciag, cała reszta mo e pozosta bez zmian.
-- sortowanie ciagu liczb calkowitych
-- oraz znajdowanie najwiekszego elementu
-- tego ciagu
with ada.text_io,ada.integer_text_io;
use ada.text_io,ada.integer_text_io;
procedure pr37 is
type ciag is array(1..10) of integer;
81
c,d:ciag;
pom,max,miejsce_maxa:integer;
begin
put("mozesz wprowadzic ciag liczb calkowitych o dlugosci ");
put(c'length,0);new_line;
put_line("podaj wyrazy ciagu : ");
for i in c'range loop
put("c(");put(i,0);put(")= ");
get(c(i));
end loop;
-- znajdowanie najwiekszej liczby w ciagu
max:=c(c'first);
miejsce_maxa:=c'first;
for i in c'first+1..c'last loop
if c(i)>max then
max:=c(i);
miejsce_maxa:=i;
end if;
end loop;
put("Najwiekszy wyraz w ciagu to "); put(max,0); new_line;
put("Stoi on na miejscu "); put(miejsce_maxa,0);
new_line(3);
-- sortowanie ciagu
d:=c;
for i in d'first+1..d'last loop
for j in reverse i..d'last loop
if d(j)<d(j-1) then
pom:=d(j);
d(j):=d(j-1);
d(j-1):=pom;
end if;
end loop;
end loop;
put_line("Oto Twoj ciag: ");
for i in c'range loop
put(c(i),0);put(' ');
end loop;
new_line;
put_line("A oto Twoj ciag posortowany: ");
for i in d'range loop
put(d(i),0);put(' ');
end loop;
end pr37;
Szczególnym przypadkiem tablic jednowymiarowych s wektory logiczne (boolowskie) jednowymiarowe tablice zło one z elementów typu boolean. Oprócz podstawiania i porównywania
mo emy dokonywa na nich operacji logicznych, u ywaj c not, and, or , xor. Wszystkie powy sze
operatory (prócz not) wymagaj jako argumentów dwóch tablic tego samego typu i długo ci (not
operuje na jednej tablicy). Otrzymujemy w ten sposob agregat tablicy, którego składowe s wynikami
wykonania odpowiedniej operacji logicznej na odpowiednich składowych tablic b d cych
argumentami.
-- program ilustruje dzialania
-- na wektorach logicznych
81
with ada.text_io,ada.integer_text_io;
use ada.text_io,ada.integer_text_io;
procedure pr38 is
package b_io is new ada.text_io.enumeration_io(boolean);
use b_io;
type wektor_logiczny is array(1..4) of boolean;
p,q: wektor_logiczny;
w_and,w_or,w_xor,w_not:wektor_logiczny;
k:positive_count;
begin
p:=(others=>true);
put_line("Podaj wektor q - mozliwe wartosci jego skladowych" &
" to TRUE i FALSE");
for i in q'range loop
put("q(");put(i,0);put(")= ");
get(q(i));
end loop;
w_and:=p and q;
w_or:=p or q;
w_xor:=p xor q;
w_not:=not q;
put_line("A oto wyniki dzialan na wektorach: ");
put("wektor p : ");
k:=13;
for i in p'range loop k:=k+7; set_col(k); put(p(i)); end loop;
new_line;
put("wektor q : ");
k:=13;
for i in q'range loop k:=k+7; set_col(k); put(q(i));end loop;
new_line;
put("wektor not q : ");
k:=13;
for i in q'range loop k:=k+7; set_col(k); put(w_not(i));end loop;
new_line;
put("wektor p and q : ");
k:=13;
for i in w_and'range loop k:=k+7; set_col(k); put(w_and(i));end loop;
new_line;
put("wektor p or q : ");
k:=13;
for i in w_or'range loop k:=k+7; set_col(k); put(w_or(i));end loop;
new_line;
put("wektor p xor q: ");
k:=13;
for i in w_xor'range loop k:=k+7; set_col(k); put(w_xor(i));end loop;
end pr38;
Tablice wielowymiarowe
Oprócz poznanych dot d tablic jednowymiarowych istniej równie tablice wielowymiarowe.
Analogicznie jak tablice jednowymiarowe przechowuj one dane nale ce do jednego typu, bardzo
podobna jest równie ich deklaracja:
zmienna_tablicowa :
array (okre lenie_indeksu_1, ... , okre lenie_indeksu_n)
81
of typ_elementów;
(jest to anonimowy typ tablicowy), lub
type typ_tablicowy is
array (okre lenie_indeksu_1, ... , okre lenie_indeksu_n)
of typ_elementów;
Okre lenia_indeksów maj tak sam posta , jak w przypadku tablic jednowymiarowych, mo e ich by
dowolnie du o. Poszczególne indeksy nie musz by tego samego typu. Oto przykłady deklaracji tablic
wielowymiarowych:
dwa_na_trzy : array (1..2, 1..3) of integer;
type macierz_kwadratowa is array (integer range 1..2 ,
integer range 1..2 ) of float;
m1: macierz_kwadratowa;
type miesiace is (styczen, luty, marzec, kwiecien, maj czerwiec,
lipiec, sierpien, wrzesien, pazdziernik, listopad,
grudzien);
subtype miesiace_wakacyjne is miesiace range lipiec .. wrzesien;
type miejsca is (morze, gory, jeziora, wlasny_dom);
type wakacje is array (1996..1998, miesiace_wakacyjne) of miejsca;
wakacje_Ani, wakacje_Piotra: wakacje;
type miasta is (Warszawa, Moskwa, Praga, Londyn, Paryz);
type opady_w_miastach is array (miasta, miesiace_wakacyjne,
1987 .. 1997) of float;
pomiar : opady_w_miastach;
x: integer:=3;
type xx is array (x..x+2, 2*x..3*x, 4..x+2) of miesiace;
xxx: xx;
Do składowych tablic wielowymiarowych odwołujemy si poprzez nazw tablicy i warto ci indeksu
umieszczone w nawiasach okr głych:
m1(1,1):=12;
wakacje_Ani(1998,lipiec):=morze;
Nadawanie warto ci poszczególnym składowym tablicy wielowymiarowej mo e odby si w p tli
(a raczej kilku p tlach):
for i in 1..2 loop
for j in 1..2 loop
m1 (i,j) := 2;
end loop;
end loop;
for i in miasta loop
for m in miesiace_wakacyjne loop
for r in 1987 .. 1997 loop
pomiar (i, m, r) := 12.0;
end loop;
end loop;
end loop;
lub przy u yciu agregatu tablicy:
wakacje_Piotra := (1996 .. 1998 => (lipiec..wrzesien => wlasny_dom));
wakacje_Piotra := (1996..1998 => (miesiace_wakcyjne =. wlasny_dom));
wakacje_Piotra := (others => (others => wlasny_dom));
81
Dozwolone jest u ywanie zarówno notacji pozycyjnej, jak i nazywanej dla ka dego z wymiarów.
Mo na je równie dowolnie ze sob ł czy , cho notacja nazywana jest bardziej czytelna. Ograniczenia
dotycz ce ł czenia notacji pozycyjnej i nazywanej przedstawione przy okazji tablic jednowymiarowych
obowi zuj nadal w obr bie pojedynczego podagregatu uj tego w nawiasy. Oto przykłady:
wakacje_Ani := ((morze, jeziora, morze), (gory, jeziora, wlasny_dom),
(wlasny_dom, morze, wlasny_dom));
wakacje_Ani := ( 1996 => (morze, jeziora, morze),
1997 => (gory, jeziora, wlasny_dom),
1998 => (wlasny_dom, morze, wlasny_dom) );
wakacje_Ani :=(
(lipiec => morze, sierpien => jeziora,
wrzesien => morze),
(lipiec =>gory, sierpien => jeziora,
wrzesien => wlasny_dom),
(lipiec =>wlasny_dom, sierpien => morze,
wrzesien => wlasny_dom) );
wakacje_Ani := (
1996 => (lipiec => morze, sierpien => jeziora, wrzesien =>morze),
1997 => (lipiec =>gory, sierpien => jeziora, wrzesien => wlasny_dom),
1998 =>(lipiec =>wlasny_dom, sierpien => morze,
wrzesien => wlasny_dom)
);
m1
m1
m1
m1
:=
:=
:=
:=
((0,1), (7,8));
( 1=> (0,1), 2=> (7,8));
( (1=>0, 2=>1), (1=>7, 2=>8));
(1=> (1=>0, 2=>1), 2=> (1=>7, 2=>8));
Z tablic wielowymiarowych nie mo emy "wycina kawałków", tak jak z tablic jednowymiarowych.
Mo emy jednak sprawdza , czy tablice te s równe, czy nie (u ywaj c =, /=), o ile nale one do tego
samego typu.
Dla tablic wielowymiarowych, analogicznie jak dla jednowymiarowych, okre lone s atrybuty
'First, 'Last, 'Length i 'Range, dotycz jednak nie całej tablicy, lecz okre lonego indeksu
("wymiaru"):
subtype miesiace_wakacyjne is miesiace range lipiec..wrzesien;
type miejsca is (morze, gory, jeziora, wlasny_dom);
type wakacje is array (1996..1998, miesiace_wakacyjne) of miejsca;
wakacje_Ani: wakacje;
wakacje_Ani'first(1)
wakacje_Ani'last(2)
wakacje_Ani'range(1)
wakacje_Ani'range(2)
wakacje_Ani'length(1)
wakacje_Ani'length(2)
-------
zwróci 1996
zwróci wrzesien
1996..1998
lipiec..wrzesien
3
4
Je eli u ywaj c atrybutu pominiemy okre lenie wymiaru, domy lnie przyjmowane b dzie jeden. Zatem
wakacje_Ani'first i wakacje_Ani'first(1) zwróc t sam warto .
-- program oblicza srednia ilosc dni slonecznych
-- w miesiacach letnich w poszczegolnych miastach
with ada.text_io,ada.integer_text_io;
use ada.text_io,ada.integer_text_io;
procedure pr39 is
type miasta is (Berlin,Londyn, Paryz, Warszawa, Rzym);
type miesiace is (styczen, luty, marzec, kwiecien, maj,
czerwiec, lipiec, sierpien, wrzesien,
pazdziernik, listopad, grudzien);
81
package mies_io is new ada.text_io.enumeration_io(miesiace);
package miasta_io is new ada.text_io.enumeration_io(miasta);
use mies_io,miasta_io;
type ilosc_dni is array
(miasta, miesiace range czerwiec..sierpien) of integer;
sloneczne : ilosc_dni;
s:integer;
begin
put("Podaj ilosc dni slonecznych w poszczegolnych miastach" &
" i miesiacach: ");
new_line(2);
for i in sloneczne'range(1) loop
for j in sloneczne'range(2) loop
put(i);put(" / ");put(j);put(" : ");
get(sloneczne(i,j));
end loop;
end loop;
new_line(3);
put("W okresie ");put(sloneczne'first(2));
put(" - ");put(sloneczne'last(2));
put_line(": ");
for i in miasta loop
s:=0;
for j in sloneczne'range(2) loop
s:=s+sloneczne(i,j);
end loop;
s:=s/sloneczne'length(2);
put("srednia ilosc dni slonecznych w miesiacu w miescie ");
put(i);
put(" wyniosla ");put(s,0);
new_line;
end loop;
new_line;
put("Wyniki zostaly zaokraglone do wartosci calkowitych.");
new_line;
end pr39;
Tablice anonimowe (anonimowy typ tablicowy)
Zarówno w przypadku tablic jedno-, jak i wielowymiarowych widzieli my przykłady tzw.
anonimowego typu tablicowego. Typ taki nie ma własnej nazwy i nie istnieje jako co odr bnego zdefiniowany jest niejako na u ytek konkretnej zmiennej. Ka da zmienna b d ca anonimow tablic
nale y do innego typu, niemo liwe jest zatem ich porównywanie czy przypisywanie:
Oto dwa fragmenty programów. W pierwszym z nich deklarujemy odr bny typ tablicowy, za w drugim
u ywamy typu anonimowego.
type towary is (garnitury, marynarki, spodnie);
type produkcja is array (towary) of natural;
prod_IV, prod_V : produkcja;
...
prod_IV := (garnitury => 3_000, marynarki => 2_000,
spodnie => 5_000);
prod_V := prod_IV;
-- mo liwe do wykonania
prod_V(garnitury) := prod_IV(spodnie);
-- równie mo liwe - składniki
-- s tego samego typu
if prod_V > prod_IV then
...
-- tak e wykonalne,
-- tablice s jednowymiarowe
-- i tego samego typu
81
type towary is (garnitury, marynarki, spodnie);
prod_IV, prod_V : array (towary) of natural;
-- tablice anonimowe
...
prod_IV := (garnitury => 3_000, marynarki => 2_000,
spodnie => 5_000);
prod_V := prod_IV;
-- bł d, tablice nie nale do tego samego typu
prod_V(spodnie):=prod_IV(garniury); -- prawidłowo - elementy tablic s
-- tego samego typu
if prod_V > prod_IV then ...
-- bł d - tablic ró nych typów nie mo na
-- porówna
Jak wida , stosowanie tablic anonimowych pozbawione jest sensu, je eli w programie u ywamy kilku
tablic o takim samym wygl dzie (a wi c mog cych nale e do tego samego typu).
Tablice dynamiczne
Tablic dynamiczn nazywamy tablic , której rozmiar staje si znany dopiero w momencie
wykonywania programu. Mo na zdefiniowa w ten sposób zarówno tablic anonimow , jak i typ
posiadaj cy nazw . Do tego celu u ywamy tzw. bloku programu (wi cej informacji na ten temat mo na
b dzie znale w dalszej cz ci ksi ki):
procedure xxx is
n: integer;
begin
get(n);
declare
-- pocz tek bloku
x: array (1..n) of integer;
begin
-- tu u ywamy tablicy x
end;
-- koniec bloku
end xxx;
W trakcie kompilacji programu rozmiar tablicy x nie jest jeszcze znany. Wła ciwa deklaracje tablicy x,
a wi c zarezerwowanie dla niej odpowiednio du ego obszaru pami ci, ma miejsce po wczytaniu n
(czyli po uruchomieniu programu). Wykonywane s wówczas instrukcje zawarte w cz ci deklaracyjnej
bloku - mi dzy declare a begin. Tablica x " yje" tylko wewn trz bloku, w którym została
zadeklarowana, a wi c do słowa end (oczywi cie je li słowo end ko cz ce blok poprzedza
bezpo rednio end ko cz cy procedur , tablica taka funkcjonuje od momentu zadeklarowania do ko ca
programu). Mo liwo deklarowania takich tablic jest wielkim udogodnieniem - napisany przez nas
program mo e obsługiwa dowolnie du o danych, wykorzystuj c za ka dym razem inn ,
odpowiadaj c potrzebom ilo pami ci.
-- dodawanie macierzy kwadratowych
-- o dowolnym rozmiarze
with ada.text_io,ada.float_text_io,ada.integer_text_io;
use ada.text_io,ada.float_text_io,ada.integer_text_io;
procedure pr40 is
n: integer;
begin
put_line("Program wykonuje dodawanie macierzy kwadratowych. ");
put("Podaj rozmiar macierzy> ");
get(n);
declare
type macierz_kwadratowa is array(1..n,1..n) of float;
t1,t2,tw: macierz_kwadratowa;
k: positive_count;
81
begin
-- wczytywanie tablic
put_line("Podaj wyrazy pierwszej macierzy: ");
for i in t1'range(1) loop
for j in t1'range(2) loop
put('[');put(i,0);put(',');put(j,0);put("]=");
get(t1(i,j));
end loop;
end loop;
put_line("Podaj wyrazy drugiej macierzy: ");
for i in t2'range(1) loop
for j in t2'range(2) loop
put('[');put(i,0);put(',');put(j,0);put("]=");
get(t2(i,j));
end loop;
end loop;
-- obliczanie sumy
for i in tw'range(1) loop
for j in tw'range(2) loop
tw(i,j):=t1(i,j)+t2(i,j);
end loop;
end loop;
-- wypisywanie wyniku
-- w wypadku duzych rozmiarow
-- moze byc niezbyt udane
new_line(2);
for i in t1'range(1) loop
k:=1; set_col(k);
for j in t1'range(2) loop
put(t1(i,j),aft=>2,exp=>0);k:=k+10; set_col(k);
end loop;
end loop;
new_line; set_col(k/2);put('+');new_line;
for i in t1'range(1) loop
k:=1; set_col(k);
for j in t2'range(2) loop
put(t2(i,j),aft=>2,exp=>0);k:=k+10; set_col(k);
end loop;
end loop;
new_line;set_col(k/2);put('=');new_line;
for i in t1'range(1) loop
k:=1; set_col(k);
for j in tw'range(2) loop
put(tw(i,j),aft=>2,exp=>0);k:=k+10; set_col(k);
end loop;
end loop;
end;
-- koniec bloku
end pr40;
Typy tablicowe bez oke lenia rozmiaru
Istnienie typów tablicowych bez okre lenia rozmiaru (ang. unconstrained array types) umo liwia
tworzenie tablic nale cych do jednego typu, lecz maj cych ró ne rozmiary i zakresy indeksów. Typ
taki definiujemy nast puj co:
type nazwa_typu is array (typ_indeksu range <>) of typ_elementów;
np.
type sznur_cyfr is array (integer range <>) of integer;
Jak wida , w deklaracji tej w miejscu okre lenia zakresu indeksu znajduje si symbol <> (ang. box),
oznaczaj cy brak okre lenia zakresu indeksu. Zdefiniowany jest natomiast typ indeksu i typ elementów
81
nale cych do tej tablicy. Rozmiar tablicy (i zakres indeksu dla danej tablicy) okre lamy dopiero w
momencie deklarowania zmiennej nale cej do danego typu:
sznur10: sznur_cyfr(1..10);
sznur6: sznur_cyfr(0..5);
sznur2: sznur_cyfr(-3..-2);
Oczywi cie podawane granice zakresu indeksu nie mog przekracza granic zakresu typu indeksu.
Ka dy z zadeklarowanych powy ej "sznurów" jest tablic o innej długo ci, innym zakresie i warto ci
indeksu, nale y jednak do tego samego typu - sznur_cyfr. Typ tablicowy bez okre lenia rozmiaru
umo liwia równie tworzenie podtypów:
type sznur_liczb_calkowitych is array (integer range <>) of integer;
subtype sznurek is sznur_liczb_calkowitych (1..10);
s1,s2:sznurek;
Indeks ka dej z tablic s1, s2 przyjmuje warto ci od 1 do 10. Zauwa my, e kolejny typ tablicowy - ju
o okre lonym rozmiarze - jest tutaj zdefiniowany jako podtyp typu o rozmiarze nie okre lonym. w
poprzednim przykładzie - gdy deklarowali my tablice pisz c np.
s1: sznur_cyfr(1..10);
deklarowali my w zasadzie anonimowy typ tablicowy b d cy podtypem typu sznur_cyfr.
Porównajmy oba sposoby deklarowania tablic oraz u yteczno typu tablicowego bez okre lenia
rozmiaru:
with ada.text_io;
use ada.text_io;
procedure pr41_1 is
type tab1 is array (integer range 1..10) of integer;
type tab2 is array (integer range 1..5) of integer;
t1:tab1:=(others=>0);
t2:tab2:=(others=>2);
begin
t1(1):=t2(1);
-- t1(1..3):=t2(1..3);
-- niewykonalne
-- niewykonalne
-- if t1(1..5)=t2(1..5) then
-put("poczatkowe kawalki tablic sa rowne");
-- end if;
-- niewykonalne
-- if tab1=tab2 then
-put("cale tablice tez sa rowne");
-- end if;
-- niewykonalne
-- if t1(1..5)>t2(1..5) then
-put("poczatkowy kawalek pierwszej tablicy jest wiekszy");
-- end if;
-- niewykonalne
-- if tab1>tab2 then
-put("pierwsza tablica jest wieksza");
-- end if;
end pr41_1;
with ada.text_io;
use ada.text_io;
81
procedure pr41_2 is
type typ1 is array (integer range <>) of integer;
subtype tab1 is typ1(1..10);
subtype tab2 is typ1(1..5);
t1:tab1:=(others=>0);
t2:tab2:=(others=>2);
begin
t1(1):=t2(1);
t1(1..3):=t2(1..3);
----
if t1(1..5)=t2(1..5) then
put("poczatkowe kawalki tablic sa rowne");
end if;
-- niewykonalne
if tab1=tab2 then
put("cale tablice sa rowne");
end if;
----
if t1(1..5)>t2(1..5) then
put("poczatkowy kawalek pierwszej tablicy jest wiekszy");
end if;
-- niewykonalne
if tab1>tab2 then
put("cale - pierwsza wieksza");
end if;
end pr41_2;
Zmienne i stałe nale ce do typu tablicowego bez okre lenia rozmiaru mo emy deklarowa równie
ograniczaj c zakres indeksu niejawnie - poprzez nadanie tablicy warto ci pocz tkowej przy u yciu
agregatu:
type tab1 is array (positive range <>) of integer;
t1:tab1 := (4,2,0);
type tab2 is array (integer range <>) of integer;
t2:tab2 := (29.30,30);
Indeks tablicy t1 przyjmuje warto ci od 1 do 3, za tablicy t2 - od integer'first do
(integer'first+2). Jak wida , jako pocz tek zakresu indeksu przyjmowana jest najmniejsza
warto w typie danych wyspecyfikowanym jako typ indeksu. Mo na tego unikn stosuj c w agregacie
tablicy notacj nazywan :
t2: tab2 := (1=>29, 2=>30, 3=>30);
Zakres indeksu przyjmuje wówczas
dan warto
(tutaj - od 1 do 3).
Jak zauwa yli my poprzednio, ka da z tablic nale ca do typu tablicowego bez okre lenia rozmiaru
nale y wła ciwie do pewnego jego podtypu okre lonego przez ograniczenie zakresu indeksu.
Przypisuj c tablicy warto przy u yciu agregatu mo emy mie do czynienia z niejawn konwersj
tego agregatu do odpowiedniego podtypu. Zjawisko to nosi nazw przesuwania (ang. sliding). Ma
miejsce tylko w niektórych przypadkach, nie wyst puje na przykład przy agregatach zawieraj cych
słowo kluczowe others:
with ada.text_io,ada.integer_text_io;
use ada.text_io,ada.integer_text_io;
procedure pr42 is
type tab is array (positive range <>) of integer;
t: tab(1..5);
81
begin
t:=(10..11=>1, 12..14=>4);
-- agregat przesuwa sie tak, ze t(1) i t(2) maja wartosc 1
-- pozostale - wartosc 4
for i in 1..5 loop put(t(i),0);put(' ');end loop; new_line;
t:=(5..6=>9, others=>0);
-- nie ma przesuniecia - t(5) ma wartosc 9
-- pozostale - wartosc 0
for i in 1..5 loop put(t(i),0);put(' ');end loop; new_line;
t:=(3..5=>9, others=>0);
-- nie ma przesuniecia - t(3) do t(5) maja wartosc 9
-- pozostale - wartosc 0
for i in 1..5 loop put(t(i),0);put(' ');end loop; new_line;
t:=(7..9=>0, others=>4);
-- nie ma przesuniecia - wszystkie skladowe tablicy maja
-- wartosc 4
for i in 1..5 loop put(t(i),0);put(' ');end loop; new_line;
t:=(8,8,8,others=>0);
-- nie ma przesuniecia - t(1) do t(3) maja wartosc 8,
-- pozostale - wartosc 0
for i in 1..5 loop put(t(i),0);put(' ');end loop; new_line;
end pr42;
Tablice nie maj ce okre lenia rozmiaru mog by zarówno jedno-, jak i wielowymiarowe. w przypadku
tablic wielowymiarowych brak okre lenia zakresu indeksu wyst powa musi we wszystkich
wymiarach. Oto przykład prawidłowego u ycia typu nie majacego okre lenia rozmiaru:
type macierz is array (positive range <>,
positive range <>) of float;
m2: macierz(1..2,1..2);
subtype macierz_3_na_3 is macierz(1..3,1..3);
m3_1, m3_2: macierz_3_na_3;
i przykłady nieprawidłowych definicji typów:
type inna_macierz is array (positive range <>,1..3) of float;
type inna_macierz1 is array (1..5,positive range <>) of float;
Warto wiedzie , e typy string i wide_string s zdefiniowane jako typy tablicowe nie maj ce
okre lenia rozmiaru:
-- zdefiniowane w pakiecie Standard
type String is array (Positive range <>) of Character;
type Wide_String is array (Positive range <>) of Wide_Character;
Operacje na tablicach
Dla tablic okre lone s nast puj ce operacje:
• Atrybuty 'first, 'last, 'length i 'range (opisane w poprzednich rozdziałach). w
przypadku typów tablicowych bez okreslenia rozmiaru zwracaj odpowiednie warto ci indeksu
podtypu typu rodzicielskiego.
• Operacje logiczne (not, and, or, xor), wykonywalne jedynie dla jednowymiarowych tablic
zło onych z elementów typu boolean; tablice te musz by tego samego typu i tej samej długo ci
(zob. program pr38).
• Konkatenacja (&) okre lona dla jednowymiarowych tablic tego samego typu.
81
•
"Wycinanie" fragmentów z tablicy (ang. array slicing) okre lone dla tablic jednowymiarowych
type tablica is array(integer range 1..10) of integer;
t1:tablica:=(others=>6);
t2:tablica:=(others=>8);
t1(1..3):=t1(2..4);
put(t1(2..8));
•
•
Operacja przypisania (:=) - przypisa sobie mo na tablice tego samego typu i rozmiaru
Konwersja typów. Tablica mo e by przekonwertowana do tablicy innego typu tylko wówczas, gdy
obie maj ten sam rozmiar, typ składowych oraz te same lub konwertowalne odpowiednie typy
indeksów.
type
type
t1 :
t2 :
typ1 is array (positive range 1..10) of integer;
typ2 is array (integer range 1..10) of integer;
typ1 := (others=>0);
typ2;
t2:=t1;
t2:=typ2(t1);
t1:=typ1(t2);
•
--tu "wycinamy" fragmenty
-- niewykonalne - niezgodno
typów
-- wykonalne dzieki konwersji
-- wykonalne dzieki konwersji
Relacje <, <=, >, >= - wykonalne dla jednowymiarowych tablic o elementach typu dyskretnego.
Tablice musz by tego samego typu. Porównywanie odbywa si element po elemencie, od lewej
do prawej, do momentu wykrycia składowych o ró nych warto ciach lub do wyczerpania sie
elementów w której tablicy. Je eli napotkana została ró ni ca tablice składowa, za "wi ksz "
uznawana jest ta z tablic, w ktorej wymieniona wy ej składowa ma wi ksz warto . Je eli nie
wykryto ró nic, natomiast składowe jednej z tablic zostały wyczerpane, jako "wi ksza"
przyjmowana jest tablica dłu sza.
type typ1 is array (positive range 1..3) of integer;
t1,t11 : typ1;
t1 := (1,2,4);
t11 := (1,2,0);
put( t1>t11 );
put( t1(1..2) > t1(1..3));
•
•
-- wypisze TRUE
-- wypisze FALSE
Test równo ci (=, /=). Mo na porównywa tablice tego samego typu. Dwie tablice s równe, gdy
maj t sam ilo składowych, a odpowiednie składowe obu tablic s jednakowe.
Test przynale no ci tablicy do podtypu (in). Testowane w ten sposób tablica i podtyp musz
nale e do tego samego typu bazowego. Wynik jest typu boolean - true, gdy testowana tablica
ma dla ma dego z wymiarów taki sam zakres indeksu jak podtyp. do którego przynale no
testujemy, false w przeciwnym przypadku.
type macierz is array (positive range <>,
positive range <>) of float;
subtype macierz_3_na_3 is macierz(1..3,1..3);
subtype macierz_2_na_2 is macierz(1..2,1..2);
subtype tez_macierz_2_na_2 is macierz(2..4,1..3);
m2: macierz(1..2,1..2);
m3_1, m3_2: macierz_3_na_3;
put(m2 in macierz_2_na_2);
put(m3_1 in macierz_2_na_2);
put(m2 in tez_macierz_2_na_2);
-- wypisze TRUE
-- wypisze FALSE
-- wypisze FALSE
Rekordy
Poznana w poprzednich rozdziałach zło ona struktura danych - tablica - składała si z elementów
jednego typu. Mo liwe jest równie tworzenie struktur, w których typ ka dej ze składowych mo e by
81
inny. Tak zło on struktur danych jest rekord. Podobnie jak w przypadku tablic, do rekordu
(przechowuj cego np. imi i nazwisko osoby - dane typu ła cuchowego, jej rok urodzenia i wzrost dane typu positive itp) mo emy zarównoodwoływa si za pomoc pojedynczego identyfikatora,
jak i działa na poszczególnych jego składnikach. Kolejn analogi s agregaty rekordów, które podobnie jak w przypadku tablic - pozwalaj na przypisanie warto ci równocze nie wszystkim
składowym (tu zwanym polami). Tak e i tu mo na - jak w tablicach stosowa notacj pozycyjn lub
nazywan .
Rekordy "zwykłe" (bez wyró ników)
Typ rekordowy definiujemy nast puj co:
type nazwa_typu_rekordowego is record
pole_1 : typ_pola_1 ;
...
pole_n : typ_pola_n ;
end record;
Pola jednego typu mo emy zadeklarowa równocze nie, oddzielaj c ich nazwy przecinkami, np.
type punkt_plaszczyzny is record
x,y : float;
end record;
type osoba is record
imie: string (1..15);
nazwisko: string(1..30);
rok_urodzenia: positive;
wzrost: positive;
stanu_wolnego: boolean;
end record;
Zmienne danego typu deklarujemy w zwykly sposób:
student : osoba;
punkt_A : punkt_plaszczyzny;
za warto ci przypisujemy im b d u ywaj c kolejno poszczególnych pól rekordu - do których
odwołujemy si poprzez nazw zmiennej rekordowej i oddzielon od niej kropk nazw pola rekordu
(student.imie, punkt_A.x) - jak w poni szym przykładzie,
student.imie:="Grzegorz
";
student.nazwisko:="Brzeczyszczykiewicz
student.rok_urodzenia:=1974;
wzrost:=201;
stanu_wolnego:=true;
";
punkt_A.x:=2.0;
punkt_A.y:=-1.1;
b d za pomoc agregatu rekordu, w którym mo emy zastosowa notacj pozycyjn (warto ci
poszczególnych pól wpisujemy w kolejno ci wyst powania tych pól w definicji typu rekordowego)
punkt_A:=(2.0,-1.1);
student:=("Grzegorz
201, true);
", "Brzeczyszczykiewicz
lub notacj nazywan :
punkt_A:=(x=>2.0, y=>-1.1);
student:=
81
", 1974,
(imie=>"Grzegorz
", nazwisko=>"Brzeczyszczykiewicz
wzrost=> 201, rok_urodzenia=>1974, stanu_wolnego=>true);
Wówczas, jak wida , kolejno
",
składników nie ma znaczenia.
Podobnie jak w przypadku agregatów tablic, w niektórych sytuacjach mo na w agregatach rekordow
u ywa znaku | oraz słowa others . Oczywi cie odnosi si one mog tylko do komponentów
jednego typu:
punkt_A:=(others=>0.0);
punkt_A:=(x|y=>0.0);
Poprawne s równie agregaty rekordów u yte w poni szym przykładzie:
type miasto is record
wojewodztwo:string(1..2);
odleglosc_od_Warszawy,
odleglosc_od_stolicy_wojewodztwa:natural;
ilosc_mieszkancow_w_tys:natural;
end record;
type miasto1 is record
wojewodztwo:string(1..2);
nadawana_rejestracja:string(1..2);
odleglosc_od_Warszawy, odleglosc_od_stolicy_wojewodztwa,
il_mieszkancow_w_tys:natural;
end record;
m1,m2:miasto;
m:miasto1;
...
-- przyklady dla typu miasto
m1:=(wojewodztwo=>"WA",others=>50);
m1:=(wojewodztwo=>"WR", odleglosc_od_stolicy_wojewodztwa=>10,
others=>300);
m1:=(wojewodztwo=>"RA",
odleglosc_od_Warszawy|odleglosc_od_stolicy_wojewodztwa=>80,
others=>20);
m1:=("WA",10,others=>20);
-- przyklady dla typu miasto1
m:=(wojewodztwo|nadawana_rejestracja=>"PL", others=>40);
m:=(odleglosc_od_Warszawy|odleglosc_od_stolicy_wojewodztwa=>0,
il_mieszkancow_w_tys=>10,others=>"WA");
Jak wida słowo others wyst puje zawsze na ko cu agregatu rekordu.
W agregatach rekordów, inaczej ni w przypadku tablic, niedozwolone jest natomiast u ywanie
zakresów (..). Tak wi c
punkt_A:=(1..2=>3.0);
jest zapisem nieprawidłowym.
Mo liwe jest ł czenie w jednym agregacie notacji pozycyjnej i nazywanej (przy czym wyst powa one
musz w tej wła nie kolejno ci):
type xxx is record
x,y,z:integer;
end record;
81
a:xxx;
a:=(1,2,z=>3);
a:=(x=>1,2,3);
-- zapis prawidłowy
-- bł d
Nadawanie warto ci zmiennym rekordowym mo e odbywa si równie w inny sposób. Otó typom
rekordowym (jako jedynym w Adzie95) mo na nada warto
pocz tkow ju w momencie
definiowania tych typów:
type punkt_ekranu is record
x : natural range 0..80 := 0;
y : natural range 0..120 := 0;
end record;
type osoba is record
imie,nazwisko : string(1..20) := (others=>' ');
wiek : integer := 0;
end record;
(mo liwe jest równie nadanie warto ci pocz tkowych tylko niektórym polom rekordu):
type pracownik is record
imie, nazwisko : string(1..20) := (others=>' ');
staz_pracy : integer;
end record;
I tak na przykład ka da z deklarowanych zmiennych typu punkt_ekranu b dzie miała składowe
o warto ci 0, chyba e postanowimy inaczej:
p : punkt_ekranu;
q : punkt_ekranu := (1,2);
-- m.x i m.y maj
-- m.x=1, m.y=2
warto
0
Nale y pami ta , e zmienne (dowolnego typu), którym nie została nadana warto pocz tkowa,
posiadaj ju w chwili rozpocz cia wykonywania programu pewn warto , stanowi c interpretacj
zawarto ci komórek pami ci przeznaczonych do przechowywania tej zmiennej. Nieznajomo tego
faktu mo e by przyczyn pisania programów daj cych bardzo dziwne wyniki...
Predefiniowanymi operacjami okre lonymi dla typów rekordowych s podstawienie (:=)
i porównywanie (=, /=). Oczywi cie operacje te mo emy wykonywa jedynie na elementach
nale cych do tego samego typu.
Dla zmiennych p1, p2 typu punkt_ekranu podstawienie
p1:=p2;
jest odpowiednikiem wykonania kolejno instrukcji
p1.x:=p2.x;
p1.y:=p2.y;
Podczas operacji porównywania rekordów porównywane s ich kolejne pola.
Oprócz deklarowania zmiennych mo emy deklarowa stałe nale ce do danego typu rekordowego.
Czynimy to w standardowy sposób:
type punkt_ekranu is record
x: natural range 0..80:=0;
y: natural range 0..120:=0;
end record;
type punkt_plaszczyzny is record
x,y:float;
end record;
81
gorny_rog_ekranu : constant punkt_ekranu := (0,0);
poczatek_ukladu_wspolrzednych : constant punkt_plaszczyzny
:=(0.0, 0.0);
(zauwa my, e warto stałej nale y poda niezale nie od tego, czy okre lili my warto pocz tkow
dla danego typu).
W zadeklarowanych w ten sposób stałych typu rekordowego ka da ze składowych jest traktowana jako
stała:
gorny_rog_ekranu.x := 3;
-- bł d, x jest stał
Niemo liwe jest natomiast zadeklarowanie jako stałych poszczególnych składowych rekordu:
type jakis_typ is record
a: constant integer := 3;
b: float;
end record;
-- niedozwolone
(zobacz jednak podrozdział Rekordy z wyró nikami).
A oto praktyczny przykład wykorzystania typów rekordowych:
-- dzialania na liczbach zespolonych
with ada.text_io,ada.float_text_io;
use ada.text_io,ada.float_text_io;
procedure pr43 is
type zespolona is record
re,im:float;
end record;
i: constant zespolona := (re=>0.0, im=>1.0);
a,b : zespolona;
sprzezenie_a,sprzezenie_b,suma,roznica,iloczyn,
iloraz :zespolona;
mian:float;
begin
put_line("Podaj liczbe zespolona a : ");
put("a.re : ");get(a.re);
put("a.im : ");get(a.im);
put_line("Podaj liczbe zespolona b : ");
put("b.re : ");get(b.re);
put("b.im : ");get(b.im);
suma:=(a.re+b.re,a.im+b.im);
roznica.re:=a.re-b.re;
roznica.im:=a.im-b.im;
iloczyn:=(re=>a.re*b.re-a.im*b.im, im=>a.re*b.im+a.im+b.re);
mian:=b.re**2+b.im**2;
iloraz.re:=(a.re*b.re+a.im*b.im)/mian;
iloraz.im:=(b.re*a.im-a.re*b.im)/mian;
sprzezenie_a:=(re=>a.re,im=>-a.im);
sprzezenie_b:=(b.re,-b.im);
81
new_line(2);
if a=b then
put_line("Liczby a i b sa rowne");
else
put_line ("Liczby a i b nie sa rowne");
end if;
new_line;
put_line("Liczby sprzezone do : ");
put("a : "); put(sprzezenie_a.re,aft=>2,exp=>0);
if sprzezenie_a.im>=0.0 then put(" +");
else put(" -");end if;
put(abs(sprzezenie_a.im),aft=>2,exp=>0);
put_line(" i");
put("b : "); put(sprzezenie_b.re,aft=>2,exp=>0);
if sprzezenie_b.im>=0.0 then put(" +");
else put(" -");end if;
put(abs(sprzezenie_b.im),aft=>2,exp=>0);
put_line(" i");
new_line;
put_line("Wyniki dzialan : ");
new_line;
put("a + b = ");put(suma.re,aft=>2,exp=>0);
if suma.im>=0.0 then put(" +");
else put(" -");end if;
put(abs(suma.im),aft=>2,exp=>0);put_line(" i");
put("a - b = ");put(roznica.re,aft=>2,exp=>0);
if roznica.im>=0.0 then put(" +");
else put(" -");end if;
put(abs(roznica.im),aft=>2,exp=>0);
put_line(" i");
put("a * b = ");put(iloczyn.re,aft=>2,exp=>0);
if iloczyn.im>=0.0 then put(" +");
else put(" -");end if;
put(abs(iloczyn.im),aft=>2,exp=>0);
put_line(" i");
put("a / b = ");put(iloraz.re,aft=>2,exp=>0);
if iloraz.im>=0.0 then put(" +");
else put(" -");end if;
put(abs(iloraz.im),aft=>2,exp=>0);
put_line(" i");
end
pr43;
Struktury danych "wielokrotnie zło one"
Nie ma wła ciwie adnych ogranicze w ł czeniu ze sob poznanych ju zło onych struktur danych tablic i rekordów. Mo emy zadeklarowa tablic zło on z tablic, rekord o polu b d cym innym
rekordem czy tablic , tablic zło on z rekordów... Ilustruj to poni sze przykłady. Zwró my uwag na
przedstawiony w nich sposób odwoływania si do elementów danej struktury:
-- przyklad rekordu, ktorego polem jest rekord
with ada.text_io,ada.integer_text_io;
use ada.text_io,ada.integer_text_io;
procedure pr44 is
81
type data is record
dzien: integer range 1..31;
miesiac: integer range 1..12;
rok: integer;
end record;
type osoba is record
imie:string(1..20):=(others=>' ');
nazwisko: string(1..30):=(others=>' ');
data_urodzenia:data;
end record;
ktos1, ktos2 : osoba;
rr,rm,rd,n:integer;
begin
put_line("Podaj dane dwoch osob:");
new_line;
put_line("OSOBA PIERWSZA");
put("Imie: "); get_line(ktos1.imie,n);
put("Nazwisko: "); get_line(ktos1.nazwisko,n);
put_line("Data urodzenia: ");
put("dzien: "); get(ktos1.data_urodzenia.dzien);
put("miesiac (liczba): ");get(ktos1.data_urodzenia.miesiac);
put("rok: "); get(ktos1.data_urodzenia.rok);
skip_line;
new_line(2);
put_line("OSOBA DRUGA");
put("Imie: "); get_line(ktos2.imie,n);
put("Nazwisko: "); get_line(ktos2.nazwisko,n);
put_line("Data urodzenia: ");
put("dzien: "); get(ktos2.data_urodzenia.dzien);
put("miesiac (liczba): ");get(ktos2.data_urodzenia.miesiac);
put("rok: "); get(ktos2.data_urodzenia.rok);
new_line(4);
put(ktos1.imie);set_col(40);put_line(ktos2.imie);
put(ktos1.nazwisko);set_col(40);put_line(ktos2.nazwisko);
rr:=ktos1.data_urodzenia.rok-ktos2.data_urodzenia.rok;
rm:=ktos1.data_urodzenia.miesiac-ktos2.data_urodzenia.miesiac;
rd:=ktos1.data_urodzenia.dzien-ktos2.data_urodzenia.dzien;
if
rr=0 and rm=0 and rd=0 then
put("urodzili sie tego samego roku, miesiaca i dnia");
else
if rr=0 then
put("urodzili sie w tym samym roku");
if rm=0 then
put(", miesiacu ");
if rd=0 then
put("i dniu");
elsif rd>0 then
put("i pierwsza z tych osob jest starsza o ");
put(rd,0);put(" dni");
else
put("i pierwsza z tych osob jest mlodsza o ");
put(abs(rd),0);put(" dni");
end if;
end if;
else
put("roznica rocznikow tych osob: ");put(abs(rr),0);
81
end if;
end if;
end pr44;
A oto przykład zastosowania tablicy, której składowymi s tablice innego typu:
-- przyklad tablicy tablic
with ada.text_io,ada.integer_text_io,ada.float_text_io;
use ada.text_io,ada.integer_text_io,ada.float_text_io;
procedure pr45 is
type odpowiedzi_do_pytania is array(1..4) of boolean;
type tablica_odpowiedzi is array(1..5) of odpowiedzi_do_pytania;
type pytanie_i_odpowiedzi is array (0..4) of string(1..100);
type tablica_pytan is array(1..5) of pytanie_i_odpowiedzi;
test
odpowiedzi
podane_odp
op
punkty
:
:
:
:
tablica_pytan;
tablica_odpowiedzi;
tablica_odpowiedzi:=(others=>(others=>false));
integer;
: float:=0.0;
begin
--nadanie pocz tkowych warto ci
--pytaniom i odpowiedziom
test:=(others=>(others=>(others=>' ')));
test(1)(0)(1..33):="Swiatla drogowe moga byc uzywane:";
test(1)(1)(1..25):="poza obszarem zabudowanym";
test(1)(2)(1..22):="w obszarze zabudowanym";
test(1)(3)(1..53):="poza obszarem zabudowanym, na drogach "&
"szybkiego ruchu";
test(1)(4)(1..85):="w obszarze zabudowanym, lecz tylko na "&
"drogach o dopuszczalnej " &
" predk. powyzej 60 km/h";
odpowiedzi(1):=(true,false,false,false);
test(2)(0)(1..91):="Jadac pasem ruchu dla pojazdow powolnych "&
"i zblizajac sie do jego konca, kierujacy"&
" pojazdem";
test(2)(1)(1..53):="powinien zmienic pas zachowujac szczegolna"&
" ostroznosc";
test(2)(2)(1..52):="posiada pierwszenstwo przed jadacymi pasem"&
" sasiednim";
test(2)(3)(1..35):="powinien wlaczyc lewy kierunkowskaz";
test(2)(4)(1..44):="zmieniajac pas jest wlaczajacym sie do "&
"ruchu";
odpowiedzi(2):=(true,false,true,true);
test(3)(0)(1..28):="Wyprzedzanie jest zabronione";
test(3)(1)(1..41):="bezposrednio przed przejazdami kolejowymi";
test(3)(2)(1..40):="na przejazdach kolejowych i tramwajowych";
test(3)(3)(1..43):="bezposrednio przed przejsciami dla pieszych";
test(3)(4)(1..27):="na przejsciach dla pieszych";
odpowiedzi(3):=(true,true,true,true);
81
test(4)(0)(1..43):="W ktorych miejscach cofanie jest dozwolone:";
test(4)(1)(1..26):="na drodze jednokierunkowej";
test(4)(2)(1..21):="na drodze ekspresowej";
test(4)(3)(1..24):="na jezdni dwukierunkowej";
test(4)(4)(1..15):="na autostradzie";
odpowiedzi(4):=(true,false,true,false);
test(5)(0)(1..69):="Wjazd na skrzyzowanie jest zabroniony, " &
"jezeli wyswietlany jest sygnal";
test(5)(1)(1..8):="czerwony";
test(5)(2)(1..14):="zolty migajacy";
test(5)(3)(1..12):="zolty ciagly";
test(5)(4)(1..81):="zielony, ale warunki ruchu uniemozliwiaja"&
" dalsza jazde i opuszczenie skrzyzowania";
odpowiedzi(5):=(true,false,true,true);
--test
put_line("
------ TEST - PRAWO JAZDY ----- ");
new_line;
put_line("Za kazda prawidlowa odpowiedz czastkowa dostajesz"&
" 0.25 pkt. ");
put_line("Jesli poprawnie odpowiesz na cale pytanie, dostajesz"&
" dodatkowo 1 pkt.");
new_line;
set_col(45);
put_line("Powodzenia!");
new_line(2);
for i in test'range loop
for j in 0..4 loop
if j/=0 then put(j,0);put(')');end if;
put_line(test(i)(j));
end loop;
new_line;
put_line("podaj numery poprawnych odpowiedzi," &
" 0 zakancza podawanie");
for k in podane_odp(2)'range loop
--1..4 (ilosc odp. w pytaniu)
put("> "); get(op);
exit when op=0;
podane_odp(i)(op):=true;
end loop;
new_line;
end loop;
-- zliczanie punktow
for i in odpowiedzi'range loop
for j in odpowiedzi(2)'range loop
if odpowiedzi(i)(j)=podane_odp(i)(j) then
punkty:=punkty+0.25; end if;
end loop;
if odpowiedzi(i)=podane_odp(i) then
punkty:=punkty+1.0;
end if;
end loop;
81
-- wyniki
put("Uzyskales ");put(punkty,exp=>0,aft=>1);put(" punktow");
end pr45;
A teraz tablica, której elementami s rekordy. Jednym z pól rekordu jest tablica:
-- tablica rekordow
with ada.text_io,ada.float_text_io,ada.integer_text_io;
use ada.text_io,ada.float_text_io,ada.integer_text_io;
procedure pr46 is
type przedmioty is (polski, niemiecki, angielski, matematyka, fizyka,
chemia, biologia, geografia, historia, wf);
package przedmioty_io is new enumeration_io(przedmioty);
subtype ocena is integer range 1..6;
type tabela_ocen is array (przedmioty) of ocena;
type uczen is record
imie
: string(1..20)
nazwisko : string(1..30)
oceny
: tabela_ocen;
srednia : float;
end record;
:= (others=>' ');
:= (others=>' ');
klasa
: array(1..3) of uczen;
s, sr_klasy : float;
n
: integer;
begin
-- wprowadzanie danych uczniow
sr_klasy:=0.0;
for i in klasa'range loop
put_line("Uczen nr " & integer'image(i));
put("Imie:
");get_line(klasa(i).imie,n);
put("Nazwisko: ");get_line(klasa(i).nazwisko,n);
s:=0.0;
for j in przedmioty loop
przedmioty_io.put(j);put(" ");
get(klasa(i).oceny(j));
skip_line;
s:=s+float(klasa(i).oceny(j));
end loop;
klasa(i).srednia:=s/float(przedmioty'pos(przedmioty'last)+1);
sr_klasy:=sr_klasy+klasa(i).srednia;
new_line;
end loop;
sr_klasy:=sr_klasy/float(klasa'length);
for i in klasa'range loop
put(klasa(i).imie);put(klasa(i).nazwisko);
put("Srednia ocen: "); put(klasa(i).srednia,exp=>0); new_line;
81
end loop;
new_line(2);
put("Srednia ocen klasy: "); put(sr_klasy,aft=>2,exp=>0);
end pr46;
Mo liwe jest skrócenie sposobu odwołania do składowej struktury zło onej poprzez jej
przemianowanie (ang. renaming). Robimy to w cz ci deklaracyjnej programu pisz c na przykład:
rok1:integer renames ktos1.data_urodzenia.rok;
(deklarujemy now zmienn wi c j ze składow rekordu - oczywi cie musz by one tego samego
typu). Odt d do roku urodzenia osoby ktos1 mamy dost p zarówno przez pełn jej nazw
(ktos1.data_urodzenia.rok), jak i przez zmienn rok1.
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
when warto
1 | warto 2 | ... => pole : typ_pola;
1 .. warto 2 => pole : typ_pola;
a tak e
when others =>
pole : typ_pola;
81
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);
sasiad
: osoba (kierowca => true);
-- notacja pozycyjna
-- 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
W ten sposób równie otrzymujemy rekord o stałej warto ci wyró nika.
81
", 1955);
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));
81
",
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
81
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;
81
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 :
81
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
przypadku rekordów z wariantami), b d te
nazwy okre lili my na zawsze (podobnie jak w
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
Napisanie na przykład
wyst powa
w deklaracji typu jako elementy jakiego wyra enia.
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,
81
− "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);
obszar_zabudowany.max_predkosc:=50;
put (obszar_zabudowany.max_predkosc);
Jak wida , warto
"okre lony").
wyró nika mo na tutaj zmienia
-- wypisze 60
-- wypisze 50
(nawet je li zadeklarowali my rekord jako
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 ................................................................................................................................... 7
Funkcje matematyczne........................................................................................................................ 11
Typy liczbowe i ich zakresy ................................................................................................................... 14
Instrukcja warunkowa............................................................................................................................. 16
Operatory logiczne. Kolejno działa ................................................................................................... 18
Typ boolean............................................................................................................................................ 19
Typy znakowe......................................................................................................................................... 21
Typ wyliczeniowy................................................................................................................................... 22
Podtypy................................................................................................................................................... 24
81
Definiowanie nowych typów liczbowych ............................................................................................... 27
Atrybuty typów....................................................................................................................................... 28
Instrukcja wyboru ................................................................................................................................... 32
Instrukcje p tli ........................................................................................................................................ 35
Typ ła cuchowy...................................................................................................................................... 43
Tablice.................................................................................................................................................... 49
Tablice jednowymiarowe.................................................................................................................... 49
Tablice wielowymiarowe.................................................................................................................... 55
Tablice anonimowe (anonimowy typ tablicowy) ................................................................................ 58
Tablice dynamiczne ............................................................................................................................ 58
Typy tablicowe bez oke lenia rozmiaru.............................................................................................. 60
Operacje na tablicach.......................................................................................................................... 63
Rekordy .................................................................................................................................................. 64
Rekordy "zwykłe" (bez wyró ników) ................................................................................................. 64
Struktury danych "wielokrotnie zło one" ............................................................................................... 69
Rekordy z wyró nikami.......................................................................................................................... 74
Rekordy z wariantami ......................................................................................................................... 74
Rekordy ze składowymi o zmiennym rozmiarze ................................................................................ 79
Inne zastosowania wyró ników. ......................................................................................................... 80
81