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