fel
le

Léptetős ciklusok

Elsősorban a vektorok kezelésével kapcsolatosan gyakoriak azok a ciklus-alakok, ahol valamilyen változót végig kell futtatni 0..n-1 értékek között. Az ilyen ciklusokat meg tudjuk fogalmazni az alábbi módon:

int i=0;
while (i<n)
{
   // ..
   // tevékenység
   // ..
   i++;
}
 

Az ilyen ciklusok közös ismérve, hogy valamilyen ciklusváltozó (jelen esetben az i) kezdőértékének beállításával indulunk (ez jellemzően 0 értékről indul), a ciklus vezérlő feltételében az i értékét nem engedjük hogy elérje az n értékét, és minden ciklusmenet végén növeljük az i értékét 1-el.

Az ilyen alakú ciklusok speciális alakja miatt saját ciklusfajtája alakult ki. Ez az alak annyira gyakori a programozásban, hogy ez a speciális ciklus valójában gyakrabban szerepel programkódokban, mint az eredeti őse, a while ciklus.

for(int i=0; i<n; i++)
{
   // ..
   // tevékenység
   // ..
}
 

Mint láthatjuk, a for ciklusban pontosan a fenti három rész kerül kiemelésre és hangsúlyozásra, a for ciklus fejrészében. A for ciklus fejrésze e miatt mindíg három részből áll:

  • ciklusváltozó kezdőértékbeállítása
  • vezérlő feltétel
  • léptető utasítás

A három részt kettő pontosvessző határolja egymástól. A három rész egyike sem kötött szintaxisú, nyilván bizonyos megkötésekkel: pl. a középső résznek egy logikai kifejezést (feltételt) kell leírnia, míg az első és utolsó résznek egyszerű végrehajtható utasításnak kell lennie. Szokás a ciklusváltozót a for ciklus fejrészében deklarálni, de nem kötelező:

int i;
for(i=0; i<n; i++)
{
   // ..
   // tevékenység
   // ..
}
 

Nyilván, ha a ciklusváltozó már korábban deklarálásra került, továbbra is tilos újra deklarálni a hatáskörén belül, ezért a fenti esetben a for ciklus fejrésze pl. nem tartalmazza a deklarációt, csak a kezdőértékadást. De ez sem kötelező:

int i=0;
for( ; i<n; i++)
{
   // ..
   // tevékenység
   // ..
}
 

A fenti esetben az i változó nem csak deklarálásra került a ciklus előtt, de kezdőértéke is megfelelő a ciklus kezdetekor, így annak értéke már nem kerül beállításra a ciklus fejrészében. A helyét ekkor is fenn kell tartani, ezért a pontosvessző mint határolójel lezárja ezt a szakaszt. Ekkor úgy fogalmazunk, hogy a kezdőértékbeállítás egy üres utasítás.

Az üres utasítás jellemzője, hogy fizikailag nincs ott a kódban semmi írva, de mivel azon a helyen utasításnak kell szerepelnie, így pontosvessző zárja, mint minden más, normális utasítást.

Hasonlóan nincs igazán megkötés a harmadik, növelő utasítás alakjára sem a fejlécben. Szokásosan ott a változó értékét szoktuk növelni 1-el, de bármi más is elképzelhető:

for(int i=0; i<n; i=i*2)
{
   // ..
   // tevékenység
   // ..
}
 

Ahogy az az eset is elképzelhető, hogy a ciklusváltozó értékének növelése vagy csökkentése ilyen egyszerűen nem írható le, ezért igazából a ciklusmagban akarjuk ezt elrendezni. Ekkor a for ciklus fejrészének ezen szakasza szintén elhagyható (üres utasítással helyettesíthető):

for(int i=0; i<n; )
{
   // ..
   // tevékenység
   // ..
   if ( i<n/2 ) i=i+1;
   else i=i+2;
}
 

Igazából, akár az alábbi eset is elképzelhető:

int i=0;
for( ; i<n ; )
{
   // ..
   // tevékenység
   // ..
   i++;
}
 

Ebben az alakjában a for ciklus igazából az ősére, a while ciklusra hasonlít, csak ezen szintaktika szokatlanabb. Nem szabad elfelejtkezni, hogy hiába hiányzik az inicializáló, vagy a növelő (vagy mindkettő) utasítás, a helyüket üres utasítás őrzi, így a pontosvesszőket mindíg ki kell rakni.

Szokások

Szokás a ciklusváltozókat (hagyománytiszteletből) i-vel jelölni. Ha az már foglalt, haladunk az ABC-ben előre, tehát j, k, l stb. ciklusváltozókat is szoktunk használni.

Igazából a for ciklus nem tud többet mint a while, ezért csak akkor alkalmazzuk, ha a while ciklusunk for alakú lenne. Ha nem ilyen alakú, akkor meghagyjuk a ciklusunkat eredeti while formában. Ezért a fenti példák bár valójában szintaktikailag helyesek voltak, de ilyen eltorzult alakban eleve nem is alkalmazzuk a for-t, ilyen alakot a programozók kódjában nem lehet találni.

Ha egy programozó for ciklust ír, akkor ezzel is tudatja magával (illetve másokkal), hogy a ciklus működése szabályos lesz. A ciklusváltozó kezdőértékéről indul, és minden menetben növelődik egyel, amíg el nem éri a beállított határt. Ezért bár szintaktikailag szintén működő, de ilyen szempontból nagyon zavaró megoldás az alábbi:

Console.WriteLine("Kérek 10 db páros számot:");
for( int i=0; i<10 ; i++)
{
 int x = int.Parse(Console.ReadLine());
 if (x%2!=0) i--;
}
 

Ebben úgy próbálunk trükközni, hogy ha x nem páros, akkor csökkentjük 1-el az i értékét, melyet a ciklusmenet végén a növelő utasítás újra növelni fog 1-el (i++). Vagyis ha nem páros számot írnának be, akkor végülis abban a ciklusmenetben az i értéke nem változna meg. Csak akkor tudna növelődni ténylegesen 1-el, ha a beírt szám is páros. Eképpen érjük el, hogy tényleg beírjanak 10 darab páros számot.

Ezek (és ehhez hasonló) trükkök persze működnek, de nagyon csúnyák. Ha egy másik programozó látja ezt a kódot, a for ciklus fejrészét, azonnal arra gondol, hogy ez a ciklus bizony 10-szer fog lefutni, és ehhez már nem kell beleolvasnia a for ciklus belsejében szereplő utasításokba, mert azt hiszi, minden, a lefutások számát befolyásoló információ ott szerepel. Azért hiszi ezt, mert ekkor szokás for ciklust írni. Ha ez nem így van, épeszű programozó nem for ciklust, hanem while ciklust ír. A fentit így kellett volna megoldani:

Console.WriteLine("Kérek 10 db páros számot:");
int i=0;
while ( i<10 )
{
 int x = int.Parse(Console.ReadLine());
 if (x%2==0) i++;
}
 
Hernyák Zoltán
2013-01-24 10:48:50