Az adatsruktúrák új
típusok definícióival vannak megadva. Ennek a célnak az elérése
érdekében lehetõség van típusok definíciójára algebrai és
absztrakt típusdefiníciók révén. Így a típus specifikálja a
felhasználó által definiált adatstruktúra minden legális
elõfordulását. Az algebrai adattípussal az új adatstruktúra
(mint pl.: a rekord) rekurzívan definiálható, már létezõ
adatstruktúrák összekapcsolásával. Minden rekord tartalmaz egy
megkülönböztetõ adatkonstruktort és nulla vagy több más
objektumot, melyeket az adatkonstruktor argumentumainak nevezünk. Az
adatkonstruktor egy azonosító, mely egyértelmûen megadja az
objektum típusát. Az absztrakt adattípus lehetõvé teszi az
adatstruktúrák oly módon való létrehozását, hogy azok aktuális
implementációja rejtve legyen a felhasználó elõtt. Az ilyen
adatstruktúrákon csak az ún. absztrakt leírással specifikált
függvényeket lehet alkalmazni. Az ilyen függvények konkrét
definíciója akár a specifikációtól elkülönítve is megadható.
Algebrai adattípus
A (rekurzív) algebrai
adattípusok definíciója felsorolja az új adatkonstruktorokat az
argumentumaikkal együtt. Az algebrai típusdefiníciók minden
lehetséges fajtája egy egyedi megkülönböztetõ
adatkonstruktorral kezdõdik. Egy ilyen adatkonstruktor csak
egy algebrai típus egy alternatívájában fordulhat elõ. Példák
algebrai típus definíciókra:
day ::= Mon | Tue | Wed | Thu |
Fri | Sat | Sun
Ebben a definícióban
van elõre definiált típus (num), a felhasználó által
definiált algebrai típus (day, numlist, numtree) és a felhasználó
által definiált adat konstruktor (Mon,...,Sun, Nillist, Numlist,
Niltree, Node).
numlist ::= Nillist | Numlist num numlist numtree
::= Niltree | Node num
numtree numtree
Az adatkonsruktorok feltûnhetnek függvény
definíciók kifejezéseiben, melyek új objektumát hozzák létre a
specifikált algebrai típusnak; mintáiban, adott formájú struktúrák
kiválasztására vagy vetítõ függvények képzésére az
adatkonstruktor argumentumaihoz való hozzáférés érdekében. Algebrai
típus objektumainak konstruálása:
payment_day:: day
Az adatkonstruktort, mint mintát használva,
a következõ függvény a numtree tükörképét eredményezi:
payment_day = fri
tree1:: numtree
tree1 = Node 1 (Node 3 Niltree
Niltree) Niltree
mirror
Niltree = Niltree
Az algebrai adattípus definíciójában egy új megkülönböztetõ
konstruktor szükséges minden új típushoz és minden új
alternatívához. Ez lehetõvé teszi például általános Nil
definiálását. Minden új típushoz szükséges egy saját speciális
konstruktor a típus üres objektumának jelölésére.
mirror (Node n l r) = Node n (mirror r) (mirror l)
Az algebrai
típusok lehetnek polimorfak. Egy vagy több általános típusváltozó
használható paraméterként a típus definícióban. Polimorf típus
definíció:
tree* ::= Niltree | Node * (tree*) (tree*)
Ezt
a polimorf algebrai típus definíciót lehet használni például a
következõ módon:
numtree == tree num.
Ezen a
módon a fatípusok családját lehet definiálni (ez tartalmazza pl. a
következõ típusokat: tree num, tree bool, tree
(num->num) ...).
Fontos tudni, hogy elvben a Miranda minden
elõre definiált típusa definiálható polimorf algebrai típus
definíciókkal. Az elõre definiált típusok algebrai típus
definíciója:
bool ::= True | False
De az elõre definiált típusok algebrailag való
definiálása a gyakorlatban kényelmetlen és használhatatlan mind
idõben, mind méretre.
char ::= a | b | ...
[*] ==
list * ::= Nil | Cons * (list *)
(*,**) == tuple2 * ** ::= Tuple2 *
**
Absztrakt adattípus
A konkrét típusok (melyeket a ::= vagy a ==-vel
vezettünk be) kiegészítéseképp a Miranda lehetõvé teszi
absztrakt adattípusok megvalósítását is, melyek implementációja
rejtve marad a program többi része elõl. Rögtön egy példát
nézve lássuk a szabványos verem absztrakt megvalósítását jelen
esetben listák segítségével:
abstype stack * with empty::
stack *
Láthatjuk, hogy az
absztrakt adattípus definíciója két részbõl áll. Az elsõ
a deklarációs rész az abstype ... with ..., ahol a with utáni
neveket az absztrakt adattípus szignatúrájának nevezzük. Ezek a
nevek a látható felület az absztrakt adattípus és a program többi
része között. Ezek után néhány megszorítás következik a
szignatúrákban felsorolt nevekre, egyenletek formájában. Ezeket
hívjuk implementációs egyenleteknek. Az implementációs egyenleteknek
nem kell közvetlenül a megfelelõ abstype definíció mögött
állni, akárhol elhelyezkedhetnek a leírásban.
isempty::
stack *->bool
push::
*->stack *->stack *
pop::
stack *->stack *
top::
stack *->*
stack * == [*]
empty = []
isempty x = (x=[])
push a x
= (a:x)
pop (a:x) = x
top (a:x) = a