Utolsó frissítés: 2012.02.13.
Facebook Twitter iWiW MySpace Digg Delicious Google bookmarks Startlap Windows Live

Ciklusok I.

Ahogy a legutóbbi lecke elején ígértem, most belevágunk az "igazi" programozásba!

Ciklusok létrehozása

A programozás leglényegesebb elemei a ciklusok! A működésüket és hasznukat az alábbi probléma megoldásán keresztül mutatom meg:
Mit tegyünk ha ki akarjuk íratni 2-től 100-ig az összes páros számot? Az eddigi ismeretek alapján a megoldás ez lenne:

	$n = 2;
	print $n;    //2
	$n = $n + 2;
	print $n;    //4
	$n = $n + 2;
	print $n;    //6
	$n = $n + 2;
	// ... stb. ...
	$n = $n + 2;
	print $n;    //98

Amint látjuk, másolás-beillesztés sorozatokat kellene csinálnunk éjjel-nappal! Tulajdonképpen már a hatványozó program esetén is láthattunk hasonló dolgot, bár ott csak háromszor kellett megismételni ugyanazt. Mivel a programozásban igen gyakran előfordul az ismételgetés, ezért ki kellett találni egy nyelvi elemet, amely segítségével csak egyszer kell leírni az ismételendő műveletet. A magyar nyelvben valami ilyesmit mondanánk:

	$n = 2;
	ismételd az alábbi műveletsort addig, amíg $n kisebb mint 100:
		print $n;
		$n = $n + 2;
	vége

Ez PHP nyelven az alábbi módon hangzik:

	$n = 2;
	while ($n < 100){
		print $n;
		$n = $n + 2;
	}

A < kacsacsőr jelentése "kisebb mint", akárcsak a matekban! A PHP-ben egy ciklus általános esetben így néz ki:

while (feltétel){ utasítások }

A while egy kulcsszó, ami azt jelzi hogy most egy ciklus jön, és azonosítja a ciklus típusát (többféle ciklus is van a PHP-ben, ezek közül a while ciklus a legegyszerűbb; a "while" angol szó, jelentése "amíg"). A while után kerek zárójelek közt szerepel egy feltétel (ez a ciklusfeltétel). Amíg a feltétel teljesül, addig a kapcsos zárójelek közti utasítások végrehajtódnak, majd a feltétel újra megvizsgálódik (a kapcsos zárójelek közti utasítások listáját ciklusmagnak nevezzük). Amint a feltétel hamissá válik, a ciklus véget ér. A ciklus ezen működését részletesen megnézzük az alábbi példán:
Hozzuk létre az alábbi kimenetet ciklus segítségével: 1, 2, 3, ...

	$szam = 1;
	while ($szam < 4){
		print $szam;
		print ", ";
		$szam = $szam + 1;
	}
	print "...";

Hogy működik a fenti kód? Először is beállítjuk a $szam változó értékét 1-re. Ezután jön a feltételvizsgálat: a $szam változó kisebb-e mint 4? Mivel 1 kisebb mint 4, ezért elkezdődik a kapcsos zárójelek között lévő utasítások (ciklusmag) végrehajtása. Kiírjuk a $szam változó értékét majd a vesszőt, utána pedig növeljük a változót 1-gyel. Vége a ciklusmagnak, most megint megvizsgálódik a feltétel. $szam értéke már 2, de mivel 2 < 4 teljesül, ezért a ciklusmag újra végrehajtódik: kiíródik a 2-es, és a $szam értéke 3 lesz. A feltétel megint teljesül, megint lefut a ciklusmag, kiíródik a 3-as. Most viszont $szam értéke a ciklusmag végén 4 lesz. A feltétel ismét megvizsgálódik: 4 < 4 hamis, ezért a ciklusmag már nem fog lefutni, hanem átugrunk a mag utáni első utasításra (vagyis arra, ami már a kapcsos zárójelen kívül van). Ez pedig a print "..."; vagyis az eddig kiírt sor végére kiírjuk a "..." karakterláncot.
Természetesen a fenti kódot lerövidíthetjük a legutóbb tanult összefűző és növelő operátorok segítségével:

	$szam = 1;
	while ($szam < 4){
		print $szam.", ";
		++$szam;
	}
	print "...";

Formai javaslatok

A fenti kódokban a ciklusmag utasításait beljebb írtam, mint a többi sort. Ezt azonban nem írja elő a PHP nyelvtana, mint ahogy a kerek zárójel előtti vagy a kacsacsőr körüli szóközöket sem. Az utasításokat sem szükséges külön sorba írni, a fenti program így is működik:

	$szam=1;
	while($szam<4){print $szam.", "; ++$szam;}
	print "...";

Sőt, akár a ciklus előtti és utáni utasításokat is írhatnánk egy sorba a ciklussal, de úgy sokkal áttekinthetetlenebb lenne a program. A ciklusmag utasításainak behúzása azt a célt szolgálja, hogy mutassa a program hierarchikus felépítését, és persze hogy könnyen látható legyen hogy hol a ciklus eleje és vége.

Összehasonlító operátorok

Miket írhatunk a ciklusfeltételbe (vagyis mik lehetnek a kerek zárójelek között)? Olyan operátorokat kell itt használni, amelyekről eddig nem volt szó, most viszont gyorsan lerendezzük őket! A fontosabb összehasonlító operátorok:

	operátor    jelentés

	   <        kisebb mint
	   >        nagyobb mint
	   <=       kisebb vagy egyenlő
	   >=       nagyobb vagy egyenlő
	   ==       egyenlő
	   !=       nem egyenlő

Az első kettőtől remélem senki nem kap szívrohamot, ugyanúgy néznek ki mint a matematikában és ugyanúgy is kell őket használni! A 3. és 4. esetén a használat szintén ugyanígy megy, csak kicsit furán néznek ki, mivel a matekban az egyenlőségjelet a kacsacsőr alá írjuk. Egy szövegszerkesztőben viszont ezt nem lehet, így az egyenlőségjelet utána kell raknunk! Az egyenlőség operátor azért dupla egyenlőségjel, mert a szimpla (=) az értékadó operátor (a kettő közti különbséget az "Elágazások" tananyagban fogjuk megvizsgálni, ciklusokban általában nem szokták használni). A nem egyenlőt, ami a matekban egy áthúzott egyenlőségjel, pedig úgy is fel lehet fogni hogy kisebb vagy nagyobb.
Mindegyik összehasonlító operátor kétoperandusú, a két oldalán egyaránt állhatnak kifejezések (más operátorok segítségével gyártott összetett kifejezések is), számok és karakterláncok is. A == és != operandusai felcserélhetők, a többié viszont csak akkor, ha az operátort ellenkezőjére változtatjuk (pl. $n < 5 egyenértékű az 5 > $n feltétellel). Nézzünk pár példát ezek használatára:

	// számok kiírása csökkenő sorrendben:
	$n = 5;
	while ($n >= 0){
		print $n;
		--$n;
	}

Ennek az eredménye ez lesz: 543210. Ha a >= helyett a > operátort használjuk, akkor az $n nulla értékénél a feltétel már nem teljesül, és ezt kapjuk: 54321.

	$x = 0;
	$szam = 99;
	while ($x != ($szam + 1)){
		print "Hörb";
		++$x;
	}

Ez meg 100-szor kiírja azt hogy Hörb (ráadásul sortörés nélkül). A program kipróbálása kötelező! :D
A ciklus addig megy, amíg $x értéke ($szam + 1)-gyel, vagyis 100-zal nem egyenlő. Bár itt a $szam + 1 kifejezést nem muszáj zárójelbe rakni, mivel az aritmetikai operátorok (+, -, *, /) az összehasonlító operátoroknál szorosabban kötnek, de kezdők számára ajánlott a zárójelezés, és a program így talán jobban olvasható is.
Az összehasonlító operátorok karakterláncok esetén is használhatók, de azokra a ciklusok esetén nem sok jó példát lehet mutatni, itt van esetleg ez:

	print "Itt kezdődik!<br />";
	$szoveg = "";
	while ($szoveg != "XXXXX"){
		print $szoveg."<br />";
		$szoveg = $szoveg."X";
	}

Az alábbi kimenetet produkálja a böngészőben:

Itt kezdődik!

X
XX
XXX
XXXX

Ez egy idétlen program, de tartalmaz egy-két újdonságot is. A 2. sorban a $szoveg változónak egy üres karakterláncot adunk értékül. Azért érdemes ezt tenni, mert ha nem kap semmilyen értéket, akkor nem használható biztonsággal a feltételvizsgálatban! Így viszont (mivel "" != "XXXXX" teljesül) a ciklus biztosan elindul. A ciklusmag első végrehajtásánál a print $szoveg."<br />"; jelentése print ""."<br />"; Az üres karakterlánc az összefűző operátor szempontjából olyan, mintha ott se lenne, így először csak egy sortörés (<br />) kerül kiírásra. Utána viszont hozzáfűződik az X betű, a ciklus újbóli végrehajtása során pedig az eddig felépített karakterlánchoz fűződik hozzá egy X karakter, és így tovább. Amikor pedig a $szoveg változó már 5 db X betűt tartalmaz, vagyis úgy néz ki hogy "XXXXX", a feltétel nem teljesül, és átugorjuk a ciklust.

Házi feladat

Mivel nagyon fontos hogy a ciklusok működését megértsük, javaslom hogy az összes fenti példaprogramot próbáld ki! Úgy is, hogy itt-ott megváltoztatsz benne egy-két dolgot (például a ciklusfeltételben más operátort használsz, a ciklusmagba beillesztesz további utasításokat, stb.)! Azért csak óvatosan, mert előfordulhat hogy hibaüzenetet kapsz, ezekről a következő leckében lesz szó. Ha pedig eleget szórakoztál, próbáld meg megoldani ezeket a feladatokat:

1.) Írjuk ki csökkenő sorrendben a negatív számokat (-1)-től (-50)-ig a következő formában:
-1, -2, -3, ... , -49, -50.
A ... helyén persze a többi számnak kell lennie. Próbálj a fentivel azonos kimenetet produkálni, azaz a számokat vesszővel válaszd el egymástól, de a -50 után pontot írass ki! Nem kell megijedni a negatív számoktól, ezek nem igényelnek különleges bánásmódot.

Megoldás

Egy lehetséges megoldás:

		$szam = -1;
		while ($szam > -50){
			print $szam.", ";
			--$szam;
		}
		print $szam.".";

A ciklus belsejében nincs semmi különös, (-1)-től indulunk, mindig kiírjuk a számot, utána a vesszőt, majd csökkentjük eggyel. A (-50)-es számot viszont már nem írja ki a ciklusmag, mivel ha ezt megengednénk, akkor utána kerülne a vessző. Ahhoz, hogy a (-50)-es szám után pontot írjunk, az ezt kiíró utasítást a cikluson kívül kell elhelyezni. A ciklus utáni sorban a $szam változó értékét használtam a -50 kiírására, de persze lehet azt is írni, hogy:
print (-50).".";
Azért lehet az utolsó sorban a $szam változót használni, mert a ciklusból akkor keveredünk ki, amikor a feltétel hamissá válik. Ez akkor történik, amikor a $szam (-50)-nel egyenlő. Ekkor már nem fut le a ciklusmag, hanem átugrunk a ciklus utáni első utasításra, ahol természetesen továbbra is használhatjuk a változót.

2.) Írassuk ki az 1 és 100 közötti 3-mal osztható számokat növekvő sorrendben! Próbáljuk meg úgy megcsinálni, hogy a számokat felváltva, egyszer vessző, egyszer pontosvessző választja el egymástól:
3, 6; 9, 12; 15, 18; ... , 96; 99,
Figyelem! A 102-t már nem írhatjuk ki! Majd meglátod hogy ezt miért mondtam! :D
Tipp: ne használd a nem egyenlő (!=) operátort, helyette inkább a < -et.

Megoldás

A 3-mal osztható számokat úgy írhatjuk ki, hogy az elsőt 3-nak választjuk, majd a számot mindig 3-mal növeljük. Ha csak ennek a feltételnek akarunk eleget tenni, a program így nézhet ki:

		// 2. házi - félkész változat
		$szam = 3;
		while ($szam < 100){
			print $szam.", ";
			$szam = $szam + 3;
		}

Ez a következő kimenetet adja: 3, 6, 9, 12, 15, 18, ... 96, 99,
Azért nem szerencsés, ha a != operátort használjuk, mert a $szam változó sosem lesz 100, így sosem érne véget a ciklus (ilyen problémákról a következő tananyagban lesz szó).
Ha felváltva akarunk vesszőt és pontosvesszőt írni, akkor a ciklusmagban két számot kell kiíratni, mivel az ismédlődés két számonként következik be. Ezt úgy értem hogy nem az ismétlődik hogy $szam.", " hanem az hogy $szam.", ".$szam."; ".

		// 2. házi - majdnem kész változat
		$szam = 3;
		while ($szam < 100){
			print $szam.", ";
			$szam = $szam + 3;
			print $szam."; ";
			$szam = $szam + 3;
		}

Az írásjelek váltakozó kiírása jól adja, viszont most kiírta a 102-es számot is! Nyilván le kell csökkentenünk a ciklusfeltételben lévő számot, hogy hamarabb véget érjen a ciklus. Ha például 99-et írunk oda, akkor már nem írja ki a 102-est, de a 99-es is eltűnt mivel a ciklusmag egyszerre két számot is kiír, és most eggyel kevesebbszer futott le. A 99-es számot megint csak a ciklus után írva tudjuk berakni:

		// 2. házi - végleges változat (A verzió)
		$szam = 3;
		while ($szam < 99){
			print $szam.", ";
			$szam = $szam + 3;
			print $szam."; ";
			$szam = $szam + 3;
		}
		print $szam.",";

A ciklusmagot azonban máshogy is meg lehet írni, nem feltétlenül kell kétszer növelni a változót. Lehet úgy is, hogy kiírjuk a $szam-ot, utána a vesszőt, majd a ($szam+3)-at és ezután a pontosvesszőt. Ilyenkor persze a ciklusmag végén a $szam-ot 6-tal kell növelnünk:

		// 2. házi - végleges változat (B verzió)
		$szam = 3;
		while ($szam < 99){
			print $szam.", ".($szam + 3)."; ";
			$szam = $szam + 6;
		}
		print $szam.",";

3.) Gyártsuk le a 3-as szorzótáblát! A 3-as számot változóban add meg, és próbáld meg úgy csinálni, hogy ennek az egy változónak az átírásával egy másik szorzótáblát írjon ki! Pl. ha a 3-ast átírom 8-ra, akkor a 8-as szorzótáblát dobja elém. A kimenet lehet valami ilyesmi:
1 x 3 = 3
2 x 3 = 6
3 x 3 = 9
...
9 x 3 = 27

Megoldás

A 3-as számot változóban kell tárolni, és nem változtathatjuk meg, mivel a második oszlop 3-asait ezzel kéne kiíratni, és a szorzatok kiszámításához is ez kell. A feladat lényege, hogy be kell vezetni még egy változót, amit az első oszlop kiírására (és persze a szorzatok kiszámítására) használunk:

		// Szorzótábla
		$szam = 3;
		$n = 1;
		while ($n < 10){
			print $n." x ".$szam." = ".($n * $szam)."<br />";
			++$n;
		}

Az $n a futó változó, ami kezdetben 1 és folyamatosan növekszik a ciklusban. Ha a $szam változó értékét átírjuk, akkor pedig egy másik szorzótáblát kapunk, mivel a cikluson belül sehol sem használtuk a 3-as számot, csak a változót!

Na, azt hiszem ennyi elég lesz egyszerre. Némelyik feladat nem is olyan nehéz, mint első látásra tűnik! Ha a harmadik nagyon nem megy, akkor először próbáld meg ciklus nélkül megcsinálni (hasonlóan a 4. leckében lévő hatványozó programhoz). Kellemes fejtörést! :)