A InfoWiki wikiből
(Új oldal, tartalma: „<cim cim3="Vektorok" cim2="Magasszintű Programozási Nyelvek I." cim1="Imperatív, procedurális nyelvek alapjai" prev="mp1/page320" next="mp1/page410" kep="hz_c...”) |
Aktuális változat (2009. november 14., 19:49) (lapforrás) |
||
1. sor: | 1. sor: | ||
<cim | <cim | ||
- | cim3=" | + | cim3="Előírt lépésszámú ciklusok" |
cim2="Magasszintű Programozási Nyelvek I." | cim2="Magasszintű Programozási Nyelvek I." | ||
cim1="Imperatív, procedurális nyelvek alapjai" | cim1="Imperatív, procedurális nyelvek alapjai" | ||
15. sor: | 15. sor: | ||
- | = | + | = Előírt lépésszámú ciklusok = |
+ | Tekintsük meg az alábbi kódot: | ||
+ | |||
+ | <code lang="csharp"> | ||
+ | int i=0; | ||
+ | while (i<10) | ||
+ | { | ||
+ | ... | ||
+ | i++; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | A fenti ''while'' ciklus a ciklusmagját 10-szer fogja végrehajtani, mivel az ''i'' változó induló értéke 0, | ||
+ | minden menetben az ''i'' értéke pontosan 1-el nő, és akkor fogja elhagyni a ciklust, ha az ''i'' értéke elérte | ||
+ | (vagy meghaladta) a 10 értéket. | ||
+ | |||
+ | Gyakoriak azok az esetek, amikor előre ismert, hogy a ciklusmag hányszor kerül majd végrehajtásra. Ez esetben | ||
+ | a fentihez hasonló while ciklus alkalmazható, de nem túl szerencsés az alkalmazása. Az információk, amelyből | ||
+ | a kiderül, hogy ez a ciklus 10-szer ismétlődik majd - a kódban szétszórtan helyezkedik el. Ráadásul az ''i'' | ||
+ | változó deklarációja meg kell előzze a while ciklus indulását, így hatásköre (és az élettartama) nem a ciklusra | ||
+ | korlátozódik, hanem a ciklus lefutása után is megmarad. | ||
+ | |||
+ | A fenti problémák kezelésére speciális ciklusfajta szolgál, a '''for''' ciklus. | ||
+ | |||
+ | <box type="warn">A FOR ciklus akkor alkalmazható, ha a ciklus futása előre ismert darabszámú, mely darabszám a ciklus indításakor már kiszámolható, és később sem változik. Ha a ciklus futási darabszáma nem előre kiszámítható, vagy esetleg az változhat, akkor inkább WHILE ciklust kell alklamazni.</box> | ||
+ | |||
+ | |||
+ | = FOR fejrésze = | ||
+ | |||
+ | A ''for'' ciklus fejrésze három részre osztható, melyeket kettő darab pontosvessző határol. A részek szerepe: | ||
+ | * az inicializáló utasítás (mely a ciklus kezdetekor egyszer hajtódik végre | ||
+ | * a ciklus vezérlő feltétele (pozitív vezérlésű a for ciklus) | ||
+ | * a léptető utasítás (mely a ciklusmag lefutása után automatikusan kerül végrehajtásra) | ||
+ | |||
+ | Példa: | ||
+ | |||
+ | <code lang="csharp"> | ||
+ | for(int i=0; i<10; i++) | ||
+ | { | ||
+ | Console.Write(i); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | A fenti példa teljesen megfelel a korábban ismertetet while ciklus példában szereplő kód működésével. Az | ||
+ | ''i'' változó azonban itt a ciklusra lokális, tehát a for ciklus törzsén túl már nem terjed a hatásköre. | ||
+ | A ciklusmag minden lefutása után végrehajtódik az inkrementáló utasítás, vagyis az ''i'' értéke nő 1-el. | ||
+ | |||
+ | A példában szereplő for ciklus fejrészében minden információ szerepel, melyből meghatározható, hogy | ||
+ | ez a for ciklus pontosan 10-szer fog lefutni. A ciklusmagban az ''i'' változó értéke kiolvasható, | ||
+ | de megváltoztatása tilos. Ennek oka, hogy az ''i'' változó értékének átírása a pontos 10 ismétlést | ||
+ | módosítaná (vagy kevesebbszer, vagy többször kerülne végrehajtásra). | ||
+ | |||
+ | <box type="warn">A ciklusváltozó ciklusmagon belüli módosítása nem szintaktikai, hanem elvi hiba. A fordító eltűri, de ezen tevékenység ellentmond a for ciklus lényegének. Akinek ilyenre van szüksége, az használjon while ciklust.</box> | ||
+ | |||
+ | = Példa = | ||
+ | |||
+ | Hibás tehát az alábbi példa (bár nem szintaktikailag, hanem elvi, stílusbeli hiba). A programban | ||
+ | a 10 elemű tömböt csak pozitív számokkal kívánjuk feltölteni. Amennyiben a program kezelője valamely | ||
+ | elem értékének negatív számot kíván megadni, azt az elemet újra bekérjük: | ||
+ | |||
+ | <code lang="csharp"> | ||
+ | int[] tomb = new tomb[10]; | ||
+ | for(int i=0;i<10;i++) | ||
+ | { | ||
+ | Console.Write("{0}. szám értéke:"); | ||
+ | tomb[i] = int.Parse(Console.ReadLine()); | ||
+ | if (tomb[i]<0) i--; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | A feltételben az ''i--'' utasítás az ''i'' értékét csökkenti 1-el, amit | ||
+ | az inkrementáló utasítás (''i++'') meg fog növelni 1-el. E miatt negatív | ||
+ | szám beírása esetén az ''i'' értéke gyakorlatilag nem fog változni - a ciklus | ||
+ | következő lefutásakor is ugyanazon ''i'' tömbelem kerül feltöltésre. | ||
+ | |||
+ | Ez elegánsabban megoldható while ciklussal: | ||
+ | |||
+ | <code lang="csharp"> | ||
+ | int[] tomb = new tomb[10]; | ||
+ | int i=0; | ||
+ | while (i<10) | ||
+ | { | ||
+ | Console.Write("{0}. szám értéke:"); | ||
+ | tomb[i] = int.Parse(Console.ReadLine()); | ||
+ | if (tomb[i]>0) i++; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Vagyis csak akkor lépünk a következő tömbelem bekérésére, ha ezen tömbelemmel | ||
+ | elégedettek vagyunk. | ||
+ | |||
+ | Másik lehetséges megoldás: | ||
+ | |||
+ | |||
+ | <code lang="csharp"> | ||
+ | int[] tomb = new tomb[10]; | ||
+ | for(int i=0;i<10;i++) | ||
+ | { | ||
+ | do | ||
+ | { | ||
+ | Console.Write("{0}. szám értéke:"); | ||
+ | tomb[i] = int.Parse(Console.ReadLine()); | ||
+ | } | ||
+ | while (tomb[i]<0); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Ez esteben az ''i''-edik tömbelem bekérésől addig nem lépünk tovább, amíg | ||
+ | annak értékével elégedettek nem vagyunk. | ||
+ | |||
+ | = Trükkösebb ciklusok (a) = | ||
+ | |||
+ | A for ciklus fejrészében szereplő vezérlő feltétel tetszőlegesen összetett lehet, | ||
+ | hiszen az gyakorlatilag megfelel a while ciklus vezérlő feltételének szerepkörével: | ||
+ | |||
+ | <code lang="csharp"> | ||
+ | int db=0; | ||
+ | for(int i=0;i<10 && db<3; i++) | ||
+ | if (tomb[i]>0) db++; | ||
+ | </code> | ||
+ | |||
+ | Ez a for ciklus legkésőbb 10 menetben leáll. Korábban is leállhat, ha | ||
+ | a tömbben találtunk 3 pozitív számot. | ||
+ | |||
+ | Ez a működés némiképp ellentmond a korábban említett elvvel: a for ciklus | ||
+ | működésének darabszáma előre kiszámított kell legyen. Nos, a ez a határeset | ||
+ | kategória. Az ''i'' változó szerepe a tömb elemeinek indexelése, és ez a ciklus | ||
+ | sorra veszi a tömbelemeket. A ciklus ''maximális'' végrehajtási száma a fejben | ||
+ | adott (ez 10 darab). A korábbi kilépő feltétel belefér. | ||
+ | |||
+ | = Trükkösebb ciklusok (b) = | ||
+ | |||
+ | Az inkrementáló lépés nem mindíg az 1-el növelés. Tetszőleges utasítás megadható | ||
+ | azon a ponton: | ||
+ | |||
+ | <code lang="csharp"> | ||
+ | for(double a=0;a<1.5; a=a+0.1) { ... } | ||
+ | for(int i=10;i>0; i-- ) { ... } | ||
+ | for(int i=2;i<100; i=i+2 ) { ... } | ||
+ | for(int i=2;i<100; i=i*2-1 ) { ... } | ||
+ | </code> | ||
+ | |||
+ | = Trükkösebb ciklusok (c) = | ||
+ | |||
+ | A C nyelvben szerepel egy misztikus jellemzőjű vessző operátor (, operátor). A | ||
+ | vessző operátorral lehetséges egyetlen utasítást megengedő helyre több utasítást | ||
+ | is szerepeltetni. Erre szükség lehet olyan esetekben, amikor adott pontján | ||
+ | a programnak tilos programblokkot alkalmazni, mégis szeretnénk több utasítást | ||
+ | is végrehajtani. Ilyen hely a for ciklus léptető (inkrementáló) utasítsása. | ||
+ | Helytelen az alábbi: | ||
+ | |||
+ | <code lang="csharp"> | ||
+ | int ossz=0; | ||
+ | for( int i=0; i<10; {ossz=ossz+t[i];i++}) {} | ||
+ | Console.WriteLine("Az összeg={0}",ossz); | ||
+ | </code> | ||
+ | |||
+ | Ezzel szemben helyes az alábbi: | ||
+ | |||
+ | <code lang="csharp"> | ||
+ | int ossz=0; | ||
+ | for( int i=0; i<10; ossz=ossz+t[i],i++) {} | ||
+ | Console.WriteLine("Az összeg={0}",ossz); | ||
+ | </code> | ||
+ | |||
+ | A vessző operátor súlyos megkötése, hogy a vesszővel elválasztott utasítások adott | ||
+ | sorrendben hajtódnak végre (szekvencia szabály speciális esetre alkalmazása). Ezért | ||
+ | a fenti for ciklus még jól is működik. | ||
+ | |||
+ | Vegyük észre, hogy ebben az esetben a for ciklus magja üres. Ez megfelel annak | ||
+ | az esetnek, amikor a for ciklus magja egyetlen üres utasítást tartalmaz. Ez | ||
+ | esetben a pontosvessző írása a for ciklus fejrésze után nem hiba, hanem ezen | ||
+ | üres utasítás lezárása: | ||
+ | |||
+ | <code lang="csharp"> | ||
+ | int ossz=0; | ||
+ | for( int i=0; i<10; ossz=ossz+t[i],i++) ; | ||
+ | Console.WriteLine("Az összeg={0}",ossz); | ||
+ | </code> | ||
+ | |||
+ | A vessző operátor azonban a C nyelv azon misztikus, és sokat vitatott megoldása, | ||
+ | amely (mint kiderült) több baj forrása, mint amennnyi problémát megold. Ezért | ||
+ | a fenti kód a C# nyelvben bár működik, de kerülendő. | ||
+ | |||
+ | = A for fejrésze = | ||
+ | |||
+ | A for ciklus fejrésze minden esetben három részből áll, akkor is, ha valamely | ||
+ | utasítás az '''üres''' utasítás. A for ciklus fejrészéből csak a vezérlő | ||
+ | feltétel nem lehet üres, de mind a kezdőértékbeállítás, mind a léptetés | ||
+ | üresen hagyható: | ||
+ | |||
+ | <code lang="csharp"> | ||
+ | int i=int.Parse(Console.ReadLine()); | ||
+ | for( ;i<10; i++) | ||
+ | { | ||
+ | ... | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | A for ciklus fejrésze ekkor is három részből kell álljon! Vagyis az üres | ||
+ | utasítást is le kell zárni a pontosvesszővel. | ||
+ | |||
+ | <code lang="csharp"> | ||
+ | int i=0; | ||
+ | for( ;i<10; ) | ||
+ | { | ||
+ | ... | ||
+ | i++; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Ebben a példában szereplő for ciklus fejrészéből már mindkét elhagyható | ||
+ | utasítás elhagyásra is került. Ez már gyakorlatilag megfelel a korábban | ||
+ | felírt while ciklus működésének. | ||
+ | |||
+ | |||
+ | <code lang="csharp"> | ||
+ | for(int i=0;i<10; ) | ||
+ | { | ||
+ | ... | ||
+ | i++; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Ezek a hiányos for ciklus fejrészek szintaktikai hibát nem okoznak, de | ||
+ | mivel szintén ellentétben állnak a for ciklus lényegét leíró működésnek, | ||
+ | használatuk stílustalan, nem javasolt. | ||
<alairas>Hernyák Zoltán</alairas> | <alairas>Hernyák Zoltán</alairas> | ||
__NOTOC__ | __NOTOC__ |
Aktuális változat
Előírt lépésszámú ciklusok
Tekintsük meg az alábbi kódot:
int i=0; while (i<10) { ... i++; }
A fenti while ciklus a ciklusmagját 10-szer fogja végrehajtani, mivel az i változó induló értéke 0, minden menetben az i értéke pontosan 1-el nő, és akkor fogja elhagyni a ciklust, ha az i értéke elérte (vagy meghaladta) a 10 értéket.
Gyakoriak azok az esetek, amikor előre ismert, hogy a ciklusmag hányszor kerül majd végrehajtásra. Ez esetben a fentihez hasonló while ciklus alkalmazható, de nem túl szerencsés az alkalmazása. Az információk, amelyből a kiderül, hogy ez a ciklus 10-szer ismétlődik majd - a kódban szétszórtan helyezkedik el. Ráadásul az i változó deklarációja meg kell előzze a while ciklus indulását, így hatásköre (és az élettartama) nem a ciklusra korlátozódik, hanem a ciklus lefutása után is megmarad.
A fenti problémák kezelésére speciális ciklusfajta szolgál, a for ciklus.
FOR fejrésze
A for ciklus fejrésze három részre osztható, melyeket kettő darab pontosvessző határol. A részek szerepe:
- az inicializáló utasítás (mely a ciklus kezdetekor egyszer hajtódik végre
- a ciklus vezérlő feltétele (pozitív vezérlésű a for ciklus)
- a léptető utasítás (mely a ciklusmag lefutása után automatikusan kerül végrehajtásra)
Példa:
for(int i=0; i<10; i++) { Console.Write(i); }
A fenti példa teljesen megfelel a korábban ismertetet while ciklus példában szereplő kód működésével. Az i változó azonban itt a ciklusra lokális, tehát a for ciklus törzsén túl már nem terjed a hatásköre. A ciklusmag minden lefutása után végrehajtódik az inkrementáló utasítás, vagyis az i értéke nő 1-el.
A példában szereplő for ciklus fejrészében minden információ szerepel, melyből meghatározható, hogy ez a for ciklus pontosan 10-szer fog lefutni. A ciklusmagban az i változó értéke kiolvasható, de megváltoztatása tilos. Ennek oka, hogy az i változó értékének átírása a pontos 10 ismétlést módosítaná (vagy kevesebbszer, vagy többször kerülne végrehajtásra).
Példa
Hibás tehát az alábbi példa (bár nem szintaktikailag, hanem elvi, stílusbeli hiba). A programban a 10 elemű tömböt csak pozitív számokkal kívánjuk feltölteni. Amennyiben a program kezelője valamely elem értékének negatív számot kíván megadni, azt az elemet újra bekérjük:
int[] tomb = new tomb[10]; for(int i=0;i<10;i++) { Console.Write("{0}. szám értéke:"); tomb[i] = int.Parse(Console.ReadLine()); if (tomb[i]<0) i--; }
A feltételben az i-- utasítás az i értékét csökkenti 1-el, amit az inkrementáló utasítás (i++) meg fog növelni 1-el. E miatt negatív szám beírása esetén az i értéke gyakorlatilag nem fog változni - a ciklus következő lefutásakor is ugyanazon i tömbelem kerül feltöltésre.
Ez elegánsabban megoldható while ciklussal:
int[] tomb = new tomb[10]; int i=0; while (i<10) { Console.Write("{0}. szám értéke:"); tomb[i] = int.Parse(Console.ReadLine()); if (tomb[i]>0) i++; }
Vagyis csak akkor lépünk a következő tömbelem bekérésére, ha ezen tömbelemmel elégedettek vagyunk.
Másik lehetséges megoldás:
int[] tomb = new tomb[10]; for(int i=0;i<10;i++) { do { Console.Write("{0}. szám értéke:"); tomb[i] = int.Parse(Console.ReadLine()); } while (tomb[i]<0); }
Ez esteben az i-edik tömbelem bekérésől addig nem lépünk tovább, amíg annak értékével elégedettek nem vagyunk.
Trükkösebb ciklusok (a)
A for ciklus fejrészében szereplő vezérlő feltétel tetszőlegesen összetett lehet, hiszen az gyakorlatilag megfelel a while ciklus vezérlő feltételének szerepkörével:
int db=0; for(int i=0;i<10 && db<3; i++) if (tomb[i]>0) db++;
Ez a for ciklus legkésőbb 10 menetben leáll. Korábban is leállhat, ha a tömbben találtunk 3 pozitív számot.
Ez a működés némiképp ellentmond a korábban említett elvvel: a for ciklus működésének darabszáma előre kiszámított kell legyen. Nos, a ez a határeset kategória. Az i változó szerepe a tömb elemeinek indexelése, és ez a ciklus sorra veszi a tömbelemeket. A ciklus maximális végrehajtási száma a fejben adott (ez 10 darab). A korábbi kilépő feltétel belefér.
Trükkösebb ciklusok (b)
Az inkrementáló lépés nem mindíg az 1-el növelés. Tetszőleges utasítás megadható azon a ponton:
for(double a=0;a<1.5; a=a+0.1) { ... } for(int i=10;i>0; i-- ) { ... } for(int i=2;i<100; i=i+2 ) { ... } for(int i=2;i<100; i=i*2-1 ) { ... }
Trükkösebb ciklusok (c)
A C nyelvben szerepel egy misztikus jellemzőjű vessző operátor (, operátor). A vessző operátorral lehetséges egyetlen utasítást megengedő helyre több utasítást is szerepeltetni. Erre szükség lehet olyan esetekben, amikor adott pontján a programnak tilos programblokkot alkalmazni, mégis szeretnénk több utasítást is végrehajtani. Ilyen hely a for ciklus léptető (inkrementáló) utasítsása. Helytelen az alábbi:
int ossz=0; for( int i=0; i<10; {ossz=ossz+t[i];i++}) {} Console.WriteLine("Az összeg={0}",ossz);
Ezzel szemben helyes az alábbi:
int ossz=0; for( int i=0; i<10; ossz=ossz+t[i],i++) {} Console.WriteLine("Az összeg={0}",ossz);
A vessző operátor súlyos megkötése, hogy a vesszővel elválasztott utasítások adott sorrendben hajtódnak végre (szekvencia szabály speciális esetre alkalmazása). Ezért a fenti for ciklus még jól is működik.
Vegyük észre, hogy ebben az esetben a for ciklus magja üres. Ez megfelel annak az esetnek, amikor a for ciklus magja egyetlen üres utasítást tartalmaz. Ez esetben a pontosvessző írása a for ciklus fejrésze után nem hiba, hanem ezen üres utasítás lezárása:
int ossz=0; for( int i=0; i<10; ossz=ossz+t[i],i++) ; Console.WriteLine("Az összeg={0}",ossz);
A vessző operátor azonban a C nyelv azon misztikus, és sokat vitatott megoldása, amely (mint kiderült) több baj forrása, mint amennnyi problémát megold. Ezért a fenti kód a C# nyelvben bár működik, de kerülendő.
A for fejrésze
A for ciklus fejrésze minden esetben három részből áll, akkor is, ha valamely utasítás az üres utasítás. A for ciklus fejrészéből csak a vezérlő feltétel nem lehet üres, de mind a kezdőértékbeállítás, mind a léptetés üresen hagyható:
int i=int.Parse(Console.ReadLine()); for( ;i<10; i++) { ... }
A for ciklus fejrésze ekkor is három részből kell álljon! Vagyis az üres utasítást is le kell zárni a pontosvesszővel.
int i=0; for( ;i<10; ) { ... i++; }
Ebben a példában szereplő for ciklus fejrészéből már mindkét elhagyható utasítás elhagyásra is került. Ez már gyakorlatilag megfelel a korábban felírt while ciklus működésének.
for(int i=0;i<10; ) { ... i++; }
Ezek a hiányos for ciklus fejrészek szintaktikai hibát nem okoznak, de mivel szintén ellentétben állnak a for ciklus lényegét leíró működésnek, használatuk stílustalan, nem javasolt.