Opowieść o programowaniu w Adzie



Pobieranie 0.69 Mb.
Strona7/16
Data28.04.2016
Rozmiar0.69 Mb.
1   2   3   4   5   6   7   8   9   10   ...   16

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;

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

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

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

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

new_line;

put("nazwa polska : ");

dni_io.get(d);

put("nazwa angielska : ");

days_io.put(days'val(dni'pos(d)));

end pr21;


1   2   3   4   5   6   7   8   9   10   ...   16


©absta.pl 2016
wyślij wiadomość

    Strona główna