A sorozat előző részében szó volt a többdimenziós tömbökről, majd a referenciákról, a változók érvényességi köréről, és a modulokról. A mostani rész elején néhány speciális változót ismertetek, majd a DBM adatbázisok használatát mutatom be, ezek után pedig az objektum orientált programozás alapjairól szólok. Tehát akkor nézzünk meg néhány előre definiált Perl változót. Az egyik leggyakrabban használt ilyen skalár a $_ , mely az alapértelmezett változó. Sok helyen használható, például néhány függvény esetén, ha nem adunk meg paramétert, a Perl ezt fogja használni:

while (<STDIN>) {   chomp;   s/./a/g;   print; }

Ez a kis program például a beírt adatok végéről levágja a sortörést, majd az összes karakterét egy "a" betűre cseréli le, és kiírja a kapott sztringet. Mindezt anélkül, hogy egyetlen változónév is szerepelne a programban.
A $/ az input rekordszeparátort határozza meg, más néven azt a karaktert, amely a beviteli adatok elválasztására szolgál. Ez alapesetben az újsor karakter (\n), melyet meg is változtathatunk:

$/ = "vege"; $in = <STDIN>; print "\nA beírt adatok:\n---\n$in\n---";

A kódot lefuttatva a $in változónak akár többsoros szöveg is megadható, a beolvasás mindaddig tartani fog, amíg nem talál "vege" karaktersorozatot a beolvasott sorban.
A $/ párja a $\, mely a kimeneti rekordszeparátor. Ez fog kiíródni minden rekord után. Alapesetben üres.

$\ = "[print vége]\n"; print "A romantika a klasszicizmusból nőtt ki,"; print "de hamarosan szembefordult vele."; print "Elődje a szentimentalizmus volt.";

A kimeneten minden kiírt rekord után megjelenik a $\ tartalma:

A romantika a klasszicizmusból nőtt ki,[print vége] de hamarosan szembefordult vele.[print vége] Elődje a szentimentalizmus volt.[print vége]

Az $1,$2,$3 változókkal már biztosan találkozott, ezek a reguláris kifejezésekben eltárolt sztringek, az eltárolás sorrendjében:

$ipcim = "127.0.0.1"; $ipcim =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/; print " Az IP címben szereplő négy szám: $1, $2, $3, $4.";

A kimenet:

Az IP címben szereplő négy szám: 127, 0, 0, 1.

Mintaillesztés esetén használható a $& skalár, amely az utoljára illesztett részt tartalmazza:

$valtozo = "piros alma"; $valtozo =~ s/a(.+)a/b\1b/; print "A '$valtozo'-ben eredetileg '$&' szerepelt.";

A kimenet:

A 'piros blmb'-ben eredetileg 'alma' szerepelt.

A $! skalárban mindig az utolsó hibaüzenet van:

open(FAJL,"<nincs-ilyen-fajl.txt") or die "A program ezt a hibaüzenetet adta: $!\n";

A kimenet abban az esetben, ha nem található a "nincs-ilyen-fajl.txt" fájl:

A program ezt a hibaüzenetet adta: No such file or directory

A $0 változóban az éppen futó program neve található:

print $0;

Nálam épp ezt írta ki:

D:\My Documents\PROGWORLD\Perl9\peldaprg.pl

A $] ill. $^O változók a használt Perl verziót illetve az operációs rendszer nevét tartalmazzák:

print "Oprendszer: $^O Perl verzió: $]";

A kimenetből jól látszik, hogy ezt most épp Windows alatt írom, és 5.006-os a Perl fordítóm:

Oprendszer: MSWin32 Perl verzió: 5.006

A $^T változó a program futásának kezdetét tárolja (1970. január 1. 0:00 [GMT] óta eltelt másodpercek száma).

A @ARGV tömbbel már találkoztunk egy régebbi részben, a programnak átadott parancssori argumentumok listájáról van szó. A @INC azokat a könyvtárakat tartalmazza, ahol a Perl egy modul után fog keresgélni. Ennek van egy hash párja, a %INC, mely a használt modulokat tartalmazza, a kulcsok az állománynevek, az értékek pedig az elérési utak. A %ENV hash a környezeti változók tömbje, a kulcsok a változók nevei, a hozzá tartozó értékek pedig maguk a beállítások.

Most pedig beszéljünk a DBM adatbázisokról. Azért csak erről szólok, mert ez igaz ugyan, hogy elég primitív, viszont igen támogatott, és a fordítón kívül nincs szükség másra. Az adatokhoz egy hash-en keresztül férhetünk hozzá, ami elméletileg bármiből bármennyit tartalmazhat. A használat rendkívül egyszerű, ugyanis a Perl egy külön függvényt tart fenn, amely segítségével az adatbázis tartalmát egy tetszőleges asszociatív tömbhöz rendelhetjük hozzá, ez fog felületet biztosítani az adatbázis kezeléséhez. Minden műveletet ezen a hash-en kell végrehajtani, és ezek a változások mind meg fognak jelenni az adatbázisban is. Mint már említettem, ez a módszer csak igen egyszerű adatbázisok esetén alkalmazható, mivel csak egyszerű kulcs-érték párok tárolására alkalmas. Most nézzük meg, hogyan használható ez a gyakorlatban!

dbmopen(%adatok, "adatok", 0666); $adatok{'piros'} = "szín"; $adatok{'monitor'} = "hardver"; $adatok{'teve'} = "állat"; dbmclose(%adatok);

A dbmopen() függvény segítségével nyitható meg egy ilyen adatbázis. Három paramétert kell megadni, a felületül szolgáló hash nevét, az adatbázis nevét -ez lesz a file neve is, .dir és .pag kiterjesztéssel-, végül a hozzáférési jogokat. A dbmclose() használatával zárható be az adatbázis, ekkor a hash is eltűnik. Most listázzuk ki a létrehozott adatbázisunk tartalmát!

dbmopen(%adatok, "adatok", 0666); foreach (keys %adatok) {   print "$_: $adatok{$_}\n"; } dbmclose(%adatok);
 

A kimenet:

piros: szín monitor: hardver teve: állat

Új elemek hozzáadása, vagy a már meglévők módosítása, törlése a már megismert módon történik -az értékadó operátorral, illetve a delete() függvénnyel.

Most pedig beszéljünk egy kicsit az objektum orientált programozásról Perlben. Akiknek már vannak programozási ismereteik, biztosan találkoztak már ezzel a kifejezéssel. Az eljárás előnye, hogy az így írt programok sokkal rugalmasabban fejleszthetők, illetve újrafelhasználhatók, és nem utolsó sorban a kód strukturáltabb, áttekinthetőbb. Mi is maga az objektum? Egy referencia,azonban nem olyan, mint amikkel eddig találkozott. Az különbözteti meg, hogy létrehozásakor a bless() segítségével "megáldjuk", ami annyit jelent, hogy az is eltárolásra kerül, hogy hol -melyik osztályban - jött létre. Az osztály egy egyszerű modult jelent, és az itt létrehozott függvényeket metódusoknak nevezzük. Ha nem adunk meg osztálynevet, akkor az objektum oda fog tartozni, ahol létrehoztuk. Az alábbi példában a $ido referenciát a "Datum" osztály objektumává tesszük:

bless $ido, "Datum";

A referencia általában egy hash szokott lenni, mivel ez felel meg a legjobban annak, hogy az objektum összes adata egyszerűen tárolható legyen benne. Az objektum létrehozása egy metódus segítségével történik, melyre az osztályon keresztül hivatkozunk. Ezt nevezzük konstruktor metódusnak. Ez után a létrehozott objektumon keresztül érhetjük el annak metódusait. A konstruktor metódus és az objektum metódusai között az a különbség, hogy a konstruktor metódus meghívásánál a paraméterekhez automatikusan hozzácsatolódik az osztály neve, míg az osztály metódusaihoz az objektum referencia, ezzel tudunk majd a metóduson belül további, ugyanahhoz az objektumhoz tartozó metódusokat meghívni. Ezek után hozzunk létre egy igen egyszerű konstruktor metódust:

package Datum; sub Uj {   my $osztaly = shift;   my $ref = {};   bless $ref, $osztaly;   return $ref; }

Mint mar említettem, a Perl automatikusan kibővíti az átadott argumentumokat az osztály nevével, ezt itt a shift() használatával elkülönítjük az esetleges többi paramétertől, nehogy később zavaró legyen. Ez után létrehoztuk az üres hash referenciát, majd az objektumot. És itt jött még egy lényeges sor, a metódus visszatérési értéke, amely az objektumreferencia kell, hogy legyen, mert később ezen keresztül tudjuk majd az objektum metódusait elérni. A most létrehozott konstruktor metódussal a következőképpen hozhatjuk létre magát az objektumot:

$obj = Datum->Uj();

Most hozzunk létre egy metódust, ami visszaadja azt, hogy ma épp hányadika van!

sub Hanyadika {   my $objref = shift;   my $ido = shift;   $ido = time unless $ido;   return (localtime($ido))[3]; }

Mint látható, először kivettem az objektum referenciát, majd az első argumentumot. Ha utóbbi nem létezik, vagy nincs értéke, akkor a time() függvény által visszaadott számot adom neki értékül. A visszatérési érték pedig a localtime() által visszaadott lista negyedik eleme.

Most pedig hozzunk létre egy Datum.pm nevű fájlt, benne a "package Datum;" sorral, és a két metódussal. Az így elkészült objektumorientált modult az alábbi kódrészletben hasznosítjuk:

use Datum; $obj = Datum->Uj(); print "Ma " . $obj->Hanyadika() . "-a/e van.\n"; print "100 nappal ezel?tt " . $obj->Hanyadika( time() - 60*60*24*100 ) . "-a/e volt."; exit;

Mint látható, a modul betöltése után a konstruktor metódussal létrehoztam egy objektumot, majd az objektumreferencián ($obj) keresztül értem el az objektum metódusát. Az első esetben nem adtam meg paramétert, mivel mint már tudjuk, az objektum így a jelenlegi időt fogja figyelembe venni, a második esetben pedig a jelenlegi időből vontam ki a 100 év alatt eltelt másodpercek számát (60*60*24*100). A programot lefuttatva az alábbi kimenet íródott ki:

Ma 24-a/e van. 100 nappal ezelőtt 14-a/e volt.

A cikk zárásaként egy naplót fogunk elkészíteni, melyben nevekhez tartozó pontszámokat fogunk eltárolni -természetesen egy a cikk elején megismert DBM adatbázis használatával, és objektum orientált modulokkal.
Két modult fogunk létrehozni, ez egyikkel lehet majd egy név megadása után az ahhoz tartozó pontszámot kiíratni, a másikkal pedig új név-pontszám párt létrehozni, vagy a már meglévőket módosítani. Először írjuk meg a bejegyzéseket készítő részt.
A modul létrehozása után készítsük el a konstruktor metódust, a neve legyen mondjuk Uj.

package NaploIr; sub Uj {   my $type = shift;   my $fajl = shift;   my $self = {};   bless $self, $type;   $self->{"fajl"} = $fajl;   return $self; }

Ami érdekes lehet, hogy a kapott paramétert eltárolja az objektum referenciában, hogy az később is hozzáférhető legyen. Ez után készítsük el az adatok bekérésére szolgáló metódust, a neve értelemszerűen "Beker" lesz.

sub Beker {   my $self = shift;   my ($nev, $pontszam);   NEV: {   print "Név: ";   $nev = <STDIN>;   chomp($nev);   print "Hiányos adatok!\n" and redo unless $nev; } PONTSZAM: {   print "Pontszám: ";   $pontszam = <STDIN>;   chomp($pontszam);   print "Hiányos adatok!\n" and redo unless $pontszam; } return $nev, $pontszam; }

A metódus elején definiáltunk két lokális változót, amelybe később a beírt adatok fognak kerülni. Az adatok bekérése egy külön utasításblokkban történik, melynek a végén a program ellenőrzi a beírtakat, és ha a felhasználó nem adta meg a szükséges adatot, akkor egy hibaüzenet kíséretében a program visszaugrik a blokk elejére (redo). A visszatérési értékek a kapott adatok. Már létre tudjuk hozni az objektumot, az adatokat is be tudjuk olvasni, egyetlen metódus van még hátra, mely a kulcs-érték - ebben az esetben név-pontszám - párokat eltárolja. Nevezzük el "Beir"-nak.

sub Beir {   my ($self, $nev, $pontszam) = @_;   dbmopen(%naplo, $self->{"fajl"}, 0666);   $naplo{$nev} = $pontszam;   dbmclose(%adatok); }

Most már tényleg mindennel megvagyunk, a modul kész, már csak egy program kell, ami az egészet működtetni fogja.

use NaploIr;   $naplo = NaploIr->Uj("naplo");   ($nev, $pontszam) = $naplo->Beker();   $naplo->Beir($nev, $pontszam);   print "$nev sikeresen hozzáadva."; exit;

A modul betöltése, és az objektum létrehozása után a $nev és $pontszam változóba kértük be az adatokat, melyeket tovább adtunk az eltárolást végző metódusnak. Ezt egy sorban is el lehetett volna intézni:
$naplo->Beir( $naplo->Beker() );

Így viszont nem jutottunk volna hozzá a beírt adatokhoz, tehát a $nev változó hiányában nem tudtuk volna a fent látható üzenetet abban a formában kiírni. Most pedig készítsük el a másik modult, amely a pontszámok kiírására szolgál. Itt is a konstruktor metódussal kezdünk:

package NaploOlvas; sub Uj {   my $type = shift;   my $self = {};   bless $self, $type;   $self->Beolvas(@_);   return $self; }

Jól látható, hogy az objektum elkészítése után hogyan hivatkozunk egy másik metódusra. Ebben az esetben azonnal meghívjuk a "Beolvas" metódust, argumentumként a kapott paraméter(eke)t megadva.

sub Beolvas {   my ($self, $fajl) = @_;   dbmopen(%naplo, $fajl, 0666);   foreach (keys %naplo) {   $self->{"naplo"}->{$_} = $naplo{$_}; } dbmclose(%adatok); }

Itt történik meg maga a beolvasás, és az objektumreferenciában való eltárolás. Így ha egy programon belül többször is szükségünk van a pontszámokra, nem kell mindig beolvasni azokat, mivel benn vannak a memóriában. A memóriából történő kiolvasásra is készítünk egy metódust:

sub Olvas {   my ($self, $nev) = @_;   return $self->{"naplo"}->{$nev} if defined $self->{"naplo"}->{$nev}; }

Ez egyszerűen visszaadja a kért névhez tartozó pontszámot, ha létezik a megadott kulcs a hash-ben. Már csak a név bekérését, és visszaadását kell megoldani, mely szintén egyszerű:

sub NevBeker {   my $nev;   print "Név: ";   $nev = <STDIN>;   chomp($nev);   return $nev; }

Ezzel készen vagyunk a pontok lekérdezéséhez szükséges objektummal is, melyet az alábbi programban használunk fel:

use NaploOlvas;   $naplo = NaploOlvas->Uj("naplo");   $nev = $naplo->NevBeker();   if ( $naplo->Olvas($nev) ) {     print "$nev pontszáma: " . $naplo->Olvas($nev);   } else {     print "Nem találtam $nev nev? bejegyzést.";   }   exit;

Az objektum létrehozása, és a keresett név bekérése után következik a program lényegi része: ha a névhez tartozik pontszám - melyet az Olvas() metódussal kapunk meg -, akkor kiírjuk, ha nem, akkor egy hibaüzenetet jelenítünk meg.

A cikkben szereplő példaprogramok megtalálhatók a cikk mellett. A következő részekben a Perl CGI programokban történő alkalmazásáról lesz szó, addig is minden jót.