Znovu předesílám, že není mým cílem nějaký úvod do programování v Adě, nýbrž demonstrace jazykových konstrukcí, které jsou nějakým způsobem zajímavé a užitečné. A jejichž užitečnost spočívá v možnosti aplikace i mimo tento nepříliš rozšířený jazyk. Lidem, kteří by se chtěli Adu opravdu naučit, vřele doporučuji knihu Programming in Ada 95 od Johna Barnese. Je to jedna z nejlepších knih o programování, co jsem kdy četl. Autor je neuvěřitelně vtipný a píše mimořádně srozumitelně, o jeho příkladech zdrojových kódů ani nemluvě.
Atributy
Nejprve trochu ujasním terminologii. V souvislosti s objektově orientovaným programování se občas termín „atribut“ používá ve významu „členská proměnná“, tedy proměnná spojená s nějakou třídou. Zde má tento pojem jiný, širší význam.
Atributy v Adě jsou jakési doplňkové informace spojené obvykle s nějakým typem nebo proměnnou. Popisují vlastnosti nějakého objektu a jsou programátorovi přístupné. Adovským atributům jsou asi nejvíce podobné ty z C#, které jsou ovšem mnohem sofistikovanější. Když si uvědomíte, že Ada v jistých ohledech konkuruje jazyku C, je to víc než dost. Nejlepší bude, když začnu malým příkládkem. V minulém dílu jsem se zabýval tvorbou celočíselných typů s různými omezeními.
typeInteger is range -2**31 .. 2**31-1;
subtype Natural is Integer range 0 .. 2**31-1; --podtyp přirozené číslo (s nulou)
subtype Positive is Integer range 1 .. 2**31-1; -- podtyp kladné číslo
Tento fragment kódu má ale jeden nedostatek. Podtypy Natural aPositive jsou sice zdola omezeny tak, jak potřebujeme, shora jsou však omezeny „magickými čísly“ a není zcela jasné, že horní hranici sdílejí s typem Integer. Dalo by se to vyřešit pomocí konstanty, použitím atributů dosáhneme kýženého efektu ještě elegantněji. Atributy mají v Adě vtipnou syntaxi používající apostrof.
type Integer is range -2**31 .. 2**31-1;
subtype Natural is Integer range 0 .. Integer’Last; --podtyp přirozené číslo (s nulou)
subtype Positive is Integer range 1 .. Integer’Last; -- podtyp kladné číslo
Ke každému celočíselnému typu je dostupný atribut Last, jehož hodnota je nejvyšší možná hodnota daného typu. Když se nyní rozhodnu změnit rozsah typu Integer, (třeba na 64bitové číslo), stačí jen jeden zásah do programu v místě jeho definice. Atribut obsahující nejmenší možnou hodnotu daného typu se překvapivě jmenuje First. Je to mnohem příjemnější, než vymýšlet nějaké konstanty ala MAX_INT.
Uvedu ještě několik zajímavých příkladů. Jak jsem psal minule, v Adě je možné definovat celočíselné typy tak, že si programátor poručí rozsah hodnot a překladač sám zvolí vhodnou „fyzickou“reprezentaci. Jak si s tím překladač poradil, můžeme zjistit právě z atributů. Následující řádek zavádí jeden takový extravagantní typ.
type Hrusky is range 200 .. 1_000;
Atribut Size udává počet bitů, které jsou potřeba k reprezentaci všech hodnot daného typu. Výraz Hrusky’Size má hodnotu deset, protože 1024 je dva na desátou. Jelikož většina počítačů neoplývá desetibitovou aritmetikou :-), určuje překladač ke každému typu ještě podkladový typ, jehož reprezentace nedělá počítačům problémy. Tento typ je dostupný přes atribut „Base“. Výraz Hrusky’Base’Size má hodnotu šestnáct, čiliHrusky jsou uvnitř obyčejný šestnáctibitový integer.
Atributů je v jazyce definována celá řada. Mají své využití nejen u celočíselných typů, ale i u vyjmenovaných, polí, floating-point typů, tasků (objektů používaných při vícevláknovém programování).
O polích a cyklech
Pole jsou v Adě naprosto standardní a znalce Pascalu a céčka nemají celkem čím překvapit. Opět vystřihnu malou ukázku.
declare
type Pole_Znaku is array (1..100) of Character;
type Matice is array(1..100, 1..100) of Float;
Me_Pole, Me_Pole2 : Pole_Znaku;
Ma_Matice : Matice;
begin
Me_Pole(1) := ‘A’;
-- Přiřazení jednoho znaku
Me_Pole2 := Me_Pole
-- V Adě lze celá pole přiřazovat „na jeden vrz“
Me_Pole(11..15) := Me_Pole(1..5);
-- stejně jako úseky (slices)
Me_Pole(1..5) := “Ahoj!”;
-- řetězce jsou v Adě obyčejná pole znaků
Ma_Matice(55, 55) := 100.0;
-- vícerozměrné indexování
end;
Pole se indexují pomocí kulatých závorek. Na první pohled to vypadá docela zběsile, ovšem má to své výhody. Je možné lehce zaměnit přístup do pole za volání nějaké (mapovací) funkce, tedy čistokrevné skrývání implementace. V Adě je možné přiřazovat celá pole bez použití nějakého jednoúčelového for cyklu. Stejně jednoduše je možné pracovat i s úseky (slices) polí. Řetězce jsou definovány zcela přirozeně – jako pole znaků.
Pole samozřejmě mají také různé atributy. First pro nejmenší index, Last pro největší index. Hodně praktický je atribut Range, který vrací rozsah (interval) indexů a lze jej s výhodou použít pro konstrukce procházející všechny elementy pole. Nejprve uvedu ukázku, jak vypadá adovský for.
for I in 1..20 loop
-- tady se něco dvacetkrát provede
end loop;
Tak, to je on :-). Proměnná I se nikde nedeklaruje, její typ se automaticky určí podle intervalu, který se nachází za klíčovým slovem in. Cyklus se neuzavírá mezi begin a end, ale mezi loop a end loop, což v delším kódu zvyšuje čitelnost. A teď konečně k tomu „foreach“.
for I in Me_Pole’Range loop
-- tady něco provedeme s prvkem Me_Pole(i)
end loop;
Je to opravdu elegantní a bezpečné, programátor nemusí nikde zjišťovat, zda je pole indexováno od nuly, či jedničky a jaká je horní mez. Na dnešní poměry to není nic ohromujícího – většina z nás používá rafinované kontejnerové třídy z C#/Javy/C++ STL, kde žádné překročení indexů nehrozí. Když si ale představíte, že uvedený kód lze zkompilovat na nějaký jednočip, kterému se o Javě ani nesní, tak musíte tu eleganci ocenit.
Procedury a jejich parametry
Předávání parametrů funkcím a procedurám je v různých jazycích řešeno různě. Java předává primitivní typy vždy hodnotou a objekty vždy referencí. C# umožňuje i předávání primitivních typů referencí a naopak změnit hodnotu předané reference na objekt v těle funkce. V C++ můžeme všechno možné i nemožné, dokonce předávat hodnotou objekty.
Vždy ale jde o to, že programátor musí nějak specifikovat, jak se má ten či onen parametr předávat. Zvolí nějakou možnost, která je kompromisem mezi požadovanou funkčností a výkonem. Autoři Ady šli svou zcela originální cestou. Programátor nerozhoduje, jestli se parametr bude předávat referencí, nebo hodnotou, toto rozhodnutí je ponecháno na překladači. Programátor pouze určí, jakým směrem budou u nějakého parametru téct informace. Tedy zda pouze do procedury (vstupní), pouze z procedury (výstupní), nebo oběma směry (vstupně výstupní). K určení toku je Ada vybavena dvěma klíčovými slovy in a out, jejichž význam je od pohledu jasný. Zde je několik ukázek:
function Dvojnasobek (X : in Integer) return Integer is
begin
return 2*X;
end;
Jednoduchá funkce vracející dvojnásobek svého argumentu. Tok dat je jednoznačně do funkce. Parametr je proto vstupní a je označen klíčovým slovem in. Za poznámku stojí, že proměnná x se uvnitř těla této funkce chová jako konstanta, kdybychom se pokusili hodnotu proměnné x změnit, skončilo by to chybou při překladu. Jelikož je Integer primitivní typ, bude pravděpodobně předán hodnotou. Pokud by ale šlo o nějaké pole, použije překladač pravděpodobně referenci. Další zajímavá věc je, že funkce (na rozdíl od procedur) musí mít všechny parametry označeny jako vstupní. Funkce v Adě se tedy chovají přesně jako funkce v matematice (tedy z jednoho a více parametrů se určí jeden výsledek). Pokud tedy mám demonstrovat i jiné než vstupní parametry, musím klíčové slovo function opustit:
procedure Dvojnasobek (X : in Integer; Vysledek : out Integer) is
begin
Vysledek := 2*X;
end;
Zdejsem vlastně pouze předělal funkci na proceduru. Parametr X je vstupní, parametr Vysledek je výstupní. Teď není přímo vracena žádná hodnota, ale při volání se změní hodnota proměnné dosazené za Vysledek. Poslední ukázka demonstruje vstupně – výstupní parametr.
procedure Zdvojnasob (X : in out Integer) is
begin
X := 2*X;
end;
Při zavolání této procedury její parametr zdvojnásobí. Překladač buď použije reference, nebo proměnnou X zkopíruje dovnitř a poté zase ven, to je jeho věc.
Závěrem
Doufám, že ti, co dočetli až sem, si trošku rozšířili obzory. Rozhodně jsem nepsal o žádných posledních výstřelcích vývoje programovacích jazyků, ale o poctivých klasických konceptech, které jsou dovedeny takřka k dokonalosti. Příští článek bude o vícevláknovém programování, které je v Adě naopak vyřešeno zcela jinak než v ostatních programovacích jazycích. Takže o tom, jak si vlákna dávají Randevouz, příště.