ugrás a tartalomhoz

Perl alapjai III. - Műveletek

Bártházi András · 2004. Aug. 3. (K), 21.00
Perl alapjai III. - Műveletek
Az előző cikkben megismerkedhettünk a Perl nyelv alapvető szintaktikájával, megjelenésével, a mai napon a különböző operátorokkal, azaz az egyszerű műveleti jelekkel fogunk, s kicsit előre tekintve elkészítünk egy nem túl bonyolult, de már valamire használható kis programot. Aki inkább a tevéket szereti, annak is elérhető lesz egy fotó az Állatkertből...

Műveletek skalár változókkal

Egy programozási nyelvnek tudnia kell műveleteket végeznie az egyes változókkal, hiszen nem másra valók a változók, mint hogy megváltozzon az értékük. Ez a Perlben sincsen másképp. Első körben a matematikában megszokott műveleteket minden további ismerkedés nélkül használatba vehetjük: az összeadásra a + jelet, a kivonásra a - jelet, a szorzásra a * jelet, az osztásra / jelet, a maradékképzésre (modulus számításra) a % jelet, míg a hatványozásra a ** jeleket használhatjuk, illetve zárójelezhetünk kedvünk szerint. A műveletek kiértékelésénél a sorrendet az operátorok (műveleti jelek) precedencia (elsőbbrendűségi) sorrendje határozza meg. A zárójelezés a legmagasabb szintű, ezután a hatványozás, majd az osztás és a szorzás egy szinten (értsd: amelyik előbb van, balról-jobbra), továbbá az összeadás és kivonás következik (szintén egy szinten). Ez megegyezik azzal, ahogy mi is elvégeznénk a műveletet, ahogy remélhetőleg matematika órán megtanultuk.

Amennyiben egy olyan változóra használjuk ezeket az operátorokat (műveleti jeleket), melyek karakterláncot tartalmaznak, akkor azok előzőleg automatikusan számokká alakulnak. Lássunk ezekre gyorsan pár példát is (a számok helyett használhatunk természetesen változókat is):

$szam=(10+2-6)/3;  # $szam=2
$szam=10%3;        # $szam=1
$szam=(1+2)*(3+4); # $szam=21
$szam=-3**2;       # $szam=-9

$szam='10'+2;      # $szam=12
$szam='tiz'+2;     # $szam=2
$szam='6alom'+1    # $szam=7
A megjegyzésben a szam változó értékét láthatjuk a műveletek kiértékelése után. A fenti példáknál az egyetlen meglepetés talán a negyedik, hatványozást tartalmazó példa, ahol az eredmény nem a várt 9, hanem -9. Miért ezt az értéket kaptuk? A válasz egyszerű: mert a hatványozás magasabb rendű művelet, mint a kivonás és ezért először az hajtódott végre, s csak utána a mínusz jel, a negálás.

Állatkerti teve - ő is még kezdő Perlben


Az utolsó példák működésénél annyit kell megértenünk, hogy egy szöveg számmá alakításakor a felesleges (azaz betű) részeket elhagyja a Perl, és ami megmarad, azzal dolgozik. A tiz-ből nem marad semmi, így az értéke ebben az esetben 0, a 6alom-ból pedig a 6 marad, ezért kapunk 7-et végeredményként.

Nem is olyan bonyolult ez! A könnyebb érthetőség kedvéért lássunk egy kis komplett példát, amely az eddig megismertek alapján már egy kis működő program lesz:

#!/usr/bin/perl

@szamok=(222,111);

$osszeg=$szamok[0]+$szamok[1];
$kulonbseg=$szamok[0]-$szamok[1];
$szorzat=$szamok[0]*$szamok[1];
$osztas=$szamok[0]/$szamok[1];

print "A két szám összege: $osszeg\n";
print "A két szám különbsége: $kulonbseg\n";
print "A két szám szorzata: $szorzat\n";
print "A két szám hányadosa: $osztas\n";

@eredmenyek=($osszeg,$kulonbseg,$szorzat,$osztas);

print "Az eredmények ömlesztve: @eredmenyek\n";

$bonyolult=($osztas+$kulonbseg)*($osztas);

print "A nagyon bonyolult művelet eredménye: $bonyolult\n";
Próbáljuk ki a programot, írogassuk át, próbáljunk meg bonyolultabb feladatokat is megoldani vele! Az eddigiek elolvasása (és matematikai tanulmányaink) után remélhetőleg semmi meglepő nem lesz a fenti programban, de ha valami mégse lenne világos, bátran nézzünk vissza az előző leckéhez.

A Perl nyelvben lehetőség van (a például a C-ben is használható) autoinkrementálásra (++), autodekrementálásra (--) is. Ez egy változó eggyel való növelésére vagy csökkentésére használható. Az operátort (azaz a dupla plusz, illetve mínusz műveleti jeleket) a változtatandó változó elé és mögé is írhatjuk. Ennek akkor van jelentősége, mikor az értékét előbb fel szeretnénk használni egy kifejezés részeként. Ha a változó elé írjuk, akkor az értékadás a változónak a kifejezésben történő kiértékelése előtt, amennyiben mögé, akkor a kifejezésben történő kiértékelése után hajtódik csak végre. A következőkben erre láthatunk példákat:

$szam1=3; $szam1++;        # $szam1=4;
$szam1=3; $szam1--;        # $szam1=2;
$szam1=3; $szam2=$szam1++; # $szam1=4; $szam2=3
$szam1=3; $szam2=$szam1--; # $szam1=2; $szam2=3
$szam1=3; $szam2=++$szam1; # $szam1=4; $szam2=4
$szam1=3; $szam2=--$szam1; # $szam1=2; $szam2=2
A megjegyzésekben most is a műveletek végrehajtása utáni állapotát láthatjuk a változóknak. A harmadik és negyedik sorban "posztinkrementálást" és "posztdekrementálást" láthatunk, azaz az értékadás után történik csak meg az érték növelése, illetve csökkentése, míg az utolsó két sorban ugyanezek pre- előtagú változatát, azaz a preinkrementálást és predekrementálást láthatjuk, azaz az érték változtatás a kiértékelés előtt történik meg.

Egy kicsit bonyolultabb példákkal ugyanez:

$szam1=3; $szam2=5; $eredmeny=++$szam1+$szam2++; # $szam1=4; $szam2=6; $eredmeny=9;
$szam1=3; $szam2=5; $eredmeny=++$szam2-$szam1--; # $szam1=2; $szam2=6; $eredmeny=3;
$szam1=3; $eredmeny=++$szam1*2-$szam1--+$szam1;  # $szam1=3; $eredmeny=7;
Gondoljuk végig, mi történik! Az első sornál mindenekelőtt a $szam1 értéke növekszik eggyel. Ezután a két szám (4 és 5) összeadásra kerül, az $eredmeny értéke 9 lesz. Végül a $szam2 értéke is növekszik eggyel, így 6 lesz az értéke.

A második sor esetén elég hasonló dolgok történnek. Először növekszik a $szam2 értéke eggyel, majd a két szám (6 és 3) különbségét vesszük és eltároljuk az $eredmeny változóba, s végül csökkentjük a $szam1 értékét eggyel, s a végeredmény kialakul.

Az utolsó sorban először (az első előfordulás miatt) növeljük a $szam1 értékét (4 lesz), vesszük a 4-et, megszorozzuk 2-vel (8-nál járunk), vesszük a $szam1 aktuális értékét (4) és kivonjuk a 8-ból (4-nél járunk), majd miután vettük az értékét, csökkentjük a $szam1-et eggyel (értéke megint 3 lesz), és hozzáadjuk a 4-hez, és így végeredményül 7-et kapunk. Ez első körben biztos vagyok benne, hogy bonyolult volt, sebaj. Nem kell így használnunk. Írhattuk volna így is:

$szam1=3;
$szam1=$szam1+1;
$eredmeny=$szam1*2;
$eredmeny=$eredmeny-$szam1;
$szam1=$szam1-1;
$eredmeny=$eredmeny+$szam1;
Az eredmény ugyanaz. A tanulság, hogy mindegy, hogyan tesszük, Perlben sokféleképpen tehetjük.

A logikai műveletek is a nyelv részei. Ezekből kétfajta is van, a "sima" logikai operátorok, melyeknél a művelet eredménye igaz, vagy hamis lehet, illetve a bitenkénti logikai operátorok, ahol a logikai művelet az egyes bitekre hajtódik végre. Ezek a következők:

$true=1; $false=0;

$ertek=$true && $false;
$ertek=$true || $false;
$ertek=!$false;

$szam=3 & 1;  # $szam=1
$szam=5 | 2;  # $szam=7
$szam=5 ^ 3;  # $szam=6
$szam=5 << 2; # $szam=20
$szam=5 >> 2; # $szam=1
Az első sorban csak a logikai értékeket definiáltuk. Teljesen helyes akkor lenne, amennyiben a $false értékének üres sztringet ('') adnánk, mivel a Perl ezt adja vissza hamis érték esetén, de példánk így is megállja helyét, mivel egy üres sztring értéke 0, ha automatikus konverzió hajtódik végre és számmá alakítjuk.

A második blokk első sora a logikai ÉSt, a második a logikai VAGYot a harmadik pedig a logikai TAGADÁSt mutatja be. A harmadik blokkban a bitenkénti műveletekre látunk példát, az első sora az ÉS, a második a VAGY, a harmadik pedig a kizáró VAGYra (XOR) példa. A negyedik és az ötödik a bitenkénti görgetésre, értelemszerűen az első balra, a második jobbra görget. Ezeket elég ritkán használja az ember, hacsak nem valami bonyolultabb dolgot ír, de ennek ellenére nem árt megismerni ezeket az operátorokat.

Szükségünk lehet sztring típusú változók összefűzésére (angolul konkatenálásnak hívják, ne ijedjünk meg, ha valahol ezzel a kifejezéssel találkozunk) is. Mit tehetünk ilyenkor? Természetesen erre is van megoldás, csak nem a + jel, amit esetleg más nyelvekben megszokhattunk erre a célra. Ha a +-t használnánk, akkor a műveleti jel mindkét oldalán álló érték először számmá alakulna, és könnyen 0-t kaphatnánk értékül.

A sztringekre kétfajta művelet van definiálva a Perlben: az konkatenálás-összefűzés (jele a .) és az ismétlés (jele az x, azaz a kis iksz). Tekintsünk meg erre is példákat:

$sztring1='első';
$szrting2='program';
$sztring3=$sztring1.' '.$sztring2; # $sztring3='első program'
$sztring4='-+'x3.'-';              # $sztring4='-+-+-+-'
Még mindig nem értünk a végére a műveleteknek. Ha egy változón saját magán szeretnénk csak végrehajtani egy műveletet, felesleges többször is leírni. Erre is lehetőség van, az egyenlőség jel elé kell írni a műveleti jelet:

$szam += 5;  # $szam=$szam+5
$szam -= 5;  # $szam=$szam-5
$szam *= 5;  # $szam=$szam*5
$szam /= 5;  # $szam=$szam/5
$szam %= 5;  # $szam=$szam%5
$szam **= 5; # $szam=$szam**5
$szam .= 5;  # $szam=$szam.5
$szam x= 5; # $szam=$szam x 5
$szam &&= 1; # $szam=$szam&&5
$szam ||= 1; # $szam=$szam||5
$szam &= 5;  # $szam=$szam&5
$szam |= 5;  # $szam=$szam|5
$szam ^= 5;  # $szam=$szam^5
$szam <<= 3; # $szam=$szam<<5
$szam >>= 3; # $szam=$szam>>5
A példákban talán feltűnt, hogy a műveleti jelek elé és mögé szóközt tettem. Valójában az esetek többségében teljesen mindegy hogy teszünk-e vagy nem, de van, amikor az áttekinthetőséget növeli, a kódot szellősebbé teszi, így jó ha ott van az a szóköz.

A Perl nyelvben lehetőség van úgynevezett többszörös értékadásra is. Ekkor egy értékadás jobb oldalára egy másik értékadást írunk, vagy másképpen és talán egy kicsit pontosabban megfogalmazva: egy értékadás bal oldalára több változót is írhatunk. Megint másképp fogalmazva, egy értékadást kifejezésként is használhatunk, az ilyen kifejezések értéke a jobb oldalon levő érték lesz. Lássunk erre két példát:

$szam2=$szam1=1;
$szam2=($szam1+=5)-3;
Az első talán a legtipikusabb, sorozatos értékadás, melyet általában programok elején, inicializáláskor használunk, ahol mind a $szam1, mind a $szam2 változó értéke 1 lesz. A második kifejezés először a belső értékadást fogja végrehajtani, melynek eredményeképpen a $szam1 változó értéke 5-el nő, azaz 6 lesz, majd pedig a 6-ból vonunk le 3-at, így a $szam2 változó értéke 3 lesz.

Ennek a cikknek a végére az összehasonlítások maradtak. Ezek hasonlítanak a logikai műveletekhez, olyan kifejezések, melyeknek igaz, vagy hamis lehet az értéke. Általában egy változó értékét vizsgáljuk meg segítségükkel (kisebb, mint 4?, üres sztring?, stb.). A sztringekre és a számokra külön összehasonlító operátorok vannak, az alábbi táblázat ezeket foglalja össze (az első oszlop a számokhoz, a második a sztringekhez tartozik):

>   - gt  - nagyobb, mint
>=  - ge  - nagyobb, vagy egyenlő
<   - lt  - kisebb, mint
<=  - le  - kisebb, vagy egyenlő
==  - eq  - egyenlő
!=  - ne  - nem egyenlő
<=> - cmp - összehasonlítás
A következő példák a számokra való használatukat mutatják be (sztringekre ugyanez, ha egy sztring akkor kisebb egy másiknál, ha az (angol) abc-ben előrébb van.

print 8>7;   # =1,  azaz igaz
print 7>8;   # ="", azaz hamis
print 8>=7;  # =1,  azaz igaz
print 7>=8;  # ="", azaz hamis
print 8<7;   # ="", azaz hamis
print 7<8;   # =1,  azaz igaz
print 8<=7;  # ="", azaz hamis
print 7<=8;  # =1,  azaz igaz
print 7==7;  # =1,  azaz igaz
print 7==8;  # ="", azaz hamis
print 7!=7;  # ="", azaz hamis
print 7!=8;  # =1,  azaz igaz

print 7<=>7; # =0,  azaz egyenlő
print 8<=>7; # =1,  azaz nagyobb
print 7<=>8; # =-1, azaz kisebb
Ebből a részből kimaradt az illesztő operátor működése, mivel annak megértéséhez a reguláris kifejezések ismerete szükséges. A reguláris kifejezések alfejezetben mind a magyarázata, mind pedig példák a működésére megtalálhatóak lesznek majd.

Végül lássuk a beígért egyszerű programot. Ebben olyan dolgokat is fel fogunk használni, amiről még nem tanultunk, de esetleg már más programnyelvből ismerős lehet. Ha nem értjük, nem baj, majd megértjük valamelyik jövőbeni cikkben, viszont végre láthatunk egy komplett Perl programot:

#!/usr/bin/perl

$szam = int(rand(100));

print "Gondoltam egy számra 0 és 100 között! Találd ki!\n";

while($bevitel = <STDIN>) {
  if ($bevitel < $szam)
    { print "Egy nagyobb számra gondoltam!\n"; }
  if ($bevitel > $szam)
    { print "Egy kisebb számra gondoltam!\n"; }
  if ($bevitel == $szam)
    { print "Gratulálok, a $szam számra gondoltam!\n"; exit(0); }
}
A programunk nem más, mint a klasszikusnak mondható iskolapélda, a "Gondoltam egy számot!" megvalósítása. Nagyon sok újdonság van benne, csak vázlatosan: az elején generálunk egy véletlenszámot 0 és 100 között, amit utána kerekítünk egy egész számra. Ezután kiírjuk a játék lényegét, majd amíg van a standard inputról (azaz általában a billentyűzetről) "jel", addig futtatunk egy ciklust, illetve a $bevitel változóba be is olvasunk egy sort. Ezután összehasonlítjuk a "gondolt" számmal, és ennek függvényében kiírunk egy szöveget. Ha egyenlő volt a két szám, akkor kilépünk a programból is.

A következő alkalommal az elágazásokkal, ciklusszervezéssel és a Perles eljárások alapjaival fogunk megismerkedni, és megint megnézünk egy kisebb programot ízelítőül.
 
Bártházi András arcképe
Bártházi András
Az Emarsys-nál dolgozik vezető fejlesztőként, és az API-ért, integrációkért felelős termékmenedzserként. Szeret profi csapatban profit alkotni.
1

Helyesbites

Anonymous · 2004. Aug. 5. (Cs), 12.29
A masodik peldaban:

$bonyolult=($osztas+kulonbseg)*($osztas);

Itt nem hianyzik a 'kulonbseg' elol a $ jel?
2

Már nem... :)

Bártházi András · 2004. Aug. 5. (Cs), 12.48
Javítottam, köszi a jelzést.

-boogie-
3

sorrend

foree · 2004. Dec. 23. (Cs), 12.06
"második blokk első sora a logikai VAGYot, a második a logikai ÉSt a harmadik..."

ez nem fordítva van? (&& jelenti az ÉS-t , || a VAGY-ot)
4

Igaz

Bártházi András · 2004. Dec. 23. (Cs), 12.38
Teljesen igazad van, javítottam.

-boogie-
5

Andrew

Anonymous · 2005. Nov. 7. (H), 22.17
Az elso példában ezt nem nagyon értem hogy miért pont ezt a ket szamot kellett beirni:

@szamok=(222,111);

Sokat gondolkodtam rajta de nem ugrott be!Bocs ha kicsit lama vagyok!:P
6

nem kellett

Hojtsy Gábor · 2005. Nov. 7. (H), 22.31
A cikk szerzőjének ez a két szám nagyon tetszett. Mivel itt egy tömbről van szó, bármit felvehetsz elemként, mindegy, hogy milyen számot írsz oda.
7

<Nincs cím>

Anonymous · 2005. Nov. 8. (K), 14.27
$ertek=$true || $false;
ezt a vagy karaktert nem talalom a keyboardomon!Hol talalhato ez?:P(gondoltam talan az a nagy I de nem az mnit lathatjatok:))

Andrew
8

kiosztástól függ

Anonymous · 2005. Nov. 8. (K), 14.35
A nyelvi kiosztástól függ, hogy hol van. Magyarban hagyományosan az altgr-w, azaz a jobb alt gomb és a w billentyű egyidejű lenyomásával érhető el.
9

<Nincs cím>

Anonymous · 2005. Nov. 8. (K), 15.03
nah jah megvan megtalatam !nekem a billentyűzeten mast jelolt!:P
Amugy ezt a logikai részt nem értem Valaki amgyarazz mar el jobban plz!
$true=1; $false=0;

$ertek=$true && $false;
$ertek=$true || $false;
$ertek=!$false;

$szam=3 & 1;  # $szam=1
$szam=5 | 2;  # $szam=7
$szam=5 ^ 3;  # $szam=6
$szam=5 << 2; # $szam=20
$szam=5 >> 2; # $szam=1
ez a rész nagyon homályos szamomra :(
Azt se értek hogy bitenéknti me hogy mi ez?">> <<"Meg egyáltalán mire jok ezek?
10

mire jók

Hojtsy Gábor · 2005. Nov. 8. (K), 19.49
Arra jók, amire éppen kellenek. Ha neked nincs rájuk szükséged, akkor nem lesz bajod abból, hogy nem tudod. A bitenként műveletekhez képezeld el, hogy kettő hatványai szerint vannak felírva a számok. Például

# 3 az 00000011 azaz kettő a nulladik hatványon plusz első hatványon
# 1 az 00000001 azaz kettő a nulladik hatványon
$szam = 3 & 1;
Itt most ha figyeled a biteket, akkor látod, hogy csak az utolsó egyezik meg, ami egy (kettő a nulladik hatványon). A többi is erre a kaptafára mennek.

A >> és << bitenkénti tolást jelent. Például 5 << 2 azért 20, mert 5 az 00000101 (azaz kettő a nulladikon meg kettő a négyzeten). Ezt ha eltolod kettővel, akkor 00010100 lesz, ami kettő a négyzeten (4) meg kettő a negyediken (16) azaz 20.

Különben tényleg ne izgasd magad miattuk, valóban ritkán van rájuk szükség, legalábbis webes környezetben.
11

<Nincs cím>

Anonymous · 2006. Feb. 1. (Sze), 13.35
$szam=-3**2;

nem feltetlenul azért mert a hatványpás magasabb művelet mint a kivonás hanem mert -3*-3 az 9, mert minusszor minusz az plusz!
12

<Nincs cím>

Anonymous · 2006. Feb. 4. (Szo), 12.46
$szam=5 << 2; # $szam=20

$szam=5 >> 2; # $szam=1

NAh várjunk!:P ezt nem értem hogy lesz az 5 >> 2 ; 20! vagy 5 << 2 ; 1!

$szam=3 & 1; # $szam=1

$szam=5 | 2; # $szam=7

$szam=5 ^ 3; # $szam=6

és ezeket se értem ! Kérem magyarázza el valaki! Köszönöm!
13

talán olvasni kellene

Hojtsy Gábor · 2006. Feb. 4. (Szo), 13.57
14

pontosvessző

tneva59 · 2006. Júl. 6. (Cs), 08.55
A while-t lezáró }-os zárójel után nem kell pontosvessző? Tulképpen működik nélküle is, vele is, de okoz-e galibát, ha a ciklus végéről vagy a program végéről lemarad a pontosvessző?
15

pontosvessző hiánya

Bártházi András · 2006. Júl. 6. (Cs), 12.46
A Perl liberális ebben a kérdésben. Biztosan nem kell, és a legtöbb helyen nem is szokták használni while ciklusnál (a while egyébként nem utasításként viselkedik, maga a kapcsos zárójel elválasztja az utasításokat). A program végére sem kell, bár jó gyakorlat lehet minden utasítás után kiírni, mert más nyelvek nem ilyen megengedőek, és ha több nyelven programozik az ember, csak hibaforrás lehet.