Elágazások I.
Kétirányú elágazások (if-else szerkezet)
Talán feltűnt egyeseknek hogy az előző lecke 3. házi feladata milyen egyszerű probléma (csupán el kellett dönteni valamit), és mégis milyen bonyolult megoldást lehet adni rá az eddig megismert nyelvi elemekkel. Valami hasonló dologgal állunk szemben, mint a ciklusok elején, amikor a 0 és 100 közötti páros számokat kellett kiírni. Sejthető, hogy kell lennie egy olyan nyelvi elemnek, amivel (jelen esetben nem az ismételgetést, hanem) a döntési helyzeteket egyszerűen és átláthatóan lehet leírni. Az ilyen szerkezetet hívjuk elágazásnak! Nézzük meg a 3. feladat példáján, hogy hogyan kéne működni egy elágazásnak magyarul:
$szam = 12; Ha $szam < 10, akkor hajtsd végre az alábbi utasításokat: print "Kisebb mint 10"; vége ha nem teljesül, akkor pedig az alábbiakat: print "Nem kisebb mint 10"; vége
A "vége" szóval itt az utasítássorozat végét jelzem, ami jelen esetben csak egy utasításból áll. Ez PHP nyelven így néz ki:
$szam = 12;
if ($szam < 10){
print "Kisebb mint 10";
}
else{
print "Nem kisebb mint 10";
}
Egy elágazás általános alakja:
if (feltétel){ utasítások1 }
else{ utasítások2 }
Persze érdemes itt is a ciklusoknál megismert formát követni:
if (feltétel){
utasítások1
}
else{
utasítások2
}
A dologban annyi az extra, hogy az else{ utasítások2 } rész elhagyható, erre nemsokára visszatérünk.
Az elágazás tehát úgy néz ki, hogy az if kulcsszó után kerek zárójelbe írjuk a feltételt, majd az utasításokat kapcsos zárójelek határolják, akárcsak a ciklusoknál. Ha a feltétel teljesül, akkor az if-hez tartozó ág fut le, vagyis az utasítások1 helyén lévő utasítások hajtódnak végre, az utasítások2 viszont nem! Ha nem teljesül a feltétel, akkor pont fordítva történik: az else ág (utasítások2) hajtódik végre. És természetesen miután valamelyik ág lefutott, az else-hez tartozó záró kapcsos zárójel utáni első utasítás fog következni. Az if és az else utáni kapcsos zárójelekkel határolt utasításlistákat ágaknak szokták nevezni, mivel az if-nél a program végrehajtása kettéágazik: vagy az if utáni, vagy az else utáni utasítások futnak le, olyan nincs hogy mindkettő vagy egyik sem (mivel a feltétel vagy igaz vagy hamis)!
Imént említettem, hogy az else ág elhagyható. Ez azt jelenti, hogy ha az if után írt feltétel teljesül, akkor végrehajtódik az if ág utasításlistája, ha nem teljesül akkor pedig nem történik semmi.
Bízom benne, hogy ez a szerkezet nem okoz senkinek különösebb traumát, szerintem a ciklusnál egyszerűbb. Épp ezért nézzünk is mindjárt egy példát a használatukra:
Tegyük fel, hogy a felhasználótól bekértünk egy jelszót. Ilyet például űrlapokkal lehet csinálni, amivel a tanfolyam második része foglalkozik, nekünk most elég annyit tudni, hogy az általa megadott jelszó a $pass változóba került. Az érvényes jelszó legyen az hogy "Nemtudom!" (idézőjelek nélkül)! Ha ezt adja meg, akkor megdícsérjük és megtekintheti a jelszóval védett oldalt, ha nem, akkor nem tekintheti meg:
if ($pass == "Nemtudom!"){
print "Ügyes vagy, jó jelszót írtál be!";
}
else{
print "Hát ez nem jött össze!";
exit();
}
// Ezután a sor után jön az oldal tartalma...
Lássuk, hogy működik a fenti program! Az == operátor vizsgálja azt, hogy a két oldalán lévő kifejezés megegyezik-e, ahogy a ciklusok részben már írtam. Ha jó jelszót írtunk be, akkor az if ág fut le: megdícsérjük a látogatót, majd (az else ágat átugorva) kiíródik az oldal tartalma. Viszont ha nem jó a jelszó, akkor az else ág fut le: egy másik szöveget írunk ki, ezután viszont gondoskodnunk kéne arról, hogy az oldal tartalma ne jelenjen meg. Erre szolgál az else ágban lévő exit(); utasítás! Erről eddig még nem volt szó, de nem kell megijedni, egyszerűen csak annyit csinál, hogy megszakítja a program futását, így az utána lévő utasítások nem fognak végrehajtódni (se PHP, se HTML), vagyis az oldal tartalma nem jelenik meg.
Természetesen elágazásokat használhatunk cikluson belül, és fordítva is igaz, az elágazás bármelyik ágába rakhatunk ciklust. Ilyen egymásba ágyazott szerkezetre is nézzünk egy egyszerűbb példát:
Írjuk ki 6-től 12-ig a számokat egymás alá, de úgy hogy a megfelelő helyiértékek egymás alatt legyenek (úgy is mondhatnám hogy legyen jobbra igazítva)! A kimenet tehát legyen ilyen:
6
7
8
9
10
11
12
Ez a probléma megoldható a HTML align="right" paraméterével (mondjuk táblázatot használva), de most próbáljuk meg szóközzel biztosítani ezt! Tehát azt kell tennünk, hogy ha a szám egyjegyű (kisebb mint 10), akkor kiírunk elé egy szóközt, ha nem akkor pedig nem írunk ki.
$n = 6;
while ($n <= 12){
if ($n < 10){
print " ";
}
print $n."<br />";
++$n;
}
Itt nem szükséges szóköz helyett a kódot használni, mivel csak egyet írunk ki. Az else ág pedig elhagyható, mert ha a szám nem kisebb 10-nél, akkor nem kell elé írni semmit. Ha ki akarjuk próbálni a programot, akkor használjuk a "Courier New" betűtípust, mert így azonos szélességű lesz minden karakter (csak így lesznek pontosan egymás alatt a megfelelő számjegyek).
Logikai értékek
Most kicsit részletesebben foglalkozunk azzal, hogy pontosan miket írhatunk az if utáni feltételbe (de az itt mondottak ciklusfeltételek esetén is érvényesek)! Ahhoz hogy ezeket megértsük vissza kell nyúlnunk a változók értékadásának a témájáig. Ott arról volt szó, hogy egy változóba rakhatunk egész számot, tizedestörtet és karakterláncot. Viszont más adattípusok is léteznek, ezek csak a leggyakoribbak. A változóknak logikai értéket is adhatunk, vagyis igaz vagy hamis értéket. Ezt a következőképpen tehetjük meg:
$a = true; // Logikai igaz érték $b = false; // Logikai hamis érték
A logikai igaz érték jelzésére a true vagy TRUE szolgál, a hamis pedig false vagy FALSE. A kis- és nagybetűs forma egyenértékű, mindenki azt használhatja, amelyik jobban tetszik! Visszatérve az if után írható feltételhez: az a helyzet, hogy ide bármilyen kifejezést írhatunk. Minden kifejezésnek van valamilyen értéke, ha if utáni feltételbe vagy ciklusfeltételbe kerül, akkor ez az érték átkonvertálódik logikai értékké (ha eredetileg nem volt az). Ha az így kapott érték true, akkor az if ág kerül végrehajtásra (illetve ciklus esetén végrehajtódik a ciklusmag), ha false akkor pedig az else ág fut le (illetve véget ér a ciklus).
Feltételes kifejezés esetén (vagyis a < > <= >= == != operátorok egyikével készített kifejezés esetén) nincs probléma, a kifejezés értéke true vagy false attól függően hogy a feltétel teljesül-e vagy sem. Értelemszerűen true ha teljesül, false ha nem. Ha a kifejezés értéke szám, akkor átkonvertálódik logikai értékké: ha a szám nulla, akkor false értékű lesz, különben true. Ha karakterlánc akkor szintén átalakul: ha üres ("") vagy egyetlen nullát tartalmaz ("0") akkor false lesz az értéke, minden más esetben true.
Ezeket a konverziós szabályokat viszont nem feltétlenül kell észben tartani, mivel általában feltételes kifejezést használunk az elágazások és ciklusok feltételeiben. Mellékesen megjegyzem, hogy most megismertük a legegyszerűbb módot végtelen ciklus írására is:
while (true){
// utasítások
}
Itt mindegy milyen utasításokat írunk a ciklusmagba, a feltétel mindig igaz logikai értékű lesz, így a ciklus mindig újraindul (azért mégsem teljesen mindegy, mert van olyan utasítás, amivel ciklusból ki lehet lépni, de ezt majd csak a 13. leckében ismerjük meg).
A jelszóellenőrző példa kapcsán fontosnak tartom, hogy pár szót ejtsünk az == operátorról, ugyanis ez egy gyakori hibaforrás, viszont elágazásokban gyakran használjuk.
A kezdő programozók sok esetben követik el azt a hibát, hogy = operátort használnak ott, ahol ==-t kellene, mivel a matekban az előbbit használjuk egyenletekben, és talán kicsit természetesebbnek is tűnik. Vajon mi történik, ha ezt írjuk:
if ($pass = "Nemtudom!"){ // HIBÁS!!!
print "Ügyes vagy, jó jelszót írtál be!";
}
else{
print "Hát ez nem jött össze!";
exit();
}
Az if utáni kifejezésben most nem összehasonlítás áll, hanem értékadás! Az értékadás is kifejezés, az értéke az, amit értékül adtunk a benne lévő változónak, vagyis jelen esetben a "Nemtudom!" karakterlánc. A fenti konverziós szabályok szerint ez a feltételben true értékké alakul függetlenül attól, hogy mi van (pontosabban csak volt) a $pass változóban. Ezért a feltétel igaz lesz, vagyis mindig az if ág hajtódik végre, az oldal tartalma akkor is megjelenik, ha rossz jelszót adtunk meg. Nyilván ez nem túl szerencsés dolog.
Azért említettem meg ezt a hibát, mert amellett hogy gyakran előfordul, nehezen felderíthető, mivel maga a kód nyelvtanilag helyes, így aztán nem kapunk semmilyen hibaüzenetet.
Többirányú elágazások (else-if szerkezet)
Sokszor szükség lehet arra, hogy ne csak kettő, hanem több esetet tudjunk megkülönböztetni, és ettől függően kettőnél több ágat tudjunk létrehozni. Ezt persze tehetnénk úgy, hogy az elágazás egyik ágába beágyazunk egy másik elágazást (ilyet természetesen lehet csinálni), de sokkal áttekinthetőbb kódot kapunk, ha az else-if nevű szerkezetet használjuk, ami általános esetben így néz ki:
if (feltétel_1){
utasítások_1;
}
else if (feltétel_2){
utasítások_2;
}
else if (feltétel_3){
utasítások_3;
}
...
else{
utasítások_n;
}
A ... azt jelenti, hogy ide tetszőleges számú további else if -fel kezdődő ágat rakhatunk.
Valójában ez a szerkezet ugyanaz mint amit fent láttunk, csak az else ágba be vannak ágyazva újabb elágazások. Persze akkor megkérdezhetnénk, hogy hova lettek a kapcsos zárójelek, meg a behúzás ami a beágyazást szemlélteti, de most inkább ne foglalkozzunk ezzel, hanem nézzünk erre úgy, mint egy újfajta szerkezetre!
A szerkezet úgy működik, hogy sorban végigvizsgálódnak a feltételek. Az if ág az utána lévő else if ágakkal tulajdonképpen teljesen egyenértékű. Sorban vizsgálódnak a feltételek, és amint az egyik igaz, az ahhoz az ághoz tartozó utasítások végrehajtódnak, majd ha ezekkel kész a program, átugorja az egész kóceráj hátralévő részét! Vagyis ugyanúgy, mint a kétágú if-else szerkezetnél, csak egy ág hajtódik végre. Ha a feltételek közt nincs átfedés (ami azt jelenti hogy legfeljebb egy teljesülhet, több nem), akkor az ágak sorrendje lényegtelen. Ha több is igaz lehet, akkor nyilván nem, mert akkor a legelső igaz ág fog csak végrehajtódni, a többi már nem! Az utolsó (else) ág akkor hajtódik végre, ha mindegyik feltétel hamis. Az else ág itt is elhagyható, ha ilyenkor egyik feltétel se teljesül, akkor nem történik semmi, ugyanúgy mint az egyetlen if ág esetén.
Bővítsük a 10-es számot vizsgáló programot, hogy nagyobb, kisebb és egyenlő feltételeket is vizsgáljon:
if ($szam < 10){
print "Kisebb mint 10";
}
else if ($szam == 10){
print "Egyenlő 10-zel";
}
else{
print "Nagyobb mint 10";
}
Teljesen egyértelmű, hogy az utolsó (else) ág csak akkor futhat le, ha a szám nagyobb mint 10, mert a másik két lehetőséget már megvizsgáltuk, több pedig nincs! Készítsük fel a számokat egymás alá kiíró programot 3 és 4 jegyű számok kezelésére!
$n = 1;
while ($n <= 1000){
if ($n < 10){
print " ";
}
else if ($n < 100){
print " ";
}
else if ($n < 1000){
print " ";
}
print $n."<br />";
++$n;
}
Látható, hogy itt a feltételek átfedik egymást, de ez nem baj, mert az első ág akkor hajtódik végre, ha ($n < 10), a második akkor, ha (10 <= $n < 100), mivel a kisebb mint 10 eseteket az előző ág "megette"! A harmadik pedig akkor, ha (100 <= $n < 1000). Ha $n 4 jegyű, akkor pedig nem írunk ki elé szóközt, ezért nem kell else ág. Mellesleg ez a program 1-től 1000-ig írja ki a számokat.
Érdemes megjegyezni, hogy többszörös elágazást tudunk csinálni több magányos (else ág nélküli) if ág egymás után rakásával, de ez csak akkor tekinthető többszörös elágazásnak, ha az ágak feltételei közt nincs átfedés. Ha ugyanis van, akkor több ág is le fog futni, mert ezek egymástól független if-else szerkezetek!
Házi feladat
1.) Készítsük el egy számkitaláló program magját (ez azt jelenti, hogy a felhasználótól űrlapon érkezik a szám, de azt még nem tudjuk megcsinálni)! Tegyük fel, hogy a felhasználó által tippelt szám mindig a $tipp változóba kerül, a gép által "gondolt" szám pedig a $szam változóban van! Valahogy úgy kéne kinéznie, hogy egy if (nem találta ki){...} alakú feltételt írunk, vagy legalábbis egy ezzel egyenértékűt. A felhasználó nyilván csak úgy tudja kitalálni előbb-utóbb a számot, ha útbaigazítást adunk neki, hogy kisebb vagy nagyobb számra gondolt-e a gép!
Egy lehetséges megoldás:
$szam = 10;
if ($tipp != $szam){
if ($tipp < $szam){
print "Ennél nagyobb!";
}
else{
print "Ennél kisebb!";
}
}
else{
print "Kitalálta!";
}
Az if ág akkor fut le, ha a tipp nem azonos a kitalált számmal, és útbaigazítást ad a felhasználónak, hogy kisebb vagy nagyobb; a $tipp == $szam feltételt nyilván nem kell megvizsgálni, mivel ha ez lenne igaz, akkor nem lennénk az if ág belsejében. Ha megegyezik a két szám, az if ág helyett az else ág fut le.
Mivel három lehetséges eset van, ezért a program egyszerűbben megírható az else-if szerkezet segítségével:
$szam = 10;
if ($tipp < $szam){
print "Ennél nagyobb!";
}
else if ($tipp > $szam){
print "Ennél kisebb!";
}
else{
print "Kitalálta!";
}







