background image

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. 

podaj współczynniki trójmianu a,b,c  

2. 

sprawdź, czy a /= 0. JeŜeli a = 0, to stwierdzamy, Ŝe to nie jest trójmian i przechodzimy 
do punktu 7 

3. 

oblicz wyróŜnik trójmianu delta = b^2-4ac 

4. 

jeŜeli  delta < 0,  to  równanie  nie  ma  rozwiązania  w  zbiorze  liczb  rzeczywistych, 
przechodzimy do punktu 7 

5. 

jeŜeli  delta = 0,  to  równanie  ma  jeden  pierwiastek  podwójny  x = -b/(2a),  przechodzimy 
do punktu 7 

6. 

jeŜeli  delta > 0,  to  równanie  ma  dwa  pierwiastki  x1 = (-b - pierwiastek(delta)) / (2a), 
x2 = (-b + pierwiastek(delta)) / (2a),  przechodzimy do punktu 7 

7. 

zakończenie 

 

albo znajdowanie najmniejszej liczby w ciągu n-elementowym 

 
1. 

podaj n oraz a1, a2, ... , an 

2. 

min := a1 

3. 

m := 2 

4. 

porównaj am z min. JeŜeli am  <  min to min := am 

5. 

m := m+1 

6. 

jeŜeli m <= n, to idź do punktu 4 

7. 

wypisz min 

8. 

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. 

 

background image

81 

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 

 

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 

background image

81 

 

with ada.text_io; 
 
procedure pr1 is 
 
begin 
  text_io.put("   Dzien dobry!!!   "); 
end pr1; 
 

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. beginend, 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!!! 

background image

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 : "); 

background image

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 

background image

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 

background image

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 )     dla pewnego N całkowitego 
 

(  |A mod B| < |B|,  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              j          i  rem j    i  mod 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 

background image

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_Ŝądany wartość_zamieniana ) 
 

np.  

integer (5.0)  

-- daje 5 

float (5) 

 

-- daje 5.0 

integer (4.1)  

-- daje 4 

integer(4.6)   

-- daje 5 

 

background image

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Ŝ  liczby  są  wyprowadzane  nie  w  postaci 
wykładniczej, lecz w "zwykłej", jako ułamek dziesiętny. 
 

Jak  sama  nazwa  wskazuje,  zmienna  moŜe  zmieniać  swoją  wartość  w  trakcie  wykonywania 

programu. Konkretną wartość moŜemy jej nadać wykonując instrukcję  

 
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); 

background image

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ść_stałej ; 
 

np. 

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 

background image

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 

background image

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?..."); 

background image

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; 

background image

81 

use ada.text_io; 
 
procedure pr12 is 
 
   package flo_io is new ada.text_io.float_io(long_float); 
   package fl_io is new ada.text_io.float_io(float); 
   package floa_io is new ada.text_io.float_io(long_long_float); 
   package f_io is new ada.text_io.float_io(short_float); 
   package int_io is new ada.text_io.integer_io(integer); 
   package inte_io is new ada.text_io.integer_io(long_integer); 
   package 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);   

background image

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 

background image

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; 

background image

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");          

background image

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: 
 
** 

abs  not 

*    / 

rem mod 

 

 

(jednoargumentowe) 

+  

 

 

(dwuargumentowe) 

/= 

<= 

>= 

and  or 

xor 

 

background image

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("       TABELKA WARTOSCI LOGICZNYCH"); 
  new_line(3); 
  put(" |   p   |   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); 

background image

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 

 

 

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 

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                  : constant Character := Character'Val (0); 
   SOH                  : constant Character := Character'Val (1); 

 p 

not p  

p and q  

p or q  

p xor q  

TRUE 

TRUE 

FALSE 

TRUE 

TRUE 

FALSE 

 TRUE 

FALSE 

FALSE 

FALSE 

 TRUE| 

TRUE 

FALSE 

TRUE 

 TRUE 

FALSE 

 TRUE 

 TRUE| 

FALSE 

FALSE 

TRUE 

FALSE 

FALSE 

FALSE 

background image

81 

   STX                  : constant Character := Character'Val (2); 
   ETX                  : constant Character := Character'Val (3); 
   EOT                  : constant Character := Character'Val (4); 
   ENQ                  : constant Character := Character'Val (5); 
   ACK                  : constant Character := Character'Val (6); 
   BEL                  : constant Character := Character'Val (7); 
   BS                   : constant Character := Character'Val (8); 
   HT                   : constant Character := Character'Val (9); 
   LF                   : constant Character := Character'Val (10); 
   VT                   : constant Character := Character'Val (11); 
   FF                   : constant Character := Character'Val (12); 
   CR                   : constant Character := Character'Val (13); 
   SO                   : constant Character := Character'Val (14); 
   SI                   : 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'); 
   

background image

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; 
 

background image

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; 

background image

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ć  wartości  typu  integer,  ale  z  określonego 
przedziału - od 0 do 18. Napisanie np. 
 

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 

 

background image

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 "); 

background image

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 

 

7650000.0 

 

Oto przykłady definicji typów liczbowych: 
 

type male_calkowite is range -10 .. 10; 

background image

81 

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; 

 

-- wystapi blad  

   

 

 

 

 

 

-- pomylilismy _wz i _wi 

  wz_io.put(sr_wz); 
  new_line; 
  put("sredni wiek    : "); 
  sr_wi:=(osoba1_wz+osoba2_wi)/2; 

-- wystapi blad - jw. 

  wi_io.put(sr_wi); 

background image

81 

  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) : "); 

background image

81 

  put(dni'pred(wt)); 

 

 

-- wypisze PON 

  set_col(40); 
  put("dni'succ(wt) : "); 
  put(dni'succ(wt)); 

 

 

-- wypisze SR 

  new_line; 
  put("dni robocze'pred(pt) : "); 
  put(dni_robocze'pred(pt)); 

 

-- wypisze CZW 

  set_col(40); 
  put("dni robocze'pred(nie) : "); 
  put(dni_robocze'pred(nie));   

-- wypisze SO 

  new_line; 
  put("do_stu'pred(10) : "); 

 

 

 

  put(do_stu'pred(10),0);  

 

-- wypisze 9 

  set_col(40); 
  put("integer'pred(x) (x: do_stu := 0) : "); 
  put(integer'pred(x),0);  

 

-- wypisze -1 

  new_line; 
  put("dni'robocze'succ(pt) : "); 
  put(dni_robocze'succ(pt)); 

 

-- wypisze SO 

  set_col(40); 
  put("do_stu'pred(x) (x: do_stu := 0) : "); 
  put(do_stu'pred(x)); 

 

 

-- wypisze -1 

  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); 
 
  -- atrybuty POS i VAL 
 
  put("dni'pos(pon) : ");   
  put(dni'pos(pon),0); 

 

 

-- wypisze 0 

  set_col(40); 
  put("dni'val(0) : "); 
  put(dni'val(0),0); 

 

 

-- wypisze PON 

  new_line; 
  put("srodek_tygodnia'pos(wt) : "); 
  put(srodek_tygodnia'pos(wt),0); 

-- wypisze 1 

  set_col(40); 
  put("srodek_tygodnia'val(1) : "); 
  put(srodek_tygodnia'val(1));   

-- wypisze WT 

  new_line; 
  put("srodek_tygodnia'val(0) : "); 
  put(srodek_tygodnia'val(0));   

-- wypisze PON 

  set_col(40); 
  put("integer'pos(-7) : "); 
  put(integer'pos(-7),0);  

 

-- wypisze -7 

  new_line(2); 
   
  -- atrybuty IMAGE i VALUE 
   
  put("integer'image(12) : "); 
  put(integer'image(12));  

 

-- wypisze 12 (tekst) 

  set_col(40); 
  put("do_stu'image(10) : "); 
  put(do_stu'image(10));   

 

-- wypisze 10 (tekst) 

  new_line; 
  put("dni'image(wt) : "); 
  put(dni'image(wt)); 

 

 

-- wypisze WT (tekst) 

  set_col(40); 
  put("dni_robocze'image(so) : "); 
  put(dni_robocze'image(so));   

-- wypisze SO (tekst) 

  new_line; 
  put("integer'value(""12"") : "); 

background image

81 

  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));   

 

 

-- wypisze PON 

  set_col(40); 
  put("dni_robocze'min(so,pon) : "); 
  put(dni_robocze'min(so,pon));  

 

-- wypisze PON 

  new_line; 
  put("dni_robocze'min(so,nie) : "); 
  put(dni_robocze'min(so,nie));  

 

-- 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) 

background image

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 "); 

background image

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   =instrukcje_1 

 

  when lista_moŜliwości_2   => instrukcje_2 

 

  .......... 

 

  when lista_moŜliwości_n   => instrukcje_n 

 

end case; 

 

lub teŜ 
 

[2]  case selektor is 
 

  when lista_moŜliwości_1   =instrukcje_1 

 

  when lista_moŜliwości_2   => instrukcje_2 

 

  .......... 

 

  when lista_moŜliwości_n   => instrukcje_n 

 

  when others                => instrukcje_dalsze 

 

end case; 

 
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; 
 

background image

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. 
 

A  jak  moŜe  wyglądać  lista_moŜliwości?  JeŜeli  jest  ona  jednoelementowa,  na  przykład 

tak: 

 
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ść 1, 2 lub 7, albo w 
postaci 
 

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 "); 

background image

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       => m:=waga_zuk; 
    when star_5t   => m:=waga_star_5t; 
    when jelcz_14t => m:=waga_jelcz_14t; 
    when steyer    => m:=waga_steyer; 
    when inne      => put("podaj mase wlasna samochodu (w kg) : "); 
                      get(m); 

background image

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; 

background image

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 
 

background image

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ść razy? 

W tym celu moŜemy wykorzystać pętlę for o składni: 

 
[1]  for licznik_pętli in zakres loop 

background image

81 

 

 

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 wykonuje sie po raz 1 
Petla wykonuje sie po raz 2 
Petla wykonuje sie po raz 3 
Petla wykonuje sie po raz 4 
Petla wykonuje sie po raz 5. 
 
Petla wykonuje sie dla liczby 5 
Petla wykonuje sie dla liczby 4 
Petla wykonuje sie dla liczby 3 
Petla wykonuje sie dla liczby 2 
Petla wykonuje sie dla liczby 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. 

background image

81 

 

for i in 1..10 loop 
     ..... 
     i:=5; 
end loop; 
 

jest niedozwolone (licznik jest traktowany podobnie jak stała). Jego wartość i zgodność z zakresem jest 
rozpatrywana na początku pętli, zatem napisanie  
 

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 

background image

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("       TABELKA WARTOSCI LOGICZNYCH"); 
  new_line(3); 
  put(" |   p   |   q   |  not p  | p and q | p or q | p xor q |"); 
  new_line; 
 
  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 

background image

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); 

 

 

 

-- wypisze 8 

    new_line;  
     for i in 1..3 loop  
            put(i,0); 

 

 

-- wypisze 1, 2 lub 3 

            new_line; 
            put(pr30.i,0); 

 

-- wypisze 8 

            new_line; 
       end loop; 
   
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); 

 

 

-- wypisze wartosc logiczna 

           new_line; 
       end loop; 
       put(i,0);   

 

 

-- wypisze liczbe 

       new_line; 
  end loop; 
end pr31; 
 
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;  

background image

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); 

 

background image

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ść łańcucha oraz sposób numerowania 
jego elementów. Obrazowo zmienną zadeklarowaną 
 

imie : string (1..8):="Karolina"; 

moŜemy przedstawić 
 

 
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 

zwraca liczbę będącą dolnym ograniczeniem indeksu 

łańcucha 

zmienna_łańcuchowa'last 
 

zwraca liczbę będącą górnym ograniczeniem indeksu 

łańcucha 

zmienna_łańcuchowa'range 

zwraca zakres indeksu łańcucha (ograniczenie_dolne .. 

ograniczenie_górne) - jest odpowiednikiem napisania 

zmienna_łańcuchowa'first .. 

zmienna_łańcuchowa'last 

zmienna_łańcuchowa'length 

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 

'K' 

'a' 

'r' 

'o' 

'l' 

'i' 

'n' 

'a' 

background image

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" & "Kowalski" 

daje 

"JanKowalski" 

"Jan" & ‘K’   

 

daje 

"JanK" 

'a' & 'b' 

 

 

daje  

"ab" 

'+' & "12" 

 

 

daje 

"+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ą  procedury  umieszczone  w  pakiecie 
ada.text_io. Procedura put wypisuje łańcuch lub jego część 
 
imie : string(1..8) := "Karolina"; 
...  
put (imie);  

 

-- wypisze  Karolina 

put(imie(1..5));   

-- wypisze  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ńcuchowazmienna_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. 
 

background image

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 

background image

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: 

background image

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 

background image

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ść  indeksu 
umieszczoną w nawiasach okrągłych. Indeks moŜe być wyraŜeniem, stałą lub zmienną.  
 
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; 

background image

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. 
 

background image

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 ciag_dluzszy is array(1..10) of integer; 
  type ciag_krotszy is array(1..5) of integer; 
  type ciag_rzeczywisty is array(1..5) of float; 
  type 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  

background image

81 

 

  put_line("tablice  - ciagi dni - rozne"); 

  end if; 
 
                         -- 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);     -- wykonalne 
  cw1:=cw1(1..5) & cw2(6..10); 
 
--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  

zwracający najmniejszą wartość indeksu wdanej tablicy 

zmienna_tablicowa'last   

zwracający największą wartość indeksu wdanej tablicy 

zmienna_tablicowa'range  

zwracający zakres indeksu tablicy (odpowiednik  

 

 

 

 

 

tablica'first .. tablica'last) 

zmienna_tablicowa'length 

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; 

background image

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 

 

background image

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)  

background image

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)); 
 

background image

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 := ((0,1), (7,8)); 
m1 := ( 1=> (0,1), 2=> (7,8)); 
m1 := ( (1=>0, 2=>1), (1=>7, 2=>8)); 
m1 := (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) 

-- zwróci 1996 

wakacje_Ani'last(2) 

-- zwróci wrzesien 

wakacje_Ani'range(1) 

-- 1996..1998 

wakacje_Ani'range(2) 

-- lipiec..wrzesien 

wakacje_Ani'length(1) 

-- 3 

wakacje_Ani'length(2) 

-- 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); 

background image

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 

background image

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; 

background image

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 

background image

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; 

background image

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); 

background image

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. 

background image

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);   

--tu "wycinamy" fragmenty 

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 typ1 is array (positive range 1..10) of integer; 
type typ2 is array (integer range  1..10) of integer; 
t1 : typ1 := (others=>0); 
t2 : typ2; 
                     
t2:=t1;   

-- niewykonalne - niezgodność typów 

t2:=typ2(t1);  -- wykonalne dzieki konwersji 
t1:=typ1(t2);  -- 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 );              

-- wypisze TRUE 

put( t1(1..2) > t1(1..3));  

-- 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);        

-- wypisze TRUE 

put(m3_1 in macierz_2_na_2);       

-- wypisze FALSE 

put(m2 in  tez_macierz_2_na_2);    

-- 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ć 

background image

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       ", "Brzeczyszczykiewicz           ", 1974, 
201, true); 
 
lub notację nazywaną: 
 
punkt_A:=(x=>2.0, y=>-1.1); 
student:= 

background image

81 

(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; 
 

background image

81 

a:xxx; 
 
a:=(1,2,z=>3); 

-- zapis prawidłowy 

a:=(x=>1,2,3); 

-- 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;  

 

-- m.x i m.y mają wartość 0 

q : punkt_ekranu := (1,2); 

-- m.x=1, m.y=2 

 
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; 

background image

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;  -- niedozwolone 
   b: float; 
end record; 
 
(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); 

 

 

background image

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 

background image

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); 

background image

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       : tablica_pytan; 
     odpowiedzi : tablica_odpowiedzi; 
     podane_odp : tablica_odpowiedzi:=(others=>(others=>false)); 
     op         : integer; 
     punkty  

: 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); 

background image

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; 
     

background image

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)    := (others=>' '); 

 

nazwisko : string(1..30)    := (others=>' '); 

 

oceny    : tabela_ocen; 

    srednia  : float; 
end record; 
 
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; 

background image

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ść1 | wartość2 | ... => pole : typ_pola
when wartość1 .. wartość2  => pole : typ_pola
 
a takŜe 
 
when others =>  pole : typ_pola
 

background image

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); 

 

  

-- notacja pozycyjna 

sasiad   : osoba (kierowca => true); 

-- notacja nazywana 

 

Dany  rekord  (czyli  dana  zmienna  czy  stała)  zostaje  w  ten  sposób  niejako  "ograniczona"  (ang. 

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

 
subtype osoba_bez_prawa_jazdy is osoba(kierowca=>false); 
subtype osoba_z_prawem_jazdy is osoba(kierowca=>true); 
 
sasiadka: osoba_bez_prawa_jazdy; 
sasiad: osoba_z_prawem_jazdy; 
 

Nadawanie wartości poszczególnym składowym rekordu przebiega w standardowy sposob: 

 
sasiadka.imie:="Kazimiera " 
sasiadka.nazwisko:="Kowalska  "; 
 

lub przy uŜyciu agregatu rekordu z notacją pozycyjną czy nazywaną (jak dla rekordów bez wariantów).  
 
 sasiadka:=(false, "Kazimiera ", "Kowalska  ", 1955); 
 sasiad:=(kierowca=>true, imie=>"Jan       ", nazwisko=>"Kowalski ", 
         rok_urodzenia=>1954,prawo_jazdy=>(true,true,others=>false)); 
 
ZauwaŜmy, Ŝe mimo określenia wartości wyróŜnika w momencie deklaracji występuje on jeszcze raz w 
agregacie  rekordu  (oczywiście  jego  wartość  w  agregacie  i  deklaracji  musi  być  taka  sama,  w 
przeciwnym razie wystąpi Constraint_Error). Agregat rekordu moŜe być równieŜ uŜyty podczas 
deklaracji zmiennej, powodując "określenie" rekordu:  
 
sasiadka : osoba := (false, "Kazimiera ", "Kowalska  ", 1955); 
 
W ten sposób równieŜ otrzymujemy rekord o stałej wartości wyróŜnika. 

background image

81 

 
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)); 

background image

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 

background image

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; 

background image

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ą: 
 

background image

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ść nazwy określiliśmy na zawsze (podobnie jak w 
przypadku rekordów z wariantami),  bądź teŜ 
 
   zaklad3 : dane; 

 

-- domyślna wartość wyróŜnika - 20 

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

 

   rok_zalozenia=> 1974); 

 
otrzymując rekord "nie określony": 
 
zaklad3 := zaklad4; 

 

-- zmiana długości z 10 na 13 

 
Oczywiście  wyróŜników  moŜe  być  więcej  niŜ  jeden.  Mogą  odnosić  się  one  do  tego  samego  lub  do 
róŜnych pól rekordu: 
 
type tabela_pracownikow is array (positive range <>) of osoba;     
   
type firma (dl_nazwy,il_pracownikow:positive) is record 
     nazwa_firmy : string(1..dl_nazwy); 
     zatrudnieni : tabela_pracownikow (1..il_pracownikow); 
end record; 
 
f1 : firma(dl_nazwy=>3, il_pracownikow=>10); 
f2 : firma(12,5); 
 
Zdefiniowaliśmy  tu  dwa  typy  -  tablicowy  o  nie  określonym  rozmiarze  (tabela_pracownikow)  i 
rekordowy. Niestety nie moŜna utworzyć tylko jednego typu, pisząc 
 
    type firma2 (dl_nazwy,il_pracownikow:positive) is record 
        nazwa_firmy : string(1..dl_nazwy); 
        zatrudnieni : array (1..il_pracownikow) of osoba; 
    end record; 
 
poniewaŜ Ŝadne z pól rekordu nie moŜe być zadeklarowane jako tablica anonimowa. 
 
WyróŜniki  rekordu  nie  mogą  występować  w  deklaracji  typu  jako  elementy  jakiegoś  wyraŜenia. 
Napisanie na przykład 
 
    type firma (dl_nazwy,il_pracownikow:positive) is record 
        ... 
        zatrudnieni : tabela_pracownikow1 (1..il_pracownikow+1); 
    end record; 
 
jest niedozwolone. 
 

 

Inne zastosowania wyróŜników. 

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

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

− 

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

Istnieją jeszcze trzy inne zastosowania : 
− 

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

− 

inicjowanie wartości pewnych pól rekordu, 

background image

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); 

 

-- wypisze 60 

obszar_zabudowany.max_predkosc:=50; 
put (obszar_zabudowany.max_predkosc); 

 

-- wypisze 50 

 
Jak  widać,  wartość  wyróŜnika  moŜna  tutaj  zmieniać  (nawet  jeśli  zadeklarowaliśmy  rekord  jako 
"określony"). 
 
Tworzenie w rekordzie pól zachowujących się jak stałe: 
 
 type plec is (M, K); 
 
 

 

 

 

-- plec i rok urodzenia nie moga sie zmienic,  

 

 

 

-- waga i wzrost - tak 

      
type czlowiek (p: plec;rok_urodzenia:positive) is record 
waga, wzrost: positive; 
end record; 
 

 

 

 

 

 

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

 

 

 

 

 

 

 

 

-- niedozwolone  

 Iksinski:=(p=>M, rok_urodzenia=>1965, wzrost=>187, waga=>99); 
 
WyróŜnik uŜywany do "określenia" rekordu będącego polem definiowanego rekordu: 
 
 

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

     
    type firma (il_pracownikow:positive) is record 
        nazwa_firmy : string(1..20); 
        zatrudnieni : tabela_pracownikow (1..il_pracownikow); 
    end record; 
 
   type pracodawca (ilosc_zatrudnionych:natural) is record 
 

 

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

 

 

   jego_firma : firma(ilosc_zatrudnionych); 

 

end record; 

 

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

  

Algorytmy................................................................................................................................................. 1 
Pierwsze programy ................................................................................................................................... 2 
Trochę matematyki ................................................................................................................................... 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 

background image

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