• Nem Talált Eredményt

Iterációs utasítások

4. Vezérlő utasítások

4.3. Iterációs utasítások

A következő példában bemutatjuk, hogyan lehet több esethez ugyanazt a programrészletet rendelni. A programban a válaszkaraktert feldolgozó switch utasításban az 'i' és 'I', illetve az 'n' és 'N' esetekhez tartozó case címkéket egymás után helyeztük el.

#include <iostream>

A programozási nyelveken utasítások automatikus ismétlését biztosító programszerkezetet iterációnak vagy ciklusnak (loop) nevezzük. A C++ nyelv ciklusutasításai egy ún. ismétlési feltétel függvényében mindaddig ismétlik a megadott utasítást, amíg a feltétel igaz.

while (feltétel) utasítás

for (inicializáló kifejezésopt; feltételopt; léptető kifejezésopt) utasítás do utasítás while (feltétel)

A for utasítás esetén az opt index arra utal, hogy a megjelölt kifejezések használata opcionális.

A ciklusokat csoportosíthatjuk a vezérlőfeltétel feldolgozásának helye alapján. Azokat a ciklusokat, amelyeknél az utasítás végrehajtása előtt értékelődik ki vezérlőfeltétel, az elöltesztelő ciklusok. A ciklus utasítása csak akkor hajtódik végre, ha a feltétel igaz. A C++ elöltesztelő ciklusai a while és a for.

Ezzel szemben a do ciklus utasítása legalább egyszer mindig lefut, hisz a vezérlőfeltétel ellenőrzése az utasítás végrehajtása után történik – hátultesztelő ciklus.

Mindhárom esetben a helyesen szervezett ciklus befejezi működését, amikor a vezérlőfeltétel hamissá (0) válik.

Vannak azonban olyan esetek is, amikor szándékosan vagy véletlenül olyan ciklust hozunk létre, melynek vezérlőfeltétele soha sem lesz hamis. Ezeket a ciklusokat végtelen ciklusnak nevezzük:

for (;;) utasítás;

while (true) utasítás;

do utasítás while (true);

A ciklusokból a vezérlőfeltétel hamis értékének bekövetkezte előtt is ki lehet ugrani (a végtelen ciklusból is).

Erre a célra további utasításokat biztosít a C++ nyelv, mint a break, a return, illetve a ciklus törzsén kívülre irányuló goto. A ciklus törzsének bizonyos utasításait átugorhatjuk a continue utasítás felhasználásával. A continue hatására a ciklus következő iterációjával folytatódik a program futása.

I.10. ábra - A while ciklus működésének logikai vázlata

4.3.1. A while ciklus

A while ciklus mindaddig ismétli a hozzá tartozó utasítást (a ciklus törzsét), amíg a vizsgált feltétel értéke true (igaz, nem 0). A vizsgálat mindig megelőzi az utasítás végrehajtását. A while ciklus működésének folyamata az elöző ábrán (I.10. ábra - A while ciklus működésének logikai vázlata) követhető nyomon.

while (feltétel) utasítás

A következő példaprogramban meghatározzuk az első n természetes szám összegét:

#include <iostream>

using namespace std;

int main() {

int n = 2012;

cout<<"Az elso "<<n<<" egesz szam ";

unsigned long sum = 0;

while (n>0) { sum += n;

n--;

}

cout<<"osszege: "<<sum<<endl;

}

A megoldás while ciklusát tömörebben is felírhatjuk, természetesen a program olvashatóságának rovására:

while (n>0) { sum += n;

n--;

}

while (n>0) sum += n--;

while (sum += n--, n);

A C++ nyelv megengedi, hogy a változók deklarációját bárhova helyezzük a program kódján belül. Egyetlen feltétel, hogy a változót mindenképpen deklarálnunk (definiálnunk) kell a felhasználása előtt. Bizonyos esetekben a változó-definíciót a ciklusutasítások fejlécébe is bevihetjük, amennyiben a változót azonnal inicializáljuk, például véletlen számmal.

Az alábbi példaprogram while ciklusa az első 10-el osztható véletlen számig fut. A megoldásban az

srand((unsigned)time(NULL));

utasítás a véletlen szám generátorát az aktuális idővel inicializálja, így minden futtatáskor új számsort kapunk. A véletlen számot a rand() függvény szolgáltatja 0 és RAND_MAX (32767) értékhatáron belül.

#include <iostream>

#include <cstdlib>

#include <ctime>

using namespace std;

int main() {

srand((unsigned)time(NULL));

while (int n = rand()%10) cout<< n<< endl;

}

Felhívjuk a figyelmet arra, hogy az így definiált n változó csak a while cikluson belül érhető el, vagyis a while ciklusra nézve lokális.

4.3.2. A for ciklus

A for utasítást általában akkor használjuk, ha a ciklusmagban megadott utasítást adott számsor mentén kívánjuk végrehajtani (I.11. ábra - A for ciklus működésének logikai vázlata). A for utasítás általános alakjában feltüntettük az egyes kifejezések szerepét:

for (inicilizáció; feltétel; léptetés) utasítás

A for utasítás valójában a while utasítás speciális alkalmazása, így a fenti for ciklus minden további nélkül átírható while ciklussá:

inicilizáció;

while (feltétel) { utasítás;

léptetés;

}

I.11. ábra - A for ciklus működésének logikai vázlata

Példaként a for ciklusra, tekintsük a már megoldott, természetes számok összegét meghatározó programot!

Azonnal látható, hogy a megoldásnak ez a változata sokkal áttekinthetőbb és egyszerűbb:

#include <iostream>

using namespace std;

int main() {

unsigned long sum;

int i, n = 2012;

cout<<"Az elso "<<n<<" egesz szam ";

for (i=1, sum=0 ; i<=n ; i++) sum += i;

cout<<"osszege: "<<sum<<endl;

}

A példában szereplő ciklus magjában csak egyetlen kifejezés-utasítás található, ezért a for ciklus az alábbi tömörebb formákban is felírható:

for (i=1, sum=0 ; i<=n ; sum += i, i++) ; illetve

for (i=1, sum=0 ; i<=n ; sum += i++) ;

A ciklusokat egymásba is ágyazhatjuk, hisz a ciklus utasítása újabb ciklusutasítás is lehet. A következő példában egymásba ágyazott ciklusokat használunk a megadott méretű piramis megjelenítésére. Mindhárom ciklusban lokálissá tettük a ciklusváltozót:

#include <iostream>

using namespace std;

int main () {

const int maxn = 12;

for (int i=0; i<maxn; i++) { for (int j=0; j<maxn-i; j++) { cout <<" ";

}

for (int j=0; j<i; j++) { cout <<"* ";

} cout << endl;

} }

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

* * * * * * * * * * *

4.3.3. A do-while ciklus

A do-while utasításban a ciklus törzsét képező utasítás végrehajtása után kerül sor a tesztelésre (I.12. ábra - A do-while ciklus működési logikája), így a ciklus törzse legalább egyszer mindig végrehajtódik.

I.12. ábra - A do-while ciklus működési logikája

do

utasítás while (feltétel);

Első példaként készítsük el a természetes számokat összegző program do-while ciklust használó változatát!

#include <iostream>

cout<<"Az elso "<<n<<" egesz szam ";

unsigned long sum = 0;

do {

sum += n;

n--;

} while (n>0);

cout<<"osszege: "<<sum<<endl;

}

Az alábbi ciklus segítségével ellenőrzött módon olvasunk be egy egész számot 2 és 100 között:

int m =0;

do {

cout<<"Kerek egy egesz szamot 2 es 100 kozott: ";

cin >> m;

} while (m < 2 || m > 100);

Az utolsó példánkban egész kitevőjű hatványt számolunk:

#include <iostream>

cout <<"a hatvany erteke: " << hatvany << endl;

}

Igen gyakori programozási hiba, amikor a ciklusok fejlécét pontosvesszővel zárjuk. Nézzük az 1-től 10-ig a páratlan egész számok kiírására tett kísérleteket!

int i = 1;

i+=2;

}

} i+=2;

} while(i<10);

A while esetén az üres utasítást ismétli vég nélkül a ciklus. A for ciklus lefut az üres utasítás ismétlésével, majd pedig kiírja a ciklusváltozó kilépés utáni értékét, a 11-et. A do-while ciklus esetén a fordító hibát jelez, ha a do után pontosvesszőt teszünk, így a közölt kód teljesen jó megoldást takar.

4.3.4. A brake utasítás a ciklusokban

Vannak esetek, amikor egy ciklus szokásos működésébe közvetlenül be kell avatkoznunk. Ilyen feladat például, amikor adott feltétel teljesülése esetén ki kell ugrani egy ciklusból. Erre ad egyszerű megoldást a break utasítás, melynek hatására az utasítást tartalmazó legközelebbi while, for és do-while utasítások működése megszakad, és a vezérlés a megszakított ciklus utáni első utasításra kerül.

Az alábbi while ciklus kilép, ha megtalálja a megadott két egész szám legkisebb közös többszörösét:

#include <iostream>

cout << "Legkisebb kozos tobbszoros: " << lkt << endl;

}

A break utasítás használata kiküszöbölhető, ha a hozzá kapcsolódó if utasítás feltételét beépítjük a ciklus feltételébe, ami ezáltal sokkal bonyolultabb lesz:

while (lkt<=a*b && !(lkt % a == 0 && lkt % b == 0)) { lkt++;

}

Ha a break utasítást egymásba ágyazott ciklusok közül a belsőben használjuk, akkor csak a belső ciklusból lépünk ki vele. Az alábbi példában megjelenítjük a prímszámokat 2 és maxn között. A kilépés okát egy logikai (flag, jelző) változó (prim) segítségével adjuk a külső ciklus tudtára:

#include <iostream>

}

Ha feladat úgy hangzik, hogy keressük meg a legelső Pitagoraszi számhármast a megadott intervallumban, akkor találat esetén egyszerre két for ciklusból kell kilépnünk. Ekkor a legegyszerűbb megoldáshoz egy jelző (talalt) bevezetésével jutunk:

#include <iostream>

#include<cmath>

using namespace std;

int main () { int bal, jobb;

cout <<"bal = "; cin >> bal;

cout <<"jobb = "; cin >> jobb;

bool talalt = false;

for(int a = bal, c, c2; a<=jobb && !talalt; a++) { for(int b = bal; b<=jobb && !talalt; b++) { c2 = a*a + b*b;

c = static_cast<int>(sqrt(float(c2)));

if (c*c == c2) { talalt = true;

cout << a << ", " << b << ", " << c << endl;

} // if } // for } // for } // main()

4.3.5. A continue utasítás

A continue utasítás elindítja a while, a for és a do-while ciklusok soron következő iterációját. Ekkor a ciklustörzsben a continue után elhelyezkedő utasítások nem hajtódnak végre.

A while és a do-while utasítások esetén a következő iteráció a ciklus feltételének ismételt kiértékelésével kezdődik. A for ciklus esetében azonban, a feltétel feldolgozását megelőzi a léptetés elvégzése.

A következő példában a continue segítségével értük el, hogy a 1-től maxn-ig egyesével lépkedő ciklusban csak a 7-tel vagy 12-gyel osztható számok jelenjenek meg:

#include <iostream>

using namespace std;

int main(){

const int maxn = 123;

for (int i = 1; i <= maxn; i++) { if ((i % 7 != 0) && (i % 12 != 0)) continue;

cout<<i<<endl;

} }

A break és a continue utasítások gyakori használata rossz programozói gyakorlat. Mindig érdemes átgondolnunk, hogy meg lehet-e valósítani az adott programszerkezetet vezérlésátadó utasítások nélkül. Az előző példában ezt könnyedén megtehetjük az if feltételének megfordításával:

for (int i = 1; i <= maxn; i++) { if ((i % 7 == 0) || (i % 12 == 0)) cout<<i<<endl;

}