Josef Troch (troch@mail.ru)
25.03.2003 (poslední změna 01.04.2003)
Rock & Roll - deduktivní objektově orientovaný DB systém
Co to je?
Rock & Roll je deduktivní objektově orientovaný DB systém. Jeho domovská stránka je
tady, ale už přes 4 roky nebyla změněna.
Všechny odkazy na stažení Rock & Roll-u tam uvedené jsou neplatné, momentálně ho lze nalézt
zde.
Skládá se ze 2 jazyků:
- ROCK - procedurální jazyk pro definici schématu DB a manipulaci s daty.
- ROLL - deduktivní dotazovací jazyk.
Instalace, verze atp.
Rock & Roll je k dispozici pro tyto platformy:
- PC s MS-DOSem (či jeho emulací) - Rock & Roll v. 2.17 (květen 1995) - tato verse je
nepersistentní ("main memory version").
- Solaris se Sun OS v. 5.3+ - Rock & Roll v 4.00 (listopad 1998) - liší se od předchozí
verse 3.01 především přidáním podpory pro "spatial data handling facilities". Obě tyto
verse umí již fungovat i persistentně (ale každá k tomu něco potřebuje:
4.00 - ODI ObjectStore
Storage
Manager (komerční);
3.01 - Exodus Storage Manager).
Základy
Rock & Roll funguje jako interpreter. Po spuštění programu (rnr pro nepersistentní versi,
prnr pro persistentní versi) se objeví příkazová řádka, která akceptuje mimo jiné tyto příkazy:
help | Help:-) |
run soubor_s_programem | Zkompiluje a začne interpretovat daný program. |
compile jmeno_metody | Zkompiluje Roll metodu. |
execute jmeno_metody | Spustí Roll metodu. |
list_compiled | Vypíše detaily o zkompilovaných Roll metodách. |
quit | Opustí systém commitujíce změny. |
abort | Opustí systém abortujíce změny. |
Struktura programu
program <seznam parametrů>
begin
<definice>
<deklarace>
<vlastní tělo programu>
end
- seznam parametrů odpovídá seznamu "environments" (viz
níže) ke kterým má program přístup.
- definice - seznam definic typů a tříd.
- deklarace - seznam deklarací proměnných použitých ve vlastním těle programu.
- vlastní tělo programu - seznam příkazů tvořících program.
Rock
Rock se skládá z "data definition language" pro definici schémat a
"data manipulation language" pro správu dat. Dále podporuje interakci s uživatelem a souborové
I/O.
V Rocku se rozlišuje mezi:
- type - popis struktury objektů (jako class v C++)
- class - implementace objektu - metody
Pro každý type existuje právě 1 class a naopak.
Pozn.: Komentáře v Rock-u fungují vždy do konce řádku a začínají znakem '#'.
Type
- Instance primitivních typů se nazývají primary objects, instance typů definovaných přes
type secondary objects.
- Primitivní typy: int, real, string, bool.
- Definice (secondary) objektu - př.:
type zamestnanec:
specialises: osoba; #comma separated list předků -> vícenásobná dědičnost
properties: #následuje definice atributů - zase comma separated list!
public: #specifikátor přístupu: private/public - význam jako v C++
jmeno_firmy : string,
id_firmy : int;
private
plat : real;
public {zamestnanec}; #association (sémanticky třeba množina podřízených) - viz níže
ROCK: #jazyk, který má být použit k implementaci metod(y) (ROCK nebo ROLL)
spocti_podrizene_s_udanym_platem(plat : real) : int; #deklarace metody v Rock-u (private/public se zde neudává).
end-type
- Poznámky k příkladu:
- Jazyk umožňuje tři typy složených atributů (nazývají to zde "construction"),
před každou z nich se uvádí ještě private/public:
"association" - v postatě množina prvků téhož typu (primary/secondary
objects) - př.: public {int}
"sequentation" - pole či seznam prvků téhož typu, rozsah indexů se
neudává, v praxi může být i nespojitý. - př.:private [osoba]
"aggregation" - structura/záznam (či n-tice, chcete-li:-))
- př.:public <den: int, kdo: osoba, kolik: real>
V definici typu se může vyskytnout nejvýše jedna z těchto. Asi jediným kladným důsledkem
tohoto je to, že nemusí být pojmenované - "množinovou funkci" aplikujete přímo
na objekt obsahující tuto "construction".
- Metody: Za specifikátorem jazyka opět následuje comma separated list deklarací method,
jedna třída může mít metody v Rock-u i v Roll-u (viz níže uvedený
příklad).
Class
Nejdříve musí být deklarován příslušný type.
Příklad deklarace class i s příslušným type:
type date:
public <dd: int, mm: int, yy: int>;
ROCK:
print();
end-type
class date:
public: #až zde se deklaruje private/public
print() #signatura metody
begin
write get_dd()@self, ":", get_mm()@self(), ":", get_yy()@self;
end
end-class
Data Manipulation Language
- Přiřazení: x:=15;
- Definice proměnných - klasická bez inicializace - př.:
var i: int;
var realset {real};
var realseq [real];
- Definice proměnných - s inicializací - až na vyjímky není třeba uvádět typ, systém ho
uhádne z pravé strany.
var p := new osoba():
var intset := {2,3,1};
var xset := {}: int; #prázdná množina - typ nelze uhádnout, je třeba ho explicitně uvést
var intseq := ["one", "two", "three"]; #ekvivalentní s následujícím
var intseq :=[(3,"three"),(2,"two"),(1,"one")]; #indexy pro jednotlivé prvky lze udávat explicitně
- Volání metod - metody lze v potomcích předefinovávat, systém zvládá polymorfismus.
Syntaxe je dosti nezvyklá: jmeno_metody(par1, ..., parn)@objekt
- Nemá-li metoda parametry, závorky netřeba uvádět.
- Volání metod lze řetězit (samozřejmě, jen pokud to z typového hlediska dává smysl) - př.:
get_name@rodic@karel
- Chce-li objekt volat svou metodu: jmeno_metody(...)@self
- Přístup k atributům objektu: systém generuje pro každý atribut metody
get_.../put_.... Jiný přístup k atributům není možný ani uvnitř metod téže
třídy. - př.: put_plat(get_plat@p * 1,1)@p
- Vestavěné operátory pro primární objekty (v obvyklém významu):
+, -, *, /, mod, = (porovnání),
<>, <, >, >=, <=, and,
or, not, ^ (zřetězení) a další...
- Vestavěné operátory pro sekundární objekty:
new (bezparametrický - neinicializuje, možno nadefinovat uživatelské - parametrické),
delete
- Bloky - jako v Pascalu: begin ... end, na začátku bloku lze deklarovat
lokální proměnné
- If-then-else - podmínka musí být v (), else nepovinné - př.:
if (empty@c) then write "empty";
else write "non empty";
- While - př.: while (i<16) do seq[i] := i+2;
- Foreach - př.: foreach v in seq do write v; Takto lze iterovat přes
"association", "sequentation" i přes všechny prvky nějaké třídy (její jméno
by se napsalo místo seq).
- Načtení primárního objektu ze (implicitně standartního) vstupu: read v
(místo v by tu mohl být zase comma separated list).
- Zapsání (jako výše): write v (obdobně).
- Fůra metod pro práci s "associations" a "sequentations" - např.:
insert, remove, is_in, empty, .... K prvkům sequentation
lze přistupovat přes [].
Roll
Roll je deduktivní dotazovací jazyk odpovídající Datalogu se stratifikací.
- Dotazy se píší do hranatých závorek.
- Znak '|' se používá k oddělení projekce od těla dotazu.
- Řetězce se zapisují do "".
- Infixní operátor == označuje unifikaci, =\= neúspěch při
unifikaci.
- Prefixní symbol '~' znamená negaci.
- Při použití Rock proměnných v Roll-u je třeba tyto prefixovat '!'.
Dotaz
... může být 3 druhů:
- [| goal_set] - žádný "projection list" -> výsledek jen true/false -
př.: [| get_plat@P == M, M < 20000]
(dotaz, zda existuje zaměstnanec s platem < 20000)
Pozn.: Roll odhaduje typy logických proměnných z dotazů nebo metod - zamestnanec
je jediný typ mající metodu get_plat, takže ho to najde.
Kdyby mělo více tříd stejnojmennou metodu, bylo by třeba psát uvedený dotaz takto:
[| get_plat@P:zamestnanec == M, M < 20000]]
Takovýto dotaz lze v programu použít jako boolovský výraz - třeba jako podmínku v if-then-else
atp.
- [ any projection_list | goal_set] - hledá se pouze jedno řešení (jako v
Prologu) - př.: [any P get_plat@P:zamestnanec == M, M < 20000]]
Výsledek tohoto dotazu se dá přiřadit do proměnné typu zamestnanec (obecně je výsledkem
"aggregation" ("n-tice")) - a pak s ním manipulovat pomocí Rock-u.
- [ all projection_list | goal_set] - výsledkem jsou všechny objekty (resp. jejich
projekce) splňující pravou stranu nasypané do "association" (neexistuje-li žádný
splňující objekt -> prázdná "association"). Př.:
var partSet :{part};
partSet := [all P | get_mass@P:base_part == M, M < 5.0]
foreach p in partSet do ....
var nmSet := [all <M,N> | get_mass@P:base_part == M, M < 5.0, get_name@P == N];
foreach v in nmSet do ...
Pozn.: Zápis [ {projection_list} | goal_set] je ekvivalentní s výše uvedeným.
Dále k syntaxi
- ',' mezi goaly - konjunkce
- ';' - disjunkce (Pozn.: Mám-li dotaz s disjunkcí - každá klausule v ní musí
obsahovat všechny proměnné obsažené v "projection_list"-u)
- Jazyk je deklarativní, nikdo neříká nic o pořadí vyhodnocování.
- V Roll dotazech/metodách se dají používat všechny vestavěné operátory a metody, které
nemodifikují objekty a nemají side effecty.
Metody
- Deklarace - jako v Roll-u, jen se nerozlišují vstupní a výstupní parametry:
type muj_typ
....
ROLL:
metoda(A1, ... An)
...
end-type
- Kód metody - klasicky v class.
Tělo metody: množina pravidel ve tvaru klausulí, tělo klausule se skládá z subgoal-ů (podcílů),
nezáleží na jejich pořadí. Všechny proměnné z hlavy klausule musí být někde obsaženy i v těle
klausule.
- Klausule v těle metody můžeme psát takto:
method_name(A1, A2, ... An):- goallist - pak se na objekt, na který je fce volána
odkazujeme přes self. Př.:
class composite_part #třída composite part obsahuje "association" či "sequentation"
....
is_component(composite_part)
begin
is_component(P1) : P1 is_in self; #ptáme se, zda P1 je v té "association" či "sequentation"
is_component(P1) : P2 is_in self, is_component(P1)@P2;
end
end
- Druhá možná syntaxe je tato: method_name(A1, A2, ... An)@X:- goallist - v tomto případě
se na objekt, na který je fce volána odkazujeme přes X. Př. (sémanticky totožný s předchozím):
class composite_part
....
is_component(composite_part)
begin
is_component(P1)@P0 : P1 is_in P0;
is_component(P1)@P0 : P2 is_in P0, is_component(P1)@P2;
end
end
- Polymorphismus funguje i v Roll-u.
Tento příklad je počeštěnou a výrazně zjednodušenou verzí jednoho z příkladů dodávaných s Rock & Roll-em
verse 4.00. Kompletní kód tohoto programu lze nalézt zde.
Budeme mít velmi jednoduchou databázi informací o příbuzenských vztazích. Nejdřív nadefinujeme
typy/třídy objektů:
program osoba
begin
type osoba:
properties:
public:
jmeno: string,
vek: int,
otec: muz,
matka: zena;
ROCK:
vynasob_vek(times: int): int;
ROLL:
rodic(osoba),
sourozenec(osoba),
predek(osoba);
end-type
type muz:
specialises: osoba;
end-type
type zena:
specialises: osoba;
end-type
class osoba
public:
vynasob_vek(times: int) : int
begin
get_vek@self * times
end
rodic(osoba)
begin
rodic(F)@Self :- get_otec@Self:osoba == F;
rodic(M)@Self :- get_matka@Self:osoba == M;
end
predek(osoba)
begin
predek(G)@P :- rodic(G)@Z, predek(Z)@P;
predek(G)@P :- rodic(G)@P;
end
sourozenec(osoba)
begin
sourozenec(X)@Y :- X =\= Y, rodic(P)@X, rodic(P)@Y;
end
end-class
Teď bychom měli databázi nějak naplnit - vzhledem k tomu, že jsme nenadefinovali parametrický
konstruktor pro osobu, nezbývá nám, než to udělat nějak takto:
var adam := new muz();
var eva := new zena();
var abel := new muz();
# ...
put_vek(70)@adam;
put_vek(69)@eva;
put_vek(40)@abel;
put_jmeno("adam")@adam;
put_jmeno("eva")@eva;
put_jmeno("abel")@abel;
put_otec(adam)@abel;
put_matka(eva)@abel;
Teď by měl následovat vlastní program. V našem případě jednoduché dotazy, zabalené do nějakých vhodných
výpisů na obrazovku - zde je jeden vzorový, dále uvedu už jen pár dotazů
write "Predci pro jednotlive osoby", nl,
"Dotaz: [ {A} | predek(A)@!p ]", nl, nl;
foreach p in osoba do
begin
var ancs := [ {A} | predek(A)@!p ];
write "Předci pro: ", get_jmeno()@p, nl;
foreach a in ancs do
begin
write " ", get_jmeno()@a, nl;
end
... a na závěr programu by ho to chtělo ještě ukončit:
end
Nyní ty slíbené další dotazy:
Všechny dvojice <předek, potomek>: | [ {<A,P>} | predek(!A)@P ] |
Nepřímí potomci Adama: | [ {X} | predek(!adam)@X, ~ rodic(!adam)@X ] |
Všechny dvojice sourozenců: | [ {<X, Y>} | sourozenec(Y)@X ] |
Osoby, nejmenující se Adam: | [ all P | get_jmeno@P =\= Adam ] |
... a pokud nám stačí jen 1 taková: | [ any P | get_jmeno@P =\= Adam ] |
Lidé, jejichž dvojnásobek věku je menší než 80: (Jen jako příklad volání metody Rock-u.)
| [ {<A, P>} | vynasob_vek(2)@P == A, A < 80 ] |
Máte-li zájem o složitější příklad zkuste některý z příkladů dodávaných s Rock & Roll -em. V něm
ovšem nejspíš narazíte na "environments", o kterých se tu tedy raději letmo zmíním.
"Environment" je jakási kolekce instancí tříd (a zároveň informací o
tom, jak příslušná třída vypadá). Standartně se instance třídy "ukládají" do "run-time
environment"-u, který se po skončení interpretovaného programu vždy vymaže. Jiný
"environment" tedy potřebujete jen pokud chcete třídu v 1 programu nadefinovat/vytvořit
instance a v druhém programu ji/je použít. "Environment" vytvoříte příkazem
define_env jmeno_environmentu a použijete ho při spouštění programu takto: run jmeno_programu
jmeno_environmentu. Podrobnější informace lze dohledat v níže uvedeném manuálu.
Zdroje:
- Manuál (bohužel dosti špatný) k Rock
& Roll-u v. 4.00
- ...a nějaké ty vlastní zkušenosti:-)
Tento článek smí být používán libovolně, za předpokladu, že nebude modifikován a že
takto bude učiněno se zmínkou o autorovi a odkazem na stránku
http://jt.sf.cz, odkud by měla být dostupná aktuální verse
tohoto článku.
Snažím se, aby informace zde uvedené byly pravdivé a pokud možno přesné, nicméně nenesu žádnou
odpovědnost za to, že tomu tak opravdu je, ani za jakékoli škody, které by někomu v důsledku
případných špatných či nepřesných informací z tohoto článku mohly vzniknout.
Jakékoliv dotazy či připomínky mi můžete poslat mailem.
Zpět na stránku referátů a jiných výtvorů