Thumbnail: loop

Ciklai

Ciklai

Šis skyrius skirtas susipažinti su ciklais C++ programavimo kalboje.

Kas yra ciklas?

Kartais rašant programas norisi tam tikrus veiksmus kartoti daug kartų. Pavyzdžiui, galbūt norime nuskaityti dešimt vartotojo konsolėje įvestų skaičių. Vienas būdas tai padaryti būtų rašyti tokį kodą:

int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10;
cin >> a1 >> a2 >> a3 >> a4 >> a5 >> a6 >> a7 >> a8 >> a9 >> a10;

O kas jei tų skaičių norėtume nuskaityti šimtą? Turbūt akivaizdu, kad tokį kodą rašyti yra labai nepatogu, nors logika juk paprasta - pakartoti tą patį veiksmą dešimt (ar šimtą) kartų. Būtent tokiais atvejais galime panaudoti ciklus.

Ciklų rūšys

C++ programavimo kalba palaiko trijų rušių ciklus:

  • while
  • do...while
  • for

Apie kiekvieną iš jų pakalbėsime toliau.

while ciklas

Ciklas while naudojamas, kuomet norime kartoti tam tikrą kodą tol, kol tenkinama tam tikra sąlyga. Jo sintaksė yra tokia:

while (sąlyga) {
    // kodas, kurį norime kartoti
}

Pavyzdžiui, tarkime norime sužinoti, kiek kartų skaičius 1024 dalinasi iš dviejų. Tam nustatyti galėtume pabandyti 1024 dalinti iš dviejų iki tol, kol daugiau padalinti be liekanos nebeišeis. Kodas atrodytų taip:

int x = 1024;
int kiek = 0;
while (x % 2 == 0) { // kartosime tol, kol x dalinsis iš dviejų be liekanos
    x /= 2; // padaliname x iš dviejų
    kiek++; // padidiname atsakymą vienetu
}
cout << kiek << endl; // atspausdiname atsakymą

Šis kodas išvestų skaičių 10.

Taip pat while ciklas naudingas, kuomet norime skaityti įvestį tol, kol ji pasibaigs. Tokiam atvejui rašomas toks ciklas:

int x;
int suma = 0;
while (cin >> x) { // kartosime tol, kol baigsis įvestis
    suma += 0;
}
cout << suma << endl; // atspausdiname visų įvestų skaičių sumą

Prie šio pavyzdžio derėtų pastebėti, kad, jei bandysite leisti programą pas save konsolėje, programa niekad nebaigs darbo, nes vis tikėsis įvesties. Tačiau šį kodą įkėlus į testavimo sistemą, viskas veiks gerai. Norint išbandyti tokį kodą lokaliai, reikėtų skaityti ne iš konsolės, o iš failo.

do...while ciklas

do...while ciklas yra lygiai toks pats, kaip ir while ciklas, tik jame esantis kodas garantuotai bus įvykdytas nors vieną kartą, nes sąlyga tikrinama po kodo vykdymo. Sintaksė tokia:

do {
    // kodas
} while (sąlyga);

Tarkime, kad norime nuskaityti iš konsolės skaičius iki kol bus ivestas nulis, bet bent vieną skaičių nuskaityti tikrai turime. Tada pagelbėtų toks kodas:

int a;
do {
    cin >> a;
} while(a != 0);

for ciklas

for ciklas dažniausiai naudojamas tada, kai iš anksto žinome, kiek kartų norime kartoti tam tikrą kodą. Šio ciklo sintaksė tokia:

for(ciklo skaitliuko sukūrimas; sąlyga; ciklo skaitliuko reikšmės keitimas) {
    // kodas
}

Šį ciklą geriau paaiškinti konkrečiu pavyzdžiu. Tarkime, kad norime nuskaityti penkis skaičius iš konsolės ir išvesti jų sumą. Tai galima pasiekti tokiu kodu:

int suma = 0;
for (int i = 0; i < 5; i++) {
    int a;
    cin >> a;
    suma += a;
}
cout << suma << endl;

Panagrinėkime kodą:

  • int i = 0 nustato, kad ciklo kintamasis bus pavadintas i ir jo pradinė reikšmė bus lygi nuliui.
  • i < 5 reiškia, kad cikle esantis kodas bus vykdomas tol, kol i reikšmė bus mažesnė už 5. Ši sąlyga tikrinama kiekvieną kartą prieš vykdant ciklo kodą.
  • i++ reiškia, kad kiekvieną kartą įvykdžius ciklo kodą, kintamojo i reikšmė bus padidinama vienetu.

Taigi, kodas vyks taip:

  1. Sukuriamas kintamasis i su reikšme 0
  2. Ar i < 5? Taip, todėl vykdome kodą.
  3. Įvykdomas tarp riestinių skliaustų esantis kodas.
  4. Kintamojo i reikšmė padidinama vienetu (dabar i = 1).
  5. Ar i < 5? Taip, todėl vykdome kodą.
  6. Įvykdomas tarp riestinių skliaustų esantis kodas.
  7. Kintamojo i reikšmė padidinama vienetu (dabar i = 2).
  8. Kintamojo i reikšmė padidinama vienetu (dabar i = 5).
  9. Ar i < 5? Ne, todėl cikle esantis kodas daugiau nebevykdomas.

Taigi iš viso kodas, esantis tarp riestinių skliaustų bus įvykdomas penkis kartus (kai i = 0, kai i = 1, kai i = 2, kai i = 3 bei kai i = 4).

Kurį ciklą pasirinkti?

Skirtingiems uždaviniams dažniausiai yra patogūs skirtingi ciklai, nors iš tikro bet kurį ciklą galima perrašyti kitu ciklu. Dažniausiai laikomasi tokių pasiūlymų:

  • Jei iš anksto žinome, kiek kartų reikės kartoti ciklą, geriausia naudoti for ciklą.
  • Jei žinome, kad ciklo kodą būtinai reikia įvykdyti nors kartą, dažnai patogus yra do...while ciklas.
  • Beveik visais kitais atvejais patogiausias yra while ciklas.

Pavojai

Naudojant ciklus gali kilti ir tam tikrų pavojų. Konkrečiai šnekant, galima sukurti taip vadinamą amžinąjį ciklą - tokį ciklą, kuris niekada nesibaigs. Pavyzdžiui, šie du kodo gabaliukai sukuria amžinąjį ciklą, kuris “užlaužia” programą:

for(int i = 0; i < 2; i--) {
    cout << "Labas" << endl;
}
int x = 0;
while(x < 5) {
    cout << "O ne!" << endl;
}

Tiek pirmame, tiek antrame cikluose niekada nebus įgyvendintos ciklo sąlygos, tad ciklai suksis amžinai. Dėl šios priežasties visada būtina atidžiai sužiūrėti savo kodą, kad nepaliktumėte tokių klaidų!

Programos vykdymo eiliškumo valdymas cikluose

Ciklų viduje galima naudoti du naujus raktažodžius - break ir continue. Jie skirti rankiniu būdu įsiterpti į ciklo vykdymą ir kažkiek pakeisti jo eiliškumą.

break

break yra naudojamas, kai norima nutraukti ciklą prieš tikrinant jo sąlygą. Pavyzdžiui, tarkime turime tokį kodą:

int kiek = 0;
int x = 1024;
while(x % 2 == 0) {
    x /= 2;
    kiek++;
    if (kiek > 5) {
        break;
    }
}
cout << kiek << endl;

Šis kodas bando dalinti kintamąjį x iš dviejų tol, kol tokia dalyba yra įmanoma. Be to, dar laikomas kintamasis kiek, kuris didėja vienetu su kiekvienu ciklo įvykdymu. Sakinys if (kiek > 5) { break; } užtikrina, kad, jei kiek viršys penkis, ciklas bus nutrauktas (net jei sąlyga x % 2 == 0 vis dar tenkinama). Taigi šiuo atveju, kodas spausdins 6, nors iš tikro 1024 iš dviejų galima padalinti 10 kartų.

continue

continue yra naudojamas, kai norima grįžti į ciklo pradžią dar nepabaigus vykdyti ciklo kodo. Pavyzdžiui, norime nuskaityti skaičius iš konsolės iki pirmo vieneto, ir tada skaičių 10 padalinti iš kiekvieno iš įvestų skaičių ir išvesti rezultatą. Kodas galėtų atrodyti taip:

int sk;
do {
    cin >> sk;
    cout << 10 / sk << endl;
} while(sk != 1);

Deja, šis kodas yra pavojingas dėl dalybos - juk vartotojas gali įvesti nulį, o bandant dalinti iš nulio nieko gero nebus! Tarkime, nusprendžiame nieko nespausdinti ir tiesiog toliau tęsti darbą, kai vartotojas įveda nulį. Čia gali padėti raktažodis continue. Su juo kodas atrodytų taip:

int sk;
do {
    cin >> sk;
    if (sk == 0) {
        continue;
    }
    cout << 10 / sk << endl;
} while(sk != 1);

Šiuo atveju prieš dalinant iš įvesto skaičiaus visų pirma patikriname, ar skaičius, iš kurio daliname (sk) nėra lygus nuliui. Jei taip yra, grįžtame tiesiai į ciklo pradžią panaudoję raktažodį continue. Taip padarę išvengsime dalybos iš nulio!