next up previous contents index
Next: Použité pojmy Up: Programovaci jazyk BETA - Previous: Contents

Programovací jazyk BETA

Cílem následujícího textu je seznámení s principy programovacího jazyka BETA, jeho nejdůležitějšími a nejzajímavějšími rysy a také s jeho syntaxí.

Prostřednictvím postupného seznamování se se syntaxí jazyka a za pomoci četných příkladů praktického využití jeho jednotlivých rysů bychom měli postupně dospět k poznání hlavních principů, které jsou samy o sobě tím nejdůležitějším. Tato cesta se může zdát za určitých okolností poněkud zdlouhavá, a proto lze na jejím konci najít relativně ucelené shrnutí základních principů BETAshrnuti.

První příklad

Jak jinak začít, než pozdravením:
alltt870

Vzor

 

Pomocí popisovače objektu je možno definovat vzor (pattern), jež je základním stavebním kamenem jazyka BETA. Vzor představuje zobecněný abstrakční mechanismus, který je jedním z nejvýznačnějších rysů jazyka BETA (a proto byl také termín vzor zaveden, pro odlišení abstrakčního mechanismu jazyka BETA od typových struktur v jiných jazycích). Vzor v sobě skrývá zobecnění konstrukcí známých jako třída, procedura, funkce, korutina, proces a výjimka.

Předpokládejme nyní následující definici:


alltt872

Základní vzory

Vzor integer, se kterým jsme se již setkali, patří mezi jednoduché typy, tzv. základní vzory (basic patterns). Jsou to:

integer
- reprezentuje celé číslo; k dispozici jsou operace +, -, , div (celočíselné dělení), mod (zbytek po celočíselném dělení)
boolean
- logická hodnota, může nabývat hodnot truefalse; jsou definovány operace and, ornot
char
- slouží pro reprezentaci znaku (z ASCII tabulky); konstanta typu char se v BETĚ vyjadřuje znakem uzavřeným v apostrofech, např. 'D' (pro vytisknutelné znaky); hodnota objektu char je celé číslo korespondující s polohou příslušného znaku v ASCII tabulce, proto je možné psát např. 'a'+1, což je rovno 'b', atd.; pro uložení řetězce znaků slouží typ (vzor) text, který nepatří mezi základní vzory a bude popsán dále
real
- tento vzor reprezentuje reálné číslo podobně jako integer číslo celé; definované operace jsou +, -, , /

Relační operátory

V BETĚ jsou pro základní vzory definovány relační operátory =, <>, <, >, <=, >= s obvyklým významem. Pro objekty boolean platí, že false je menší (a nikoli rovno) než true. Pro objekty char platí relace dle ASCII.

Počáteční hodnoty

Instance základních vzorů jsou implicitně inicializovány. Počáteční hodnota pro integer respektive real je 0, pro boolean false a pro char znak null. Pomocí základních vzorů lze deklarovat pouze statické odkazy (proměnné), což je výjimka z důvodu optimalizace generovaného kódu. Jiná možnost, o které se dozvíme dále, totiž dynamická proměnná (odkaz), vyžaduje použití objektů se všemi náležitostmi a tudíž i režií. Tyto jsou definovány v základní knihovně betaenv pod jmény integerObject, realObject, atd.

Složené typy

Vzor odpovídající jednoduché datové struktuře (jako např. record v Pascalu) může vypadat takto:
alltt874
statickou deklaraci jeho instance (objeku vytvořeného dle vzoru planeta), lze provést například:
alltt876
Nyní lze přistupovat k jednotlivým atributům pomocí vzdáleného přístupu prostřednictvím tečkové notace např.:
alltt878

Singulární objekty

Předpokládejme nyní, že Země je jedinečný objekt, s jedinečnými vlastnostmi, a popišme jej například takto:
alltt880
Takovémuto objektu budeme říkat singulární objekt (singular object). Strukturu objektu takto přímo definujeme popisovačem objektu.

V případě, že potřebujeme definovat objekt, který je jediný svého druhu, není třeba zavádět třídu (vzor), tak jako je tomu ve většině jiných programovacích jazyků (koncept singulárních objektů je podobný mechanismu anonymních typů známých z jazyka Pascal). Možnost zavádění takovýchto beztřídních (class-less) objektů je jedním z důležitých rysů jazyka BETA, neboť má značný význam z hlediska analýzy a návrhu programového systému.

Dynamické proměnné

Tak jako jsme se již několikrát setkali se statickým odkazem (zde například atribut zeme je statickým odkazem na příslušný objekt), tak existuje také dynamický odkaz (dynamic reference).

Statický odkaz odkazuje na stále stejný objekt, je tedy jakýmsi konstantním ukazatelem. Instance příslušného vzoru (zde planeta) je vytvořena během vytváření objektu, který obsahuje statický odkaz jako svůj atribut. Takto vytvořený objekt se nazývá statický objekt (static object) nebo také part-object, což vyjadřuje, že je součástí objektu jež obsahuje jeho deklaraci. Statické objekty jsou důležité pro modelování hierarchií typu celek-část.

Stejně tak je nutné mít možnost modelovat situace, kdy objekt obsahuje odkazy na jiné objekty, které nejsou jeho součástí, jakož i případy, kdy jedna reference může odkazovat postupně na různé objekty. Tyto požadavky mohou být splněny prostřednictvím dynamického odkazu.
Deklarace dynamického odkazu:
alltt882
satelit je jméno dynamického odkazu a planeta jméno vzoru, který dynamický odkaz kvalifikuje (typuje), což znamená, že satelit může odkazovat pouze na instance vzoru planeta (nebo jeho podvzorů - více viz příslušná kapitola podvzory). BETA je typový jazyk, její reference jsou takto typovány a díky tomu může být mnoho chyb v přiřazování referencí na objekty zachyceno již při překladu.
Nyní lze provést například přiřazení pomocí operátoru [] (jež vyjadřuje získání odkazu na objekt a jeho význam a použití je popsáno v následujícím textu):
alltt884
které přiřadí odkazu satelit odkaz na (statický) objekt zeme, což znamená, že objekt označený zeme bude nyní dostupný také pod označením satelit.
Stejně tak lze přiřazovat dynamické odkazy mezi sebou
alltt886
přičemž pak zeme, satelitodkaz označují stejný objekt.

Počáteční hodnota dynamického odkazu je NONE, což znamená odkaz na nic. Takovýto prázdný odkaz lze vytvořit také explicitně:
alltt888
přičemž jeho následné použití pro přístup k nějakému objektu, například
alltt890
způsobí chybu za běhu programu (pokud odkaz není NONE, je zajištěno již při překladu, že odkazuje na objekt mající příslušný atribut a tudíž nemůže dojít k chybě).

Dynamické vytváření objektů

V jazyce BETA je možno za běhu vytvářet nové objekty. Napíšeme-li
alltt892
znamená to, že se má vytvořit nový objekt (to vyjadřuje operátor &) jako instance vzoru planeta a odkaz na něj (vyjádřeno operátorem []) přiřadit do dynamické proměnné odkaz.

Je důležité pochopit rozdíl mezi &P&P[], kde P je nějaký vzor. První případ říká vytvoř instanci P a spust ji, zatímco druhý vytvoř instanci P (bez spuštění) a vrať odkaz na ni. Rozdíl mezi odkazem a hodnotou z hlediska přiřazování si ukážeme na následujícím příkladu: Mějme definovány
alltt894
a dynamické odkazy ukazující na dynamické objekty (díky provedení následujících příkazů)
alltt896
pak přiřazení
alltt898
provede přiřazení stavů objektů dle příslušných vstupních a výstupních částí (v případě, že by vzor objekt obsahoval také výkonnou část, pak také spuštění těchto objektů), zatímco
alltt900
pouze přiřadí odkaz z dynamické proměnné a do b, čímž budou obě odkazovat na jeden objekt. V tomto případě (pokud nebyl odkaz na objekt odkazovaný prostřednictvím b zachován ještě jinde) bude jeden z dynamicky vytvořených objektů nepřístupný. O uvolnění jemu vyhrazené paměti se v případě potřeby za běhu programu postará čistič paměti (garbage collector).  

V této souvislosti bývá užitečné použití singulárních objektů. Mějme deklaraci:
alltt902
kde object je nejvyšší vzor, zastřešující všechny možné vzory -- více viz kapitola o podvzorech. Dále mějme singulární dynamický objekt reprezentující posloupnost akcí, který bude vygenerován, ale nikoli spuštěn a odkaz na něj umístěn do proměnné akce.
alltt904
pak příkazem
alltt906
lze tento objekt spustit.

Struktura vzoru - shrnutí

Vzor je definován popisovačem objektu, který má obecně tuto strukturu:

(# <deklarace>
enter <seznam>
do <příkazy>
exit <seznam>
#)

Nejprve je seznam deklarací atributů -- odkazů a vzorů (proměnných a typů v obecnější terminologii), pak vstupní část, uvozená identifikátorem enter, se seznamem vstupních parametrů, výkonná část, uvozená identifikátorem do, obsahující posloupnost příkazů a výstupní část, uvozená identifikátorem exit se seznamem výstupních parametrů, tak jak jsme si již ukázali dříve. Všechny části jsou nepovinné.

Jednotlivé deklarace a stejně tak i příkazy se oddělují pomocí středníku. Seznam proměnných se uvádí v kulatých závorkách (pokud je v takovém seznamu jen jeden prvek, lze závorky vypustit) a jednotlivé proměnné se oddělují čárkou.

V kapitole o podvzorech podvzory se dozvíme, že popisovač objektu může mít ještě prefix.

Řídící struktury

Podmíněný příkaz

  Podmíněný příkaz v jazyce BETA umožňuje podmínku s více alternativami a jeho obecná syntaxe vypadá takto:

(if <výraz1>
// <výraz2> then <příkazy>
// <výraz3> then <příkazy>
...
else
 <příkazy>
if)

Symbol // vyjadřuje rovnost. Větev then (řádka uvozená symbolem //) může být jedna nebo více.
Nejdříve se vyhodnotí výraz1 a výsledek se porovnává s výsledky výrazů uvedených v jednotlivých then větvích. Jestliže platí rovnost ve více případech, vybere se libovolná ze splněných větví a provedou se příslušné příkazy. Pokud neplatí rovnost v žádném případě, spustí se příkazy z větve else, pokud existuje, neboť ta je nepovinná.
Příklad použití:
alltt908
Takto lze jednoduše zpracovávat příkazovou řádku programu. Slouží k tomu vzory definované v základní knihovně betaenv, noOfArguments, který vrací počet parametrů na příkazové řádce (včetně názvu programu) a arguments, pomocí něhož lze tyto parametry získat (parametr číslo jedna je název programu). Pokud je program spuštěn s jedním parametrem, vypíše se příslušný text následovaný zadaným parametrem, pokud je spuštěn se dvěma parametry, vypíše se o tom zpráva. V jiných případech (bez parametru, více než dva parametry) se použije větev else. Vzor putText má stejnou funkci jako putLine (výpis textu na obrazovku), ale nepřidává ukončení řádky.

Rozhodnutí na základě logické hodnoty lze provést například takto (případ false může být ekvivalentně řešen pomocí větve else):
alltt910
Zajímavější je použití pro větvení na základě několika logických výrazů, například:
alltt912

Jednoduchý podmíněný příkaz

V BETĚ existuje ještě jednoduchá varianta podmíněného příkazu   (simple if) odpovídající podmíněnému příkazu, tak jak se vyskytuje v jiných programovacích jazycích. Syntaxe je:

(if <výraz> then
<příkazy>
else
<příkazy>
if)

kde výraz musí nabývat logické hodnoty. Větev else je nepovinná. Příklad použití:
alltt914
Tento příklad využívá vzoru directory definovaného ve stejnojmenné knihovně. Přiřazením textu do atributu name objektu vytvořeného podle tohoto vzoru zadáme cestu k adresáři, který nás zajímá a pak pomocí atributu empty můžeme zjistit, zda tento adresář obsahuje nějaké položky. Poznamenejme, že u tohoto příkladu dojde v případě neexistence zkoumaného adresáře k vyvolání implicitní výjimky (kterou bychom mohli snadno předefinovat), která ukončí provádění programu vypsáním chybového hlášení.

Použití (běžného) podmíněného příkazu by vyžadovalo přidání // true na konec výrazu v podmínce.

Cyklus for

  Řídící struktura pro iterace se v BETĚ nazývá příkaz for (for imperative)   a má následující strukturu:

(for <proměnná>:<výraz> repeat <příkazy> for)

kde proměnná je název řídící proměnné cyklu, která je viditelná pouze uvnitř cyklu, nelze jí přiřazovat a je typu integer. Nejprve je vyhodnocen výraz, který určuje kolikrát jsou spuštěny příkazy, přičemž řídící proměnná cyklu se postupně zvyšuje o jedničku počínaje hodnotou 1.

Jednoduchý příklad, který sečte všechna čísla od 1 do 1000:
alltt916

Pokud nepotřebujeme řídící proměnnou cyklu, lze použít jednoduchý cyklus for, který má stejnou syntaxi, pouze je vypuštěna proměnná a dvojtečka. Pak jsou prostě příkazy provedeny tolikrát, kolikrát udává výraz. Tedy například:
alltt918
vypíše desetkrát ahoj.

Důležitá je v tomto kontextu myšlenka, že většina nejrůznějších řídících struktur (jako například cyklus for s určením spodní hranice řídící proměnné, apod.) může být vytvořena pomocí vzorů (tak jak tomu skutečně v základních knihovnách je a jak ještě uvidíme) a proto má BETA předdefinováno jen několik málo řídících struktur.

Návěští a příkazy leave a restart

    Návěští může být přiřazeno příkazu takto:

jméno: <příkaz>

Návěští jméno je dostupné pouze uvnitř pojmenované konstrukce.

Vykonávání pojmenovaného příkazu (může to být popisovač objektu obsahující skupinu příkazů) může být ukončeno pomocí příkazů leave nebo restart. První z nich, leave, způsobí skok za konec příkazu, jehož jméno je uvedeno za příkazem leave. Naopak vykonání příkazu restart způsobí znovuzapočetí vykonávání příslušného (jménem za příkazem restart daného) příkazu. Příklad použití těchto příkazů si ponecháme až na kapitolu o podvzorech podvzory.

Repetice

   Datová struktura podobná polím v ostatních programovacích jazycích se v BETĚ nazývá repetition. Tento termín byl zvolen pro odlišení od jiných programovacích jazyků, neboť datový typ repetition v sobě zahrnuje kromě konceptu pole (lineární posloupnost prvků téhož typu) také jisté operace (především pro dynamickou změnu velikosti datové struktury). Proto bude nadále používán jako český ekvivalent termínu repetition termín repetice.
Deklarace
alltt920
kde R je jméno repetice statických odkazů prvků popsaných vzorem P, které má velikost 10 prvků (na místě konstanty 10 může být libovolný výraz typu integer). Jednotlivé prvky lze odkazovat takto
alltt922
kde R.range je atribut, který má každá datová struktura repetice a pomocí kterého lze získat její velikost (počet prvků). Prvky jsou vždy číslovány od jedné.
Stejně tak lze deklarovat repetici dynamických odkazů
alltt924
kde každý prvek je odkaz na jednu instanci vzoru P. Zde tedy nebudou na rozdíl od předchozí repetice statických odkazů při jejím vytváření vytvořeny instance vzoru P, ale jen dynamické odkazy (inicializované na NULL).
Příklad definice vzoru reprezentujícího jednoduchý zásobník pro čísla:
alltt926
Pro uložení dat v zásobníku slouží repetice Data, proměnná Top reprezentuje aktuální vrchol zásobníku a vnořené vzory PushPop základní operace se zásobníkem. Využívá se implicitní inicializace proměnné Top na nulu, díky čemuž není nutno zásobník inicializovat. Postačí jej vytvořit jako statický odkaz a pak již ho lze používat. V následujícím příkladu
alltt928
jsou čísla vložená postupně (vzestupně) do zásobníku prvním cyklem druhým cyklem vybírána v opačném pořadí a tištěna na obrazovku (vzor newLine provede odřádkování).

Při pokusu o přístup k prvku repetice s indexem mimo rozsah 1 ...\ range dojde k vyvolání standardní výjimky, která ukončí běh programu s chybovým hlášením. To zde nastane, pokud

  1. se pokusíme do zásobníku vložit více elementů, než na kolik je dimenzován (konstanta 10 v deklaraci repetice Data)
  2. se pokusíme vybrat ze zásobníku data aniž bychom tam předtím příslušný počet elementů vložili
Výskyt výjimky z prvního důvodu eliminujeme v následujícím odstavci pomocí dynamické změny velikosti repetice.

Dynamická změna velikosti

Datová struktura repetice je pole s dynamicky měnitelnou délkou. Velikost repetice udávaná při deklaraci se chápe pouze jako počáteční velikost. Možnost dynamické změny velikosti je důležitá z hlediska použití datové struktury pro přímou implementaci prostředků pro ukládání dat, neboť z hlediska analýzy většina takových problémů vede na určité úrovni návrhu k potřebě dynamicky měnit velikost datových struktur. Například u našeho příkladu, zásobníku, může být výhodné, pokud při pokusu o vložení prvku bude zásobník v případě potřeby zvětšen namísto generování výjimky. Pak bude pro uživatele takového zásobníku zcela lhostejné, jakou (počáteční) hodnotu velikosti datové struktury zvolil ten, kdo jej implementoval.
Úprava definice zásobníku pro dosažení tohoto výsledku je poměrně jednoduchá (týká se pouze vzoru Push, jinak se nic nemění):
alltt930
Každá datová struktura repetice má atribut extend, který slouží ke zvětšení rozsahu. Pokud máme repetici Data o deseti prvcích (Data.range=10) a provedeme přiřazení
alltt932
pak se rozsah zvýší o přiřazenou hodnotu, tedy na Data.range=15, přičemž hodnoty prvků Data[1], Data[2], ... Data[10] zůstanou zachovány a nově budou k dispozici prvky Data[11], Data[12], ... Data[15], které dostanou počáteční hodnotu nula.

Jinou možností je vytvořit zcela novou datovou strukturu, pomocí atributu new, například
alltt934
vytvoří novou datovou strukturu repetice o rozsahu daném přiřazenou hodnotou (zde tedy 20 prvků), přičemž původní prvky budou nedosažitelné a nové prvky dostanou pořáteční hodnotu nula.

Pro datovou strukturu repetice je definováno vzájemné přiřazení. Předpokládejme deklarace:
alltt936
pak přiřazení
alltt938
má následující význam:
alltt940
tedy obsah repetice je překopírován s tím, že rozsah je náležitě přizpůsoben.

Řezy

  Pro datovou strukturu repetice je definován řez (repetition slice), pomocí kterého je možno vybrat část prvků repetice. Například
alltt942
zkopíruje do R2 pouze ty prvky R1, které mají index 3 až 5. Obecně přiřazení
alltt944
kde e1e2 mohou být libovolné výrazy typu integer je interpretováno jako
alltt946

Vzor text

  Vzor text slouží k reprezentaci řetězců znaků, neboli posloupnosti objektů typu char. Instanci vzoru text může být přiřazována konstanta například takto:
alltt948
Vzor text nepatří mezi základní vzory, proto může být použit k vytváření dynamických odkazů a jako nadvzor. Je definován v základní knihovně basiclib a má poměrně mnoho atributů sloužících k manipulaci s textem (viz [4]).

Podvzory

   Programovací jazyk BETA patří do rodiny objektově orientovaných programovacích jazyků a podobně jako v jiných objektově orientovaných jazycích, tak i v BETĚ lze vytvářet hierarchie vzorů (pokrývající jako zobecnění hierarchie tříd) sloužící k reprezentaci klasifikačních hierarchií. V této souvislosti budeme používat termín podvzor (subpattern) vyjadřující, že se jedná o vzor, který je specializací jiného vzorugif. Pro takovýto mechanismus se obvykle v objektově orientovaných jazycích používá termín dědičnost.

Vzpomeňme si nyní na vzor planeta, který by mohl vypadat takto:
alltt950
a vezměme vzor hvezda, který by mohl být deklarován takto:
alltt952
Takto definované vzory nám mohou sloužit pro reprezentaci příslušných pojmů a jejich vlastností, ale jak známo z objektově orientovaných metod návrhu a implementace počítačových systémů (viz například [2]), je výhodné zavést jistou klasifikační hierarchii těchto vzorů, využívající a podchycující jejich společné vlastnosti a vzájemné vztahy.

  
Figure: Znázornění příkladu jednoduché hierarchie vzorů


Za tímto účelem zavedeme zastřešující objekt shrnující společné vlastnosti všech objektů naší jednoduché hierarchie (viz obr. 1.1):
alltt954
Vzor planeta pak lze zavést jako specializaci (podvzor) vzoru teleso:
alltt956
Použitím vzoru teleso jako prefixu popisovače objektu pro vzor planeta dosáhneme toho, že popisovač objektu pro vzor planeta zdědí všechny atributy vzoru teleso. Takto objekty deklarované jako instance vzoru planeta budou obsahovat atributy velikost, hmotnostatmosfera.
Vzor hvezda deklarujeme takto:
alltt958
Vzory planetahvezda jsou podvzory vzoru teleso, zatímco ten je pro ně tzv. nadvzorem (super-pattern) . Tato terminologie je odrazem pohledu na klasifikační hierarchie z hlediska jejich významu pro generované instance. Definujeme-li vzor hvezda jako podvzor vzoru teleso, popisujeme dodatečné vlastnosti, v tomto případě je to svitivost. Lze řící, že množina všech instancí vzoru hvezda je podmnožinou množiny všech instancí vzoru těleso. Obecně přidáním nových vlastností k vlastnostem nadvzoru zmenšíme množinu všech možných instancí podvzoru a tato množina je podmnožinou množiny všech možných instancí příslušného nadvzoru.

Abstraktní nadvzory - kvalifikace odkazů

Jak již bylo řečeno dříve, dynamické odkazy v BETĚ jsou typovány (kvalifikovány) na určitý vzor, který určuje množinu objektů, na kterou se lze odkazovat. Z tohoto hlediska mají klasifikační hierarchie značný význam, neboť odkaz typovaný na nějaký nadvzor může odkazovat na libovolný objekt z množiny instancí všech jeho podvzorů.

Vzory jako teleso bývají nazývány abstraktní nadvzorygif, což vyjadřuje, že neslouží pro vytváření instancí, ale pouze jako nadvzory. Mezi jejich využití patří také vytváření univerzálních odkazů. Například:
alltt960
může odkazovat na libovolnou instanci vzorů planetahvezda (pokud by takové instance existovaly, může samozřejmě odkazovat také na instance vzoru teleso) a všech jejich případných podvzorů.

Kvalifikace dynamického odkazu také určuje, které atributy mohou být odkazovány vzdáleným přístupem. Například
alltt962
je legální a bude odkazovat na příslušný atribut, ať už R odkazuje na instanci jakéhokoli podvzoru vzoru teleso, neboť každý takový podvzor je rozšířením vzoru teleso a obsahuje tudíž zaručeně všechny jeho atributy. Legální jsou tedy vzdálené přístupy právě k těm atributům, které jsou atributy vzoru, jímž je příslušná dynamická proměnná kvalifikována.

Mějme nyní deklaraci:
alltt964
neboli dynamický odkaz P, který může odkazovat na instance vzoru planeta.
Pak je možné provést přiřazení
alltt966
neboť množina objektů, na které může odkazovat P je podmnožinou množiny objektů, na které může odkazovat R. Zatímco přiřazení
alltt968
je možné provést pouze v případě, že R odkazuje instanci, kterou může odkazovat i P, v našem případě instanci vzoru planeta. V tomto případě však nelze o legálnosti přiřazení rozhodnout při překladu. Proto je takovéto přiřazení při překladu pokládáno za správné, avšak překladač sem umístí kontrolu, která případně způsobí chybu za běhu programu (tak je zajištěna správnost vzdálených přístupů k atributům, neboť do dynamického odkazu nelze přiřadit odkaz na objekt, který nemá příslušné atributy).

Testování příslušnosti objektů ke vzorům lze provádět také explicitně. Například kontrola před přiřazením z minulého odstavce by mohla vypadat takto:
alltt970
Výraz R## odkazuje na vzor objektu na který ukazuje R a výraz planeta## znamená vzor planeta (více viz kapitola popisující vzorové proměnné vzorprom).
Poznamenejme, že test by ve skutečnosti vypadal spíše R## <= planeta##gif, neboť obecně lze přiřazovat do proměnné P odkazy nejen na instance vzoru na který je kvalifikována, ale i všech jeho podvzorů.

Využití singulárních objektů

Analogicky výše uvedeným informacím o singulárních objektech lze definovat singulární objekt také pomocí popisovače objektu s prefixem, tj. s využitím specializace:
alltt972
Zeme takto má všechny vlastnosti společné planetám a jednu, život, navíc.

Vzor object

O vzoru object jsme se již také zmínili: je to nejobecnější abstraktní nadvzor. Navíc, vzor object je také implicitní nadvzor všech vzorů, které nemají nadvzor definován, neboli pokud uvedeme popisovač objektu bez prefixu, je to interpretováno jako popisovač objektu s prefixem object. Takto jsou všechny vzory podvzory vzoru object.

Procedurální aspekty podvzorů

Výkonná část vzorů může být také děděna a specializována od nadvzorů k podvzorům.
Mějme
alltt974
kde příkaz inner P1 slouží k řízení specializace akcí mechanismem uspořádaného přidávání dalších akcí -- určuje místo, do kterého bude umístěn kód případných podvzorů specializujících vzor P1. Pro instance vzoru P1 má tedy příkaz inner P1 prázdný význam. Avšak vzor
alltt976
takto definuje pro svoje instance výkonnou část odpovídající
alltt978
neboli výkonná část byla zděděna z nadvzoru a na místo definované v nadvzoru příkazem inner přidány akce z výkonné části příslušného vzoru. Obecně vykonávání akcí instance určitého vzoru začíná ve výkonné části jeho nejvyššího nadvzoru (vzato do důsledku, je to vždy vzor object, který obsahuje ve své výkonné části pouze příkaz inner) a postupně (v místech určených umístěním příkazu inner) přechází do výkonných částí jednotlivých podvzorů, až do daného vzoru a pak stejným způsobem (tentokrát zdola nahoru) vykonává zbytky jednotlivých výkonných částí (uvedené za jednotlivými příkazy inner).

Běžně se používá zkrácená verze příkazu inner, prostě bez uvedení vzoru, což znamená, že se týká bezprostředně obklopujícího popisovače objektu.

Se způsobem dědění a rozšiřování atributů objektů od nadvzorů k podvzorům jsme se již seznámili. V této souvislosti však je třeba ještě říci, že všechny atributy deklarované v nějakém vzoru jsou viditelné ve všech jeho podvzorech.

Mechanismus specializace akcí se dá v kombinaci se singulárními objekty použít pro vytváření řídících struktur. Nejjednodušší příklad, který nám zároveň ukáže použití příkazů leaverestart, je tento:
alltt980
Vzor cycle lze jako nekonečnou smyčku (singulární objekt reprezentující nekonečnou smyčku) použít jednoduše takto:
alltt982
Takovou smyčku lze opustit příkazem leave cycle.

Je jasné, že uvnitř popisovače objektu popisujícího singulární objekt nemá příkaz inner smysl, neboť takový popisovač objektu nemůže mít podvzor, který by dal příkazu inner smysl.

Častým využitím výše naznačené techniky jsou mnohé iterátory definované v různých datových strukturách. V tuto chvíli uveďme jednoduchý příklad, který vypíše obsah adresáře pomocí atributu scanEntries vzoru directory (se kterým jsme se již setkali):
alltt984
Objekt vytvořený podle vzoru scanEntries prochází postupně všechny položky příslušného adresáře, odkaz na ně přichystá do proměnné s určitým názvem a strukturou (found, více viz [4]), načež provede příkaz inner, neboli spustí příkazy uvedené v popisovači singulárního objektu, jež je jeho potomkem.

Mnoho dalších případů využití singulárních výkonných objektů lze najít v knihovně containers (viz [7]), kde jsou definovány základní datové struktury jako například zásobník, fronta, seznam, tabulka s rozptýlenými hesly (hash), .... Ve složitějších případech se často využívají vlastnosti virtuálních vzorů o kterých pojednává samostatná kapitola virtvzory, ve které lze také nalézt další příklady použití.

Vstupní a výstupní část podvzoru

Mějme:
alltt986
pak vzor P2 má vstupní část (a,b,c) a výstupní část (z,x,y), neboli vstupní část je spojením vstupní části nadvzoru a vstupní části příslušného vzoru. Totéž platí o výstupních částech.

Virtuální vzory

  Pomocí podvzorů je možno popisovat vlastnosti a strukturu objektů specializací tohoto popisu z jiných vzorů (nadvzorů). Pomocí virtuálních vzorů  (virtual patterns) je možno popsat obecné vlastnosti určitého atributu v nadvzoru a tento popis specializovat v podvzorech.

Předpokládejme, že potřebujeme pro vzory v naší hierarchii těles definovat atributy popisující akci vytiskni na obrazovku informace, které jsou o příslušném objektu k dispozici. Bez nároků na propracování detailů zobrazení lze zavést následující definice:
alltt988
kde je dosaženo požadované funkce, neboť každý ze vzorů teleso, planeta, hvezda má atribut tisk vypisující příslušné informace, přičemž pokud zavedeme například odkaz
alltt990
pak provedení
alltt992
vykonná příslušnou činnost v závislosti na tom, kterého vzoru je instancí objekt jež R právě odkazuje. Způsob, jakým je toho dosaženo popisují následující poznámky:

Příklad použití virtuálních vzorů v řídících strukturách

V základní knihovně betaenv je definována následující řídící struktura:
alltt1000
která by se dala považovat za zobecnění řídících konstrukcí whilerepeat, jak jsou známy z jiných jazyků.

V tomto příkladu je také vidět ještě jednu možnost kvalifikace virtuálního vzoru, totiž formou P (# ... #), neboli popisovačem objektu specializujícím nějaký vzor P. Nám již známá možnost, přímá kvalifikace pomocí popisovače objektu, je vlastně speciální případ, kdy implicitně P = object.

Uvedenou řídící strukturu lze jednoduše používat, například takto
alltt1002

Příklad použití virtuálních vzorů pro obecné struktury pro manipulaci s daty

Vraťme se nyní k definici datové struktury zásobník a pokusme se definovat (generický) zásobník, který umožní vytvářet zásobníky objektů libovolného typu:
alltt1004
Virtuální vzor element určuje typ objektů, které lze ukládat do zásobníku. Protože je kvalifikován vzorem object, může být prvkem zásobníku instance libovolného vzoru. Mezi operace zásobníku byla přidána (pro zásobník zcela netypická) operace ForAll, která dovoluje procházet všechny objekty uložené v zásobníku a provádět s nimi nějaké operace. Tento mechanismus je často využíván v knihovnách pro operace iterativního charakteru, příkladem může být operace scanEntries vzoru directory jež byla použita v předcházejícím textu.

V podvzorech vzoru stack můžeme upřesnit typ prvků, které budou ukládány do zásobníku. Definujeme například zásobník pro instance vzoru hvezda:
alltt1006
pak do zásobníku hvezdy lze vkládat pouze objekty vzoru hvezda nebo jeho podvzorů. Pak také lze napsat:
alltt1008
pro vypsání všech dostupných informací o všech objektech uložených v zásobníku. Protože atribut Current je kvalifikován virtuálním vzorem element, který v objektu hvezdy znamená vzor hvezda, lze přistupovat k atributu tisk.

Vzor Stack by mohl být kořenem hierarchie vzorů pro zásobníky různých typů objektů, což by bylo výhodné například pro postupné zavádění dalších operací nad zásobníkem tak, jak se specializují vlastnosti potenciálně ukládatelných objektů.

Vzorové proměnné

  Vezměme nyní příkaz &P. Jak jsme se již dozvěděli, tímto příkazem se vytvoří a spustí nová instance vzoru pojmenovaného P. Pokud je P deklarováno jako definice vzoru
alltt1010
pak je jednoznačně určeno, jaký vzor se pod názvem P skrývá. Pokud je P deklarováno jako definice virtuálního vzoru, například
alltt1012
pak může P znamenat různé vzory (omezené však na podvzory vzoru R) podle toho, jak je P rozšiřováno v podvzorech vzoru T.

Navíc, v jazyce BETA, je k dispozici mechanismus zvaný vzorová proměnná   (pattern variable) pro vytváření proměnných, které mohou odkazovat na různé vzory, jež jim mohou být přiřazovány za běhu programu.

Deklarací
alltt1014
definujeme vzorovou proměnnou S kvalifikovanou vzorem P. Vzorové proměnné mohou být přiřazovány libovolné vzory (nikoli instance), které jsou podvzory vzoru P (nebo sám P).
Máme-li podvzory
alltt1016
pak
alltt1018
přiřadí vzor P1 do vzorové proměnné S. Příkaz &S potom vytvoří a spustí instanci vzoru P1, ale provedeme-li
alltt1020
pak &S vytvoří a spustí instanci vzoru P2.

Získání struktury objektu

V jazyce BETA je pro libovolný objekt možné získat odkaz na jeho strukturu, tj. na strukturu podle níž byl vytvořen. Pokud se nejedná o singulární objekt, získáme takto odkaz na vzor, jehož je objekt instancí. Odkaz na strukturu získáme
alltt1022
kde R je odkaz na objekt.
Mějme
alltt1024
pak R1## je vzor P (odkaz na vzor P lze jinak získat zápisem P##), R2## je struktura P(# ... #)R3## je odkaz na příslušnou strukturu, podle toho na jaký objekt R3 právě odkazuje.

Relační operátory

Pro vzorové proměnné jsou definovány relační operace. Například

S## = R3##
- levá i pravá strana jsou stejný vzor
S## < R3##
- levá strana je podvzor pravé strany
S## <= R3##
- levá strana je podvzor pravé strany nebo jsou obě stejný vzor

Jednoduchý příklad použití ukazuje jak lze snadno zjistit, zdali je nějaký virtuální vzor v aktuální instanci rozšířen, či nikoli:
alltt1026

Možnosti vzorových proměnných

Prostřednictvím vzorových proměnných lze se vzory provádět tytéž operace jako s hodnotami jiných typů, lze je přiřazovat proměnným, předávat jako parametry procedurám či vracet jako hodnoty funkcí.

Pomocí vzorových proměnných je možno měnit chování objektů za běhu programu. Použijeme-li vzorovou proměnnou jako atribut nějakého vzoru, lze dosáhnout různé funkce tohoto atributu v různých instancích vzoru a dokonce tento atribut zcela předefinovat.
Například:
alltt1028
má každá instance vzoru T atribut S jinak specializován (může se jednat i o plné předefinování, neboť P může být object). Za běhu programu lze dále libovolně měnit chování objektů, například takto:
alltt1030

Modularizace

  Má-li být programovací jazyk vhodný pro vytváření použitelných a udržovatelných netriviálních aplikací, musí být k dispozici mechanismus pro rozdělení zdrojových textů na menší části, moduly. Toto tvrzení je nepochybně dostatečně známé, případně několik dobrých důvodů pro ně lze najít například v [1] na straně 261.

Pro programovací jazyk BETA byl pro popis modularizace vyvinut a použit tzv. jazyk fragmentůgif   (fragment language), nikoli však jako součást jazyka BETA. Jazyk fragmentů popisuje strukturu programu, zatímco jazyk BETA popisuje strukturu a činnost procesu tímto programem definovaného. Navíc jazyk fragmentů je nezávislý na jazyce BETA a principiálně by mohl být použit spolu s většinou programovacích jazyků.

Základní myšlenkou jazyka fragmentů je využití gramatiky jazyka (BETA): Libovolná větná forma (sekvence terminálních a neterminálních symbolů definovaných gramatikou) může být modulem. Nejjednodušším případem je program složený z jednoho modulu, který obsahuje pouze terminální symboly. Modularizaci zavedeme tak, že v textu ponecháme některé neterminální symboly a větné formy popisující jejich expanzi umístíme do jiných modulů. Způsob složení takto definovaných větných forem do kompletního programu popisuje jazyk fragmentů.

Gramatiky

V této souvislosti je důležitý formální zápis bezkontextové gramatiky jazyka BETA, jež je uveden například v [3] v příloze Bgif.

Štěrbiny

Jazykem fragmentů je nutno popsat všechna místa ve větné formě, kde se nacházejí neterminální symboly, jež je třeba expandovat pomocí jiných modulů (větných forem). Takovému místu říkejme štěrbina  (slot). Protože v každé větné formě se může vyskytovat libovolný počet neterminálních symbolů stejné syntaktické kategorie, každý neterminální symbol musí mít přiřazen jedinečný název.
Zápis neterminálního symbolu (definice štěrbiny) vypadá takto:
alltt1032
kde SLOT je klíčové slovo, T jméno štěrbiny a A syntaktická kategorie příslušného neterminálního symbolu (odpovídá názvu neterminálního symbolu).

Vzpomeňme nyní definici zásobníku z kapitoly 1.4 a definujme
alltt1034
kde jsou namísto výkonných částí operací uvedeny štěrbiny. Jména štěrbin patří do jiného jmenného prostoru, než ostatní jména (náleží jinému jazyku). Jméno štěrbiny musí být unikátní vzhledem k celému programu.

Fragmenty

Větné formy je také třeba jednoznačně identifikovat, aby bylo jasné, které štěrbině náleží. Zaveďmě pojem fragment , označující strukturu definovanou jako:
alltt1036
kde F je název fragmentu, A syntaktická kategorie a větná forma je vlastní obsah fragmentu. Ta může obsahovat další neterminální symboly (štěrbiny).

Fragmenty lze organizovat do skupin, což je výhodné pro logicky provázané fragmenty sloužící společnému účelu. Každá skupina fragmentů má svoje jménogif   a obsahuje příslušné fragmenty prostě uvedeny za sebou v libovolném pořadí. Skupina fragmentů je (nikoli obecně) realizována formou jednoho souboru v souborovém systému a identifikována jménem tohoto souboru.
Mějme například soubor stackbody.bet obsahující:
alltt1038
neboli definující skupinu fragmentů. Klauzule ORIGIN  určuje skupinu fragmentů, která bude použita pro přiřazování fragmentů štěrbinám. Specifikuje, že fragmenty PushPop budou substituovány za odpovídající štěrbiny v souboru stackgif, který takové štěrbiny musí obsahovat.

Základní prostředí

Jak jsme již naznačili, první příklad uvedený na začátku kapitoly by měl obsahovat navíc jisté náležitosti:
alltt1040
Základní prostředí (nebo také základní knihovna), definující základní standardní vzory a zajišťující inicializaci a ukončení běhu programu se nazývá betaenv. Je uloženo v souboru, jehož jméno je udáno v příkazu ORIGIN výše uvedeného příkladugif a vypadá asi takto:
alltt1042
zkombinováním těchto fragmentů na základě příkazu ORIGIN dojde k dosazení fragmentu PROGRAM za příslušnou štěrbinu v základní knihovně (neboli každý program musí definovat fragment se jménem PROGRAM syntaktické kategorie popisovač objektu popisující jeho strukturu). Výsledek bude vypadat takto:
alltt1044
Standardní vzory uvedené v základní knihovně budou přístupné z fragmentu PROGRAM, neboť v něm jsou takto viditelná všechna jména, která jsou viditelná z místa, kde byla uvedena štěrbina PROGRAM.

Štěrbina LIB slouží pro vytváření knihoven ve smyslu souboru obecněji použitelných vzorů (za PROGRAM lze dosadit jeden popisovač objektu, za LIB seznam deklarací vzorů). Knihovnu vytvoříme například takto (pojmenujme ji knihovna):
alltt1046
Námi definované vzory jsou substituovány za štěrbinu LIB do základní knihovny, jsou viditelné pro štěrbinu PROGRAM a jsou jim přístupná všechna jména viditelná v místě štěrbiny LIB. Tento výsledný text však není program, neboť štěrbina PROGRAM zůstala bez expanze.

Pro smysluplné využití knihovny budeme potřebovat příkaz INCLUDE, který umožňuje kombinování několika fragmentů do jednoho:
alltt1048
Takto lze vzory definované ve fragmentu LIB používat ve fragmentu PROGRAM. Příkaz INCLUDE  určuje, že skupina fragmentů knihovna se stane logickou součástí souboru v němž je uveden. Proto budou nalezeny příslušné fragmenty pro štěrbiny LIBPROGRAM. Skupina fragmentů může obsahovat více než jeden příkaz INCLUDE.

Skrývání implementačních detailů modulů

Při vytváření modulů je nutné mít možnost přesně specifikovat rozhraní pro ostatní moduly a oddělit ho od vlastní implemetace modulu. K tomuto účelu slouží příkaz BODY . Ten uvádí skupinu fragmentů, kterou je ještě potřeba k nalezení expanze pro všechny štěrbiny aktuální skupiny fragmentů, ale která není součástí tohoto fragmentu, tudíž není viditelná tam, kde je viditelný tento fragment. Vraťme se k našemu zásobníku. Doplníme-li jeho definici takto:
alltt1050
jedná se o příklad definice rozhraní. Pomocí příkazu BODY je definována skupina fragmentů, která určuje implementaci zásobníku. Proměnné používané v implementaci zásobníku byly skryty zavedením atributu Private který je obsahuje (implementace musí být samozřejmě příslušně upravena, aby tyto proměnné odkazovala plným jménem, např. Private.Top)gif. Příkazů BODY může být ve skupině fragmentů více.

Poznámky

Další možnosti

V literatuře [1] lze najít diskuzi použití jazyka fragmentů pro podporu programových variant a různých implementací modulů. Podrobnější informace lze najít tamtéž. Problematika modularizace s ohledem na použití příkazu inner je diskutována v [3], kap. 6.2.

Implementace

Implementace ( BETA System) systému fragmentů je poněkud méně obecná než je patrno z výše uvedeného nástinu principů: Syntaktické kategorie pro štěrbiny jsou omezeny na DoPart, ObjectDescriptorAttributes. Jména souborů (skupin fragmentů) a odkazů na ně mají jistá omezení. Skupiny fragmentů mohou mít zadány vlastnosti, což jsou příkazy složící například pro řízení překladu a spojování generovaného kódu. Podrobnější informace lze získat především v [3] kapitola 6.

Poznámky

Bloková struktura

Zajímavým aspektem jazyka BETA je také obecná bloková struktura. Rozšiřuje vyjadřovací schopnost jazyka a dovoluje i značně specifické vyjadřovací konstrukce. Z rozsahových důvodů zde uveďme pouze jeden jednoduchý příklad pro ilustraci. Mějme vzor
alltt1052
a instance
alltt1054
pak a.X i b.X jsou vzory, ale každý jiný. Použijeme-li tyto vzory pro vytvoření instancí, budou to instance navzájem různých vzorů. Více viz [1], kapitola 8.

Konkurence

  Programovací jazyk BETA vychází z tradice jazyka Simula. Jedním z důležitých vlastností inspirovaných touto tradicí je systém založený na korutinách pro realizaci modelování konkurenčních aktivit. Více viz [1], kapitoly 13-15 a [2].

Persistence a distribuce objektů

Součástí vývoje implementace jazyka BETA () je také podpora pro zajištění persistence objektů, o které lze získat informace v [2] kapitola 12 a v [11]. Další oblastí, na kterou je zaměřen vývoj, je podpora pro vytváření distribuovaných aplikací. O současném stavu knihoven pro distribuci objektů pojednává [12].

Výjimky

V předcházejícím textu jsme se několikrát setkali s pojmem výjimka. Realizace podpory výjimek v jazyce BETA je popsána v [1], kapitola 16.

Další informace

Důležitou součástí jazyka BETA je filozofie programování, uvedená například v [1], kapitola 18. Mnoho dalších informací lze získat, kromě studia doporučené literatury, také v elektronické podobě. Vhodný startovní bod je domovská stránka jazyka BETA na http://www.daimi.aau.dk/tex2html_wrap_inline1658beta.

Základní principy jazyka BETA - shrnutí

  Programovací jazyk BETA patří mezi moderní objektově orientované jazyky. Byl vyvinut v simulovské tradici skandinávskou školou objektově orientovaného programování. Je však menší a výrazově schopnější než Simula. Jeho základní charakteristikou je mocný a jednotný abstrakční mechanismus zahrnující koncepty třídy, procedury, funkce, korutiny, procesu a výjimky. Základní stavební kámen jazyka BETA, představující tento abstrakční mechanismus, se nazývá vzor.

Podle vzorů se vytvářejí objekty (instance), které mohou za běhu programu vznikat a zanikat. Programovací jazyk BETA je spjat s používáním automatického čištění paměti.

Každý vzor se skládá z atributů, které reprezentují jeho části, či odkazy na jiné objekty. Stejně tak může obsahovat lexikálně vnořené definice jiných vzorů. Dále může každý vzor definovat svoji výkonnou část a prvky, jejichž hodnoty jsou předávány před respektive po spuštění objektu. Objekty BETY mohou mít funkci korutin, čímž je umožněno modelování alternujících sekvenčních procesů a procesů kvaziparalelních.

Vzory jsou uspořádány do hierarchie dědičnosti. Každý vzor definuje svůj nadvzor, jehož je specializací prostřednictvím definice dalších vlastností. Specializace výkonných částí je provedena mechanismem, kdy kód uvedený ve vzoru je umístěn do kódu nadvzoru na místo jednoznačně určené nadvzorem. Vzory mohou být virtuální a navíc BETA zavádí pojem vzorové proměnné, která umožňuje pohled na vzory jako na hodnoty, jež lze předávat jako parametry jiným vzorům a zacházet s nimi jako s běžnými hodnotami a tak umožnit například měnit dynamicky chování objektu.

Dále je pro jazyk BETA charakteristický silný typový systém a obecná bloková struktura. Z hlediska identifikace objektů je důležitá možnost vytváření objektů bez nutnosti zavádět vzor, což je výhodné pro případ, kdy existuje pouze jeden objekt svého druhu.

V souvislosti s jazykem BETA byl vyvinut prostředek pro zajištění modularizace nazvaný jazykem fragmentů. Základní myšlenkou jazyka fragmentů je využití gramatiky jazyka (BETA): Libovolná větná forma (sekvence terminálních a neterminálních symbolů definovaných gramatikou) může být modulem.


next up previous contents index
Next: Použité pojmy Up: Programovaci jazyk BETA - Previous: Contents

Jiri Sitera
Fri Feb 14 12:11:18 MET 1997