Hlavní navigace

Programovací jazyk Ada: překlad, atributy, smyčky a základy typové kontroly

14. 5. 2015
Doba čtení: 9 minut

Sdílet

V minulém dílu jsme dokončili základní přehled možností jazyka Ada a vyzkoušeli si překlady jednoduchých aplikací Ada včetně ukázky základních atributů. V dnešním dílu se blíže podíváme na editor Geany. Budeme se také věnovat praktickým příkladům, které ještě lépe osvětlí uvedené teoretické informace.

Než se podíváme na další kroky příkladu z minulého dílu – překlad a spuštění, trochu se zaměříme na Geany. To totiž není jenom vyspělý programátorský editor, ale také tzv. „lehké“ IDE. Pro naše základní příklady z něj budeme potřebovat vlastně jenom dva příkazy – Build a Run (Execute).

Na prvním obrázku v galerii je vidět, že při volbě menu Build → Set Build Command je již pro Adu nastaven příkaz gnatmake a není nutné nic dalšího přidávat či nastavovat. Náš soubor máme připravený, takže můžeme zkusit překlad – jestli v Geany vůbec funguje, jestli máme náš soubor sepsaný řádně dle pravidel a jestli se nám tedy ukáže/neukáže něco na výstupu z překladače. Překlad můžeme spustit trojím způsobem:

  1. Z menu Build → Build
  2. Z toolbaru pomocí ikony „tvárnice“
  3. Klávesovou zkratkou F9

Ať už se použije libovolná možnost, Geany každopádně překlad provede a výsledek se ukáže ve spodním okně v záložce Compiler – viz druhý obrázek v galerii. Jak je z obrázku patrné, Geany za nás spustilo příkaz (tedy vlastně příkazy tři) a podalo poslušné hlášení o tom, že překlad proběhl úspěšně. Pro spuštění přeložené aplikace opět nemusíme přecházet do terminálu (nebo to můžeme udělat přímo v Geany na příslušné záložce), ale můžeme aplikaci rovnou spustit. Lze to opět trojím způsobem:

  1. Z menu Build → Execute
  2. Z toolbaru pomocí ikony „tří ozubených koleček“
  3. Klávesovou zkratkou F5

Libovolná cesta vede k něčemu, co je vidět na třetím obrázku. Zde jsou očekávané výstupy z aplikace a na konci nápověda – stiskem klávesy Enter se terminál s výstupem zavře. Výstupy je možné také prohlížet v příloze ada1-run. Samotný kód je pro úplnost znovu v příloze ada1.

Již bez dalších velkých komentářů provedeme cosi podobného pro čtyři definované číselné typy s desetinnou čárkou – procedura ada2, výsledek ada2-run a čtvrtý obrázek v galerii. Další příklad ukáže něco z informací o číselných typech s definovaným rozsahem, konkrétně vždy od 1 do 25, 250, 2500, 25000 a 250000 a jejich velikost. Vše je vidět na pátém obrázku a je možné podrobněji prohlížet v přílohách ada3 a ada3-run. Z výpisu kódu je také vidět další podobnost s Pascalem: proměnné a další používané objekty musí být definovány ještě před začátkem procedury. V samotném těle procedury není možné žádné proměnné definovat ani ty předem nedefinované použít, jinak překladač hlásí chybu.

Čtvrtý příklad je z oblasti polí. Jsou v něm definovaná 4 pole – dvě jako typ Integer, dvě Long_Long_Float a dva rozsahy – od 1 do 25 a 250 000. Zobrazují se dva atributy pole – Size a Length. Podrobnosti jsou na šestém obrázku a v přílohách ada4 aada4-run.

V této souvislosti by bylo vhodné připomenout, že jsme zde nezmínili a zatím ani nezmíníme další typ, podobný polím. Jedná se opět o podobnost s Pascalem a typ record. Je to složený typ, který může obsahovat jedno nebo více polí různého typu. Pokud by se s ním někdo chtěl blíže seznámit, nalezne dost informací v dokumentaci Ada.

Další příklad je ukázka tzv. výčtových typů. Jak je vidět na sedmém obrázku, je definován výčet dnů v týdnu a jeho atributy Size, First a Last – viz také přílohy ada5 a ada5-run.

Nyní přejdeme od „čisté“ definice typů k jejich spojení se smyčkami. Tam si jednak ukážeme vlastnosti smyček, ale také některé speciální typy, které teprve po výpisu hodnot odhalí svou pravou podstatu. Jako první využijeme již definovaný výčtový typ a vytvoříme šestý příklad. Jeho kód vypadá následovně:

with Ada.Text_IO;  use Ada.Text_IO;             --1
    procedure Ada6 is               --2

    type dny is (Po,Ut,St,Ct,Pa,So,Ne);     --3

    begin                       --4

        For I in dny'Range          --5
            loop                --6
                Put_Line (I'Img);   --7
            end loop;           --8

    New_Line;                       --9

        For J in reverse dny'Range      --10
            loop                --11
                Put_Line (J'Img);   --12
            end loop;           --13

    end Ada6;                   --14

První čtyři řádky nemá smysl komentovat, zajímavý je až ten následující. Obecná konstrukce smyčky for již byla uvedena, ale zde byl použit atribut polí a výčtových typů range. Ten nás zbaví nutnosti znovu a znovu vypisovat hranice pole (Což není sice nijak pracné, ale co když třeba v definici změníme rozsah pole a ve smyčce/smyčkách na to zapomeneme?) nebo ještě hůře obsah výčtového typu! Uvedeným příkazem máme jistotu, že rozsah daného pole bude vždy správný a navíc ho definujeme ve smyčce velmi jednoduše. První smyčka je následně doplněna druhou, která vypisuje výčtový typ v obráceném pořadí. Opět není nutné nic doplňovat a dopisovat, stačí do počátku smyčky přidat klíčové slovo reverse. Vše k tomuto příkladu je na osmém obrázku a v přílohách ada6 a ada6-run.

V dalších dvou příkladech si ukážeme, jak je možné pomocí definice typů a smyček vytvořit zajímavé možnosti pro přesné uživatelské nastavení typů. Ten sedmý vypadá následovně:

with Ada.Text_IO;  use Ada.Text_IO;           --1
    procedure Ada7 is             --2

    type val is digits 3;             --3
    val1 : val:=10.098765;            --4
    val2 : val:=-8.012345;            --5

    begin                     --6
        Put_Line (val1'Img);          --7
        Put_Line (val2'Img);          --8
        Put_Line (val'Last'Img);      --9
    end Ada7;                 --10

Na řádku tři je nová definice typu s desetinnou čárkou, kde došlo k omezení na tři platné číslice. Více pak ukazují příkazy pro zobrazení dalších dvou veličin s přiřazenou hodnotou na řádcích 7 a 8. Příkaz na řádku 9 pak dokazuje, že vnitřně jsou čísla v plném rozsahu. Definici tohoto typu je možné kombinovat ještě s omezením rozsahu, která vypadá třeba takto:

type val is digits 3 range -10.0..10.0;

Na devátém obrázku v galerii a v přílohách ada7ada7-run je možné získat úplný přehled o uvedené funkci.

Osmý příklad vypadá velmi podobně, jako ten předchozí:

with Ada.Text_IO;  use Ada.Text_IO;             --1
    procedure Ada8 is                   --2

    type val is delta 0.01  digits 7 range -10.0..11.0; --3
    val1 : val:=10.98;                          --4
    val2 : val:=-8.0;                   --5

    begin                           --6
        Put_Line (val1'Img);                --7
        Put_Line (val2'Img);                --8
        Put_Line (val'Last'Img);            --9
    end Ada8;                       --10

Oba příklady se liší zásadně na řádku 3, kde je použita nová funkce delta. Ta určuje počet desetinných míst, který bude typ obsahovat. Tím vlastně vytvoříme typ s fixním počtem desetinných míst, který můžeme ještě doplnit o počet platných číslic a rozsah jejich hodnot. Výsledky (desátý obrázek v galerii a přílohy ada8 a ada8-run) ukazují také rozdíl od předchozího příkladu. Ten se týká hlavně výstupu na řádku 9, kde už není plný rozsah typu Float, ale pouze hodnota, která také odpovídá definovanému typu.

Nakonec je důležité upozornit na to, že v jazyce Ada pro smyčku loop neexistuje žádný speciální výraz pro nastavení velikosti kroku (příkaz step, popř. konstrukce např. for (i=0; i<10; i +=2) atd.). Pokud by bylo třeba něco podobného použít, je nutné situaci řešit pomocí definice typu, přes který se provádí běh smyčky. Je otázka, která z těchto variant je lepší, ale v Adě je pouze tato jedna.

Jak již bylo několikrát uvedeno v minulých dílech našeho seriálu, je silná statická typová kontrola jedním z hlavních znaků programovacího jazyka Ada. Typová kontrola jako taková je už dlouhou dobu jablkem sváru mezi programátory. Pro někoho je vhodná a přínosná, pro druhého otravná a svazující. Je asi složité až nemožné rozhodnout, jak to doopravdy je a už vůbec není možné sjednotit diametrálně odlišná stanoviska uživatelů.

Každopádně je ale asi možné konstatovat, že čím dříve se nějaká chyba v kódu aplikace objeví, tím jednodušší, rychlejší a levnější je její následné odstranění. Toto konstatování je samozřejmě o to důležitější, pokud se jedná o hledání chyb v nějaké důležité aplikaci s vysokými bezpečnostními, provozními a dalšími požadavky. Abychom se ale nevěnovali jenom holé teorii, uvedeme si několik příkladů, které vše objasní mnohem lépe a rychleji.

Vytvoříme si proto další soubor a v něm si definujeme všechny již dříve uvedené celočíselné i plovoucí číselné typy – viz obrázek č. 11. Jak ukazuje další příloha (kompletní kód postupně upravované procedury je v příloze ada9), je definováno celkem 11 proměnných, kterým je rovnou přiřazena nějaká hodnota (0 nebo 1). Postupně si v těle procedury ukážeme několik jednoduchých příkazů a jejich důsledek na překlad. Jako první vyzkoušíme kompatibilitu typů Integer a Natural. Logicky se zdá, že Natural je podmnožinou typu Integer a že by tedy s příkazem n := i; neměl být žádný problém. To nakonec ukazuje i výstup z překladu a spuštění programu:

gnatmake ada9.adb
gcc -c ada9.adb
gnatbind -x ada9.ali
gnatlink ada9.ali

./ada9
N =  0

Podobný příkaz použijeme i pro typ Positive: p := i; Výsledek bude ale trochu jiný:

gnatmake ada9.adb
gcc -c ada9.adb
ada9.adb:19:21: warning: value not in range of type "Standard.Positive"
ada9.adb:19:21: warning: "Constraint_Error" will be raised at run time
gnatbind -x ada9.ali
gnatlink ada9.ali

./ada9
raised CONSTRAINT_ERROR : ada9.adb:19 range check failed

Překladač i samotná aplikace se nám jemně snaží naznačit, že typ Positive neobsahuje hodnotu nula. Pokud bychom ale zůstávali v definičním oboru omezenějších typů, jsou tyto tři typy plně kompatibilní. Na základě podobné logiky bychom mohli dojít k závěru, že všechny ostatní typy jsou podmnožinou typu Long_Long_Integer. Zkusíme to tedy s příkazem lli := i; Výsledek ovšem našemu původnímu předpokladu moc neodpovídá:

gnatmake ada9.adb
gcc -c ada9.adb
ada9.adb:19:23: expected type "Standard.Long_Long_Integer"
ada9.adb:19:23: found type "Standard.Integer"
gnatmake: "ada9.adb" compilation error

 ./ada9
raised CONSTRAINT_ERROR : ada9.adb:19 range check failed

Zkrátíme tedy další zkoušky s tím, že žádné další typy mezi sebou kompatibilní nejsou a totéž platí i o všech typech s desetinnou čárkou. Asi není nutné nijak zvlášť upozorňovat na to, že mezi sebou nejsou kompatibilní celočíselné typy s těmi s desetinnou čárkou. Pro jejich konverzi v jazyce Ada existují dva velmi jednoduché příkazy:

i := Integer(f);
f := Float(n);

Překlad proběhne bez zádrhelů a výsledek je očekávaný:

./ada9
I =  0
F =  1.00000E+00

To by asi k jednoduchým typům mohlo zatím stačit a podíváme se na typy s definovaným rozsahem. Do definic si tedy přidáme příkaz

r : Integer range 1..25;

a do těla pak r:=5; Výstup se dá očekávat a tak ho ani nebudu uvádět. Pokud bychom ale příkaz změnili na r:=-5;, situace se radikálně změní:

gnatmake ada9.adb
gcc -c ada9.adb
ada9.adb:21:22: warning: value not in range of subtype of "Standard.Integer" defined at line 17
ada9.adb:21:22: warning: "Constraint_Error" will be raised at run time
gnatbind -x ada9.ali
gnatlink ada9.ali

./ada9
raised CONSTRAINT_ERROR : ada9.adb:21 range check failed

V dalších pokusech budeme pokračovat zase příště

Tímto dnešní díl uzavřeme. V příštím dílu se zaměříme na další stránky typové kontroly jazyka Ada a ukážeme si spoustu chybových hlášení, které překladač generuje. Kromě toho se ještě v některých speciálních případech vrátíme k definicím typů a novým atributům, které by mohly být pro další postupy zajímavé.