V. További class-ok

Set, vagyis halmaz. A halmazokban rendezetlenül tároljuk az elemeket. Az elemeket nem indexeiken keresztül találhatjuk meg, hanem csak azt tudhatjuk meg, hogy egy adott elem szerepel-e a halmazban, vagy nem. Minden elem csak egyszer szerepelhet (vagyis "vagy benne van a halmazban, vagy nem"). A halmaz tehát arra jó, hogy egy elemrôl eldöntsük, hogy része-e egy halmaznak vagy nem, ez általában olyankor használható, amikor meg kell jegyezni hogy egy objektumról "volt-e már szó vagy nem".

Technikailag a halmaz egy speciális tábla (a Table Class-ból örökölte a metódusait), ahol az elemek indexei az elemek. Így annak eldöntése, hogy szerepel-e egy elem annyit jelent, hogy megvizsgáljuk, hogy az elemhez tartozó index .NIL-t tartalmaz-e, vagy magát az elemet. Persze lehetôség van a "sokkal szebb" hasindex metódus használatára, ami igaz értéket ad vissza akkor, ha van ilyen indexû elem a halmazban, és hamisat egyéb esetben. Emellett persze minden halmaz rendelkezik a tábla osztály metódusaival is, amibôl nyilván a halmazmûveletek (difference, union, intersection, xor és subset) számunkra a legfontosabbak. Az elemek halmazban való elhelyezésére itt a put metódus praktikusabb, mint a halmaz[elem]=elem forma, ugyanis ez utóbbinál kötelezô az, hogy az index és a hozzárendelt érték megegyezzen, egyébként hibajelzés a jutalmunk.

Bag, magyarul zsák. A zsákban rendezetlenül tároljuk az elemeket. Az elemeket nem indexeiken keresztül találhatjuk meg, hanem csak azt tudhatjuk meg, hogy egy adott elem szerepel-e a zsákban, és ha igen, hányszor. Ebbôl következik hogy a zsákban szerepelhet több egyforma elem is. A zsák technikailag a Relation Class édesgyermeke, így annak metódusait kapta örökségül. A halmazhoz hasonlóan itt is arról van szó, hogy az elemek saját maguk indexei, vagyis itt is érvényes az, hogy például közvetlen értékadásnál az indexnek és a hozzárendelt tartalomnak egyeznie kell. Az elemeket itt is a put metódussal a legegyszerûbb bedobálni a zsákba. Azt, hogy hány darab objektum van a zsákban az items metódussal tudhatjuk meg, és ugyanezt használhatjuk annak eldöntésére hogy egy adott fajta objektumból hány darab található a zsákban. Hogy egy objektum egyáltalán a zsákban van, azt - azon kívül, hogy az at illetve a [ ] paranccsal megvizsgáljuk, vagy az items-nél megnézzük hogy nulla darab van-e belôle - megtudhatjuk a hasindex nevû, a halmaznál már megismert metódussal, ami a legkényelmesebb módja ennek. A zsákoknál használhatjuk a relation metódusait is mindenféle vizsgálódásra - több-kevesebb haszonnal. Felhasználási területe a halmazokéhoz hasonló, olyan esetekben, amikor azzal ellentétben azt is nyilván kell tartani hogy az adott objektum hányszor került a figyelem középpontjába, sorrendre való tekintet nélkül.

Végére értünk hát a kupacoknak. Látható, hogy igen gazdag, sokrétû eszköztárral állunk szemben. Ezek használatával sok bonyolult feladatot oldhatunk meg egyszerûen és látványosan (és az sem utolsó szempont, hogy sokkal gyorsabban, mintha azt egy REXX programrészlettel végeztetnénk el).

Így, hogy már ismerjük a különbözô fajtákat, tegyünk említést azokról a lehetôségekrôl, melyek minden kupacra alkalmazhatóak. Elôször is megemlítem az egyenlôség-vizsgálatokat (=, ==, \= és társai), melyek nem igazán használhatóak arra, amire szeretnénk, ugyanis azonosan egyezôséget vizsgálnak, vagyis csak akkor egyenlô az egyik objektum a másikkal, ha annyira ugyanazok, hogy amikor az egyiket változtatjuk akkor változik a másik is. (Ezt az objektum2=objektum1 módszerrel érhetjük el. Ha nem ez a célunk - és többnyire nem -, akkor az objektum2=objektum1~copy formában tudunk önálló életre is képes másolatot csinálni, ami azonban máris nem lesz egyenlô az eredetijével ... ) Az azonossági vizsgálatokra sokkalta alkalmasabbak a halmazmûveletek, név szerint a xor vagy a difference, melyeknél .NIL eredmény jelenti az egyezôséget. Természetesen mindenütt mûködik a put és az értékadás egyenlôségjellel, valamint az at és az érték keresése szögletes zárójelekkel.

Ezen metódusokon kívül van azonban még egy lehetôség, melyet a kupacokkal kapcsolatban nagyon hasznos ismerni, és ez a Do utasítás Do OVER formája. Nézzük az alábbi programrészletet:

/* doover.cmd: do over kupac */
kupac = .relation~new
kupac[ 'os2'] ='multithread'
kupac[ 'os2'] ='multitask'
kupac[ 'linux'] = 'multitask'
/* kiirunk minden os2-hoz tartozo tulajdonsagot */
do kepesseg over kupac~allat('os2')
	say 'OS/2='kepesseg
end
/* kiirunk minden ismert indexet (oprendszert) */
do darab over kupac
	say darab
end
/* ez ugyanaz, csak mutatom, hogy hogyan mukodik az over */
atmenet1 = kupac~makearray
do index=atmenet1~first to
atmenet1~last
 darab=atmenet1~at(index)
  say darab
end

Látható futás után, hogy az over sorban végigmegy a kupac elemein, és azokat beteszi a megadott változóba. Ezzel a módszerrel végig tudjuk járni egy nem egyértelmûen rendezett (pl. zsák vagy tábla) elemeit is. Az, hogy az over eredménye a kupacban található adat, vagy csak azok indexe az attól függ, hogy a kupacra hogyan értelmezett a makearray metódus: a tömböknél ez az elemeket eredményezi, míg a példánkban a relation-nél az indexeket adta vissza.

További hasznos osztályok

A kupacokon kívül sok olyan osztállyal is rendelkezünk indulásnál, amik nem szorosan adatokat tárolnak, hanem vagy objektumokon végeznek mindenféle varázslatot, vagy a rendszer lehetôségeit veszik igénybe, illetve segítenek ezeket egyszerûen felhasználni.

Tekintsük elôször azokat, amik a kupacokhoz hasonlóan "adatstruktúrák", adatokat tárolnak.

String, akit néha magyar barátai karakterláncnak szoktak csúfolni. Mint ismert, az eredeti REXX nyelv alapja a String adatstruktúra volt, és az OREXX esetén sem hanyagoljuk el a használatát. A stringek tetszôleges hosszúságúak lehetnek és emellett tetszôleges értékû bájtokat tartalmazhatnak. (Számunkra inkább érdekesség csupán az, hogy a REXX stringek a megfelelô fajtájú OS/2 alatt tartalmazhatnak DBCS (Double Byte Character Set) jeleket is, amik két bájton tárolt, többnyire kínai, japán és hasonló nyelvû karakterek. Ezek kezelése többnyire nem is igényel külön elbánást, ha pedig mégis, arról a megfelelô leírásokból érdemes tájékozódni, amennyiben programunkat ilyen környezetben óhajtanánk felhasználni.) A String osztály gazdagon el van látva metódusokkal; megtalálhatunk itt mindent ami egyrészt a karakterláncok kezeléséhez szükséges, (ezek a metódusok a "régi" REXX guruk számára ismerôsek lesznek, hiszen nagy részüket mint karakterláncokkal foglalkozó függvényt már ismerhetik, és ezen metódusok nagy része az OREXX-ben is megtalálható függvényként,) másrészt azon alapvetô numerikus mûveleteket melyekkel a "számokként értelmezhetô karakterláncokon" végezhetünk mûveleteket. Metódusait itt nem sorolnám fel, mert egyrészt azok megtalálhatóak a megfelelô referencia-fájlokban, másrészt igen sok van belôlük.

Érdemes azt is felidézni, hogy a String class az egyik népszerû "közös nevezô" az osztályok között; minden osztály rendelkezik a string metódussal, mellyel többé-kevésbé sikeresen próbálja elôállítani az objektum szöveges formáját, és - ha még emlékszik mindenki rá - sok objektumon végzett mûvelet - mint legtipikusabb példaként a SAY utasítás - automatikusan "odagondolja" a string metódust, hogy az objektum szöveges (vagy numerikus, hiszen ez ugyanúgy string) formájával dolgozhasson tovább.

Stem, Na igen, meg se próbálnám lefordítani (remélem mások sem) ennek az osztálynak a nevét, ami már REXX idôkben is hallatlanul érdekes programozási megoldásokat provokált ki. A Stemek furcsa jószágok, kicsit olyanok, mintha tömbök lennének, kicsit mint a Directory class hagyományos REXX-be öntve, és kicsit valami más. A Stemekrôl tudnék sok mágikus dolgot mesélni, de egyrészt ezek tényleg csak a REXX-ôrülteknek lennének érdekesek, másrészt a stem-bûvészkedést kicsit háttérbe szorították az (újonnan megjelent kupac adatstruktúrák. (Ennek ellenére a BBS-en és Interneten elérhetô példaprogramok közé betettem egy stems.cmd-t amiben ilyesmik történnek.)

A stem tulajdonképpen egy tetszôleges stringgel indexelt tömb, stemnév. index1. index2. ... formában. Maga a Stem a stemnév. (a pötty is!), ha ennek adunk értéket, akkor a stem minden eleme ezt az értéket veszi fel. Az indexek (ha szerepelnek) változónevek. Mivel a változók alapértelmezésben saját nevüket tartalmazzák nagybetûvel, így a nem létezô változók indexként megadva úgy mûködnek, mintha nem egy változót, hanem az index nevét írtuk volna oda. Példa: a STEM.I az ,I" indexû elemet jelenti, ha az I változónak nincs értéke, és például a hatos indexût, ha az I értéke 6.

Mint objektumosztály, a Stem nem igazán hasznos dolog, néhány egyszerû metóduson kívül nem sok mindent tudunk vele csinálni, az igazi "erejét" a strukturált programozásnál ("hagyományos programok", amik hatvan fokon még nem mosták ki a GOTO foltokat, és a gépünk is kifakult tôlük) érezhettük.

Stream, Nem szeretném patakocskának, esetleg folyamnak nevezni, noha a szó ilyesmit sugall. A Streamek az OREXX fájlkezelését valósítják meg, hasonlóan a REXX streamjeihez. Segítségükkel egyszerûen kezelhetjük a fájljainkat. A legegyszerûbb feladatokhoz mindössze az alábbi metódusok szükségesek:

Hasonlóan a REXX streamekhez itt sem kötelezô a megnyitás és lezárás, de szerintem nem szép az a program ahol soha nem lehet tudni, hogy épp hány fájl van nyitva (ezek száma amúgy is véges!).

Az OREXX kétféle streamet ismer: állandó és változó streamet. Az elôbbire egy fájl a példa, míg az utóbbira mondjuk a billentyûzet vagy a soros port, aminek "soha nincs vége" és menet közben változik. Az állandó streameken belül mozoghatunk (a seek metódussal), míg a változókon belül - értelemszerûen - nem.

Az OREXX egy lényeges pontban eltér a REXX streamektôl, méghozzá abban, hogy nem egy, hanem két mutatót tárol a streamen belül: egy olvasási és egy írási mutatót. Ezek a seek metódussal külön-külön állítandóak. (Nagyon fontos, hogy a seek függvény is kötelezôen kéri ezt a paramétert, így a régi REXX programjaink hibával leállnak, ha nem javítjuk ki ôket!)

Egy nagyon egyszerû példaprogram, ami kiírja egy fájl sorait:

/* Streams.cmd: file kiirasa */
parse source . . nevem
test = .stream~new(nevem)
test~open('read')
do while test~state == 'READY'
	say '>' test~linein
end
test~close

A Stream class hajlandó sok mindenben segíteni: lehetséges például az arrayin metódussal az egész fájlt egy tömbbe beolvasni, lehet soronként, karakterenként, vagy akár állandó méretû blokkonként is olvasni, írni, és hasonlóak.

Arra mindenképp ügyelni kell, hogy egy adott streamet nem szabad egyszerre metódusokkal és függvényekkel kezelni, mert ez megjósolhatatlan eredményeket produkálhat (azon kívül, hogy megjósolhatatlan adatvesztés lesz a melléktermék).

Gervai Péter
1997. 01. 15.
[ Elôzô lecke | Következô lecke | Tartalom ]