Bevezetés
Ebben a leckében a NetREXX szkriptnyelvként történô használatáról lesz szó. Ez azt jelenti, hogy egyelôre nem (vagy csak alig) fogjuk kihasználni a NetREXX objektum orientált szolgáltatásait. A példaprogramok egyszerû felépítésûek lesznek, s a hagyományos módon, függvényekbôl és eljárásokból fogunk építkezni. A végeredményként generált Java kód természetesen könnyen hordozható, így a NetREXX-ben írt szkriptek elvileg minden platformon futtathatók lesznek, amelyek rendelkeznek Java motorral.
A szkriptek felépítése
A NetREXX szkriptekre jellemzô, hogy írásuk során nem törôdünk az osztályok definiálásával, hanem csak egyszerûen belekezdünk a program megírásába, s a NetREXX környezet automatikusan elintézi a többit. Az elsô leckében bemutatott példaprogramok többsége is tulajdonképpen szkript volt. Most tekintsük meg az alábbi példát, amely egy egyszerû (ám tanulságos), "gondoltam egy számot" alapú játékot valósít meg:
/* játék */ say 'Választok egy számot 0 és 1000 között.' number = 1000 * Math.random() % 1 -- egész számmá alakítjuk say 'Kész vagyok!' guess = int loop count = 1 until guess = number say count'. tipp? \-' guess = ask select when guess > number then say 'A(z) 'guess' túl nagy.' when guess < number then say 'A(z) 'guess' túl kicsi.' otherwise say say 'Gratulálok! A kérdést' count 'próbálkozás után oldotta meg.' end catch RunTimeException say 'Sajnálom, vesztett. Csak egész számokat fogadok el.' end
Mint látjuk, a program nagyon emlékeztet egy REXX-ben írt programra, s gyakorlatilag nincs benne semmi olyan, amely elárulná azt, hogy ezt egy objektum-orientált nyelven írták. A szkript elsô részében képezünk egy 0 és 1000 közötti egész számot a Math.random függvény (metódus) segítségével. Ezt követôen egy hurokban folytatódik a program, amely egészen addig tart, amíg a felhasználó el nem találja a számot, vagy amíg érvénytelen adattal próbálkozik. Sikertelen tipp esetén tájékoztatjuk a felhasználót, hogy kisebb, vagy nagyobb-e a keresett szám a tippeltnél.
Figyeljük meg a say count'. tipp? \-' sor végén található \- hatását a program futtatása során! Amint látható, a speciális lezárás hatására kiíratáskor a cursor nem ugrik vissza a következô sor elejére, hanem a megjelenített karakterlánc végén várakozik. Ugyanilyen hatása van a \0 lezárásnak.
Függvények és eljárások
Kicsit bonyolultabb programok esetén mindig felvetôdik a strukturált programozás igénye, amelyet függvények vagy eljárások segítségével lehet megvalósítani. Mint tudjuk, a függvény és az eljárás között az a különbség, hogy a függvény értéket ad vissza, míg az eljárás nem. A NetREXX-ben a függvényeket és eljárásokat metódusként definiáljuk. Amennyiben a metódusnak van visszatérési értéke, akkor függvényrôl, amennyiben nincs, akkor pedig eljárásról beszélünk. A szkriptekben használt metódusok definiálásakor meg kell adni a static kulcsszót, ugyanis ellenkezô esetben a metódus nem lesz elérhetô a szkript számára, és a fordítóprogram nem ismert metódusra fog panaszkodni annak használata esetén:
method név([paraméterlista]) static
A paraméterlistában kell megadni mindazon paramétereket, amelyekkel a metódust akarjuk meghívni. Amennyiben nincsenek ilyen paraméterek, akkor a zárójeleket akár el is hagyhatjuk. A metódusok a return utasítás segítségével adhatnak vissza értékeket. Eljárások esetében a return utasítást csak paraméter nélkül szabad használni. Lényeges különbség a klasszikus REXX-hez képest, hogy globális változók itt nem léteznek. Ez azt jelenti, hogy a metódusok csak azokkal az adatokkal dolgozhatnak, amelyeket a paraméterlistán keresztül adunk meg. Ennyi tudással a birtokunkban már biztosan megértjük a játékprogram strukturáltabb változatát:
/* játék 2 */
say 'Választok egy számot 0 és 1000 között.'
number = 1000*Math.random() % 1 -- %1 egész számmá alakítjuk
say 'Kész vagyok!'
guess = int
loop count = 1 until guess = number
say count'. tipp? \-'
guess = getNumber() -- meghívunk egy függvényt
showAnswer(guess,number,count) -- meghívunk egy eljárást
end
method showAnswer(guess,number,count) static
select
when guess > number then
say 'A(z) 'guess' túl nagy.'
when guess < number then
say 'A(z) 'guess' túl kicsi.'
otherwise
say
say 'Gratulálok! A kérdést' count 'próbálkozás után oldotta meg.'
end
method getNumber static returns int
loop forever
number = ask
if number.datatype("W") then return number
say "Sajnos a megadott adat ("number") nem érvényes!"
say "Adjon meg új adatot! \-"
end
Amint látható, a tipp bekérését egy függvény (getNumber), a tipp kiértékelését pedig egy eljárás (showAnswer) formájában oldottuk meg.
Külsô metódusok használata
A fenti példában a metódusok és a program, amelybôl meghívtuk ôket, ugyanazon fájlon belül helyezkedtek el. Természetesen a NetREXX programokból meghívhatunk olyan metódusokat is, amelyek más fájlokban találhatók. Ebben az esetben külsô metódusokról beszélünk. Amennyiben a külsô metódust tartalmazó fájl ugyanabban a könyvtárban található, mint a program, amelybôl meghívjuk, akkor nagyon könnyû dolgunk van. Ebben az esetben ugyanis a tartalmazó fájl nevét kell a metódus meghívásakor annak neve elôtt megadni. Ha pl. a játékprogram getNumber függvénye az input.nrx fájlban lenne található, akkor a metódusra a következô módon hivatkozhatnánk:
guess = Input.getNumber()
Nehezebb dolgunk van, ha a metódus fájlja nem a fôprogrammal megegyezô könyvtárban van. Ekkor ugyanis ún. csomagot (package) kell készítenünk. A csomagkészítés receptje a következô:
Ezzel kész is a csomag. Hogy a szkriptek használni tudják a csomagban található metódusokat, elôbb importálniuk kell a csomagot az import [csomagnév] utasítással, amelynek a program elején kell szerepelnie.
Nem Java programok használata
A Java fejlesztôkörnyezet segítségével elindíthatunk nem Java programokat is a NetREXX szkriptekbôl. Erre a Runtime osztály exec metódusát használhatjuk fel:
Runtime.getRuntime().exec(program)
Mint látjuk, az exec metódus használata elôtt le kell kérdezni a jelenlegi Runtime objektumot a getRuntime hívással. Sikertelen programindítás esetén IOException történik. Az elindított programmal az exec metódus által visszaadott Process objektum metódusaival kommunikálhatunk. Az alábbi táblázatban összefoglaltuk a használható metódusokat:
| Metódus: | Feladat: |
| getErrorStream | Visszaad egy InputStreamet, amely az objektum standard error streamjéhez csatlakozik. |
| getInputStream | Visszaad egy InputStreamet, amely az objektum standard output streamjéhez csatlakozik. |
| getOutputstream | Visszaad egy OutputStreamet, amely az objektum standard input streamjéhez csatlakozik. |
| destroy | A program lelövése. |
| exitValue | A program által visszaadott érték lekérdezése. A program végrehajtásának befejezése elôtt meghívva IllegalThreadStateException hiba történik. |
| waitFor | Vár, amíg a program futása leáll. |
A jobb érthetôség kedvéért most nézzünk meg egy példát, amely az UNZIP program NetREXX programból történô használatát demonstrálja:
/* Az unzip használata NetREXX-bôl */ parse arg unzip zipfile . do say "A" zipfile" fájlban található fájlok neve, mérete és dátuma:" say child = Runtime.getRuntime().exec(unzip ' -v' zipfile) -- olvassuk az elindított folyamat output streamjét in = BufferedReader(InputStreamReader(child.getInputStream())) line = in.readline start = 0 -- a fájlok listája még nem elérhetô loop while line \= null -- amíg van adat, addig megy a feldolgozás parse line l . . . d . . n if l = ' ------' then start = \start else if start then say n l d line = in.readline() end -- megvárjuk a folyamat leállását és lekérdezzük a visszatérési értékét child.waitFor() if child.exitValue() \= 0 then say 'Az unzip visszatérési értéke:' child.exitValue() catch IOException say 'Nem találom a(z)' unzip 'programot.' catch e2=InterruptedException e2.printStackTrace() end
A program elsô soraiban kérjük be az unzip programot és a vizsgálandó zip fájlt. Mivel kezelni akarjuk a váratlan eseményeket a catch utasítással, ezért do-end struktúrába ágyazzuk be a program négy fô részre oszló folytatását. Az elsô részben elindítjuk a megfelelôen paraméterezett unzip programot az exec metódussal. A második részben rácsatlakozunk az elindított program kimenetét közvetítô csatornára (standard output), és inicializáljuk a kimenetre küldött sor (line) és a fájllista megjelenését jelzô változó (start) alapértékét. A harmadik részben található hurokban egészen addig tartózkodunk, amíg az unzip kimenetén adat van. A kimeneten megjelenô sorokból a parse utasítással szûrjük ki a zip fájlban található fájlok méretét, dátumát és nevét. A start változó 0 vagy nem 0 értéke jelzi, hogy feldolgozandó adatsorral van-e dolgunk. A start értékét az l (length) változó értéke állítja, ugyanis a fájlok listáját a '------' karakterlánc nyitja, illetve zárja. A negyedig részben figyeljük az unzip program bezárulását és nem 0 visszatérési érték esetén informáljuk a felhasználót. Itt kapott helyet a váratlan események (pl. hiányzó unzip program) lekezelésére szolgáló két catch utasítás is. Mivel a példaprogram parancssorban megadott paraméterekkel dolgozik, ezért két lépésben kell futtatni. A NetREXX fordítóval elôször el kell készíttetnünk a Java bájtkódot (nrc lecke03c), majd pedig ezt kell futtatni a megfelelô paraméterekkel (java lecke03c unzip.exe [zip-fájl]).
Mi történik a színfalak mögött?
Mivel a Java nyelv nem igazán alkalmas szkriptek írására, ezért a NetREXX környezetnek el kell végeznie néhány átalakítást az általunk írt szkripten, mielôtt az futtatható lesz a Java környezetben. Amikor egy Java programot futtatunk, akkor tulajdonképpen az elindított osztály fô metódusa kerül meghívásra. A fô metódusnak static-nek kell lennie, mivel az osztályhoz tartozik, és nem pedig az abból képzett objektumokhoz. A szkriptekben nem definiálunk osztályokat és fô metódusokat, ezért a NetREXX a fájl neve alapján készít egy osztályt, s fô metódusként a programunkat képezô kódot használja. Amennyiben programunkon belül függvényeket vagy eljárásokat definiáltunk, akkor ezeket az osztály további metódusaiként értelmezi.
Az elôzô példaprogramban láttuk, hogy a parse arg utasítás segítségével tudtuk feldolgozni a parancssorban megadott paramétereket. A NetREXX természetesen automatikusan befûzte programunk sorai elé az osztály és a fô metódus definícióját. Mi magunk is definiálhatjuk a fô metódust, és ekkor közvetlenül is használhatjuk a paramétereket tartalmazó tömböt, illetve a paraméterek számát tartalmazó length tulajdonságot is:
class valami method main(args=String[]) static say "Paraméterek száma:" args.length loop i=0 to args.length-1 say " - Az argument száma: "i", értéke: "args[i] end ...
Ha nem használjuk a paramétereket, akkor a NetREXX figyelmeztetô üzenetet fog küldeni a fordításkor, ezért érdemes egy ál sort is beiktatni a fô metódus definiálása után:
method main(args=String[]) static args = args
REXX GYÍK:
K1. A Java programok egyik elônye a széleskörû felhasználhatóság. Rendszerspecifikus programok beágyazásával ez az elôny elvész. Vagy mégsem?
V1. De igen. Éppen ezért bánjunk nagyon csínján ezzel a lehetôséggel, s operációsrendszer-függô programokat csak akkor ágyazzunk be programunkba, ha erre feltétlenül szükség van!
Gyakorlatok:
1. Készítsen egy tetszôleges szkriptet, amely külsô metódusokat használ!
2. Készítsen egy tetszôleges szkriptet, amely rendszerspecifikus programokat hív meg!
| Kádár Zsolt 1999. 12. 29. | [ Elôzô lecke | Következô lecke | Tartalom ] |