Absztrakt adattípus (ADT)
Az absztrakt adattípus vagy ADT olyan egység, amely több különböző típusú elemet tartalmaz és műveleteket definiál rajtuk.
Az ADT a C rekord típusához hasonlít, de tartalmazhat műveleteket is, mint a C++ osztályok. Lényeges eltérés a C++ osztályokhoz képest, hogy az ADT végleges és nincs polimorfizmus.
A deklarálás általános alakja:
identifier: adt {
adt-member-list
};
Az ADT tagjait (változókat és függvényeket) ugyanúgy deklaráljuk, mint egyebként.
Példa:
Inventory: adt {
id: string;
onhand: int;
cost: real;
value: fn(item: Inventory): real;
};
Ha egy ADT-t már deklaráltunk, a vele társított azonosító adattípus név lesz, ezért az ADT deklarációkat célszerű a modul deklarációs fájlokba (.m) írni.
Az ADT tagfüggvényeinek definíciója általában az implementációs fájlokban (.b) szerepel. A definíció megegyezik a Limbo függvények definíciójával, úgy, hogy a függvény neve elé odaírjuk az ADT nevét és a
. operátort:
Példa:
Inventory.value(item: Inventory): real
{
return real(item.onhand) * item.cost;
}
ADT típusú változó létrehozása:
Pl:
part1: Inventory;
ADT tagjait a
. operátorral érhetjük el, és ugyanúgy használhatjuk őket, mint a közönséges változókat és függvényeket.
Példa:
part1.id = "Widget";
part1.onhand = 250;
part1.cost = 4.23;
sys->print("Value On Hand: $%5.2f\n", part1.value(part1));
# a fenti értékeket használva a kiírás eredménye:
# Value On Hand: $1057.50
Ebben a példában ahhoz, hogy az ADT tagfüggvénye a tagadatokat használja, az ADT-re való hivatkozást explicit paraméterként át kell adni a függvénynek.
Hivatkozhatunk az ADT-re implicit módon is, a
self kulcsszó használatával. Hasonló a C++ és Java
this kulcsszavához.
Példa:
Inventory: adt {
id: string;
onhand: int;
cost: real;
value: fn(item: self Inventory): real;
};
Inventory.value(item: self Inventory): real
{
return real(item.onhand) * item.cost;
}
sys->print("Value On Hand: $%5.2f\n", part1.value());
# a korábban beveztett értékeket használva az utasítás eredménye a köv. kiírás: Value On Hand: $1057.50
Referencia ADT (ref ADT)
A ref ADT olyan ADT, melyet referencia típusként hozunk létre.
Például az Inventory ADT-t referencia típusként a köv. módon hozhatjuk létre:
part2:= ref Inventory;
# ez az utasítás új Inventory-t hoz létre, és a part2 referencia lesz rá
A
part2 hivatkozás a reguláris ADT-hez hasonlóan használható.
Példa:
part2.id = "Whatsit";
part2.onhand = 1000;
part2.cost = 0.17;
Fontos különbség a
part és
part2 között, hogy nem azonos típusúak. Így pl. a következő utasítások fordítási hibát eredményeznek:
part2 = part1; # típus ütközés
part2.value(); # paraméter típusa nem megfelelő
Ahhoz, hogy a
ref ADT-ből elérjük az eredeti ADT értékeit, a (
*) prefix operátort használhatjuk:
part1 = *part2; # helyes
(*part2).value(); # helyes
Megoldható, hogy az ADT tagfüggvények
ref ADT típusokat is elfogadjanak. Ehhez a deklarációban a
ref kulcsszót kell használni.
Példa:
clear: fn(item: self ref Inventory);
A tagfüggvény definíciója a köv.:
Inventory.clear(item: self ref Inventory)
{
item.onhand = 0;
}
A
ref ADT legnagyobb előnye a sima ADT-vel szemben, hogy másolás nélkül függvényparaméter lehet, így a kód hatékonyabb (ez főleg nagy ADT-k esetén jelentős). Az
ref ADT-t elfogadó tagfüggvények felülírhatják argumentumaik tartalmát, ami érték ADT-k esetén nem tehető meg.
A
ref ADT előnye egyben nagy hátránya is, ugyanis az értékek felülírásának lehetősége miatt a
ref ADT interfész homályosabb, zavarosabb az érték ADT felületnél.
Pick ADT
A pick ADT hasonló a C union típusához. Az egyszerű ADT deklaráció különböző típusú ADT-kre példányosítható. A pick ADT a különböző példányokban szereplő közös adatok,
az egyes példányokra specifikus adatok és tagfüggvények együttese.
A pick ADT két részből áll:
Pick deklaráció általános formája:
identifier: adt {
...
pick {
tag-identifier =>
declaration;
...
tag-identifier =>
declaration;
...
}
};
Minden változatnak van egy ún.
tag-identifier-je, ami lokális az
ADT-re nézve, és deklarációk írhatók bele.
Példa:
Apick: adt {
str: string;
pick {
String =>
val: string; # sztring elem
Int =>
val: int; # egész elem
Real =>
val: real; # valós elem
Nothing =>
; # üres elem
}
};
A példában szereplő ADT közös adattagja a
string típusú
str. A
pick elemek a
String,
Int és
Real, melyek az
Apick
ADT három változatát specifikálják.
Ha az ADT-nek van közös adattagja, a
pick blokknak utolsóként kell szerepelnie az ADT deklarációjában, csak a tagfüggvények deklarációja állhat utána.
A
pick ADT-t
ref ADT-ként kell példányosítani, és meg kell adni a specifikus
elemet.
Példa:
r := ref Apick.Real;
Amikor az ADT adattagjához értéket rendelünk, a
pick utasításban deklarált változót használjuk:
Példa:
r.str = "Pi";
r.val = 3.141593;
ADT példány létrehozása és a kezdeti értékadás a következő formában történik:
Pl.:
s := ref Apick.String("Greeting", "Hello, World!");
A
tagof operátor segítségével lekérdezhetjük egy meghatározptt ADT példányban lévő
pick elem indexét. Az elemek 0-tól indexelődnek.
Példal:
tagof r; # az Apick ADT r példányának indexével, vagyis 2-vel tér vissza
A
pick szerkezet lehetővé teszi a
pick ADT különböző típusain alapuló
utasítások kiválasztását. Általános formája:
t: ref Apick = s;
pick u := t {
String =>
sys->print("%s is %s\n", u.str, u.val);
Int =>
sys->print("%s is %d\n", u.str, u.val);
Real =>
sys->print("%s is %f\n", u.str, u.val);
Nothing =>
sys->print("%s is Nothing\n", u.str);
}
A
pick utasítás az ADT-re való ( ún.
refadtinst) referencia lokális másolatát
(
localinst) használja. Az elem(ek) a
pick ADT-ben deklarált elemek közül való(k).Pl.:
printval(a: ref Apick) {
pick t := a {
String =>
sys->print("%d: %s\n", tagof t, t.val);
Int =>
sys->print("%d: %d\n", tagof t, t.val);
Real =>
sys->print("%d: %f\n", tagof t, t.val);
Nothing =>
sys->print("%d: Nothing\n", tagof t);
}
}
A
pick utasítást megelőzően az
s példány
ref ADT-jének kezdeti értéket adunk. Ezután a
pick utasítás kiválasztja az ADT különböző típusai alapján a futtatandó
utasításokat.
A végrehajtás egy másik módja, amikor a
pick utasítást egy függvényben helyezzük el, melynek paraméterül adható a
ref ADT.
Példa:
printval(a: ref Apick) {
pick t := a {
String =>
sys->print("%d: %s\n", tagof t, t.val);
Int =>
sys->print("%d: %d\n", tagof t, t.val);
Real =>
sys->print("%d: %f\n", tagof t, t.val);
Nothing =>
sys->print("%d: Nothing\n", tagof t);
}
}
A függvény meghívásakor paraméterül adjuk az ADT
pick elemet specifikáló példányát:
printval(s);
Másik lehetőség, hogy a fenti függvényt az ADT tagfüggvényeként deklaráljuk. Ekkor az ADT implicit paraméterként használható.
Példa:
Apick.printval(v: self ref Apick)
{
pick t := v {
String =>
sys->print("%d: %s\n", tagof t, t.val);
Int =>
sys->print("%d: %d\n", tagof t, t.val);
Real =>
sys->print("%d: %f\n", tagof t, t.val);
}
}
Ekkor a függvényhívás a következő:
s.printval();