A ciklus, vagy iteráció a számítógép-programozás és az algoritmusok egyik alapvető eszköze, amely az ismétlődő (azonos vagy hasonló) tevékenységek megvalósítására szolgál. A ciklus beépítését a programba ciklusszervezésnek is nevezik. A ciklust az egyes programozási nyelvek különböző kulcsszavakkal valósítják meg.
A ciklus részei Alább egy egyszerű BASIC nyelvű ciklust láthatunk, amelyik 2-től 5-ig egyesével haladva kiírja a számokat és a négyzetüket: FOR i=2 TO 5 STEP 1 PRINT i, " négyzete: ", i*i NEXT i Ebben a ciklusban az i a ciklusváltozó, amely a ciklus kezdetén értéket kap. A szerepe kettős: egyrészt vezérli a ciklust, mivel a ciklusváltozó értékétől függ, hogy folytatódik-e a futás, másrészt a pillanatnyi értéket a program felhasználja. A ciklus futásának kezdetén tehát az i értéke 2 lesz; mivel ez kisebb, mint 5, ezért a ciklusmag lefut, vagyis a program kiírja a képernyőre a 2 négyzete: 4 sort. Ezután i értéke 3-ra változik, és ismét lefut. Ez addig ismétlődik, amíg az i értéke 5 lesz. Ekkor még utoljára lefut a ciklus, tehát a képernyőn még megjelenik az 5 négyzete is, majd az i értéke 6 lesz, de ez már nagyobb, mint 5, ezért a program kilép a ciklusból, és a futás a következő sorral folytatódik. Végeredményben a következőt írja ki a program: 2 négyzete: 4 3 négyzete: 9 4 négyzete: 16 5 négyzete: 25 Ebben a ciklusban a ciklusváltozónak van kezdőértéke (2), végértéke (5) és lépésköze (1). Nem minden ciklusban van ciklusváltozó. Egyes programnyelvekben több is lehet belőle. A ciklus első sorát ciklusfejnek nevezik. Általában ez vezérli a ciklust; itt kap helyet a ciklusváltozó(k) definíciója, valamint a futási feltétel is. A középső sor a ciklusmag – ismert még a ciklus törzse néven is –, vagyis a ténylegesen ismétlődő utasítások meghatározása. A fenti példában ciklus magja mindössze egyetlen utasításból áll. Végül az utolsó sor a ciklusvég; az elöltesztelő és a számláló ciklus esetében ez puszta lezáró utasítás, amely a ciklusmag végét jelzi (egyes programnyelvekben esetenként el is maradhat), a hátultesztelő ciklus esetén pedig ez a rész tartalmazza a futási feltételt.
Ciklus nélkül a program: PRINT 2, " négyzete: ", 2*2 PRINT 3, " négyzete: ", 3*3 PRINT 4, " négyzete: ", 4*4 PRINT 5, " négyzete: ", 5*5
Bár ez a programocska ciklus nélkül is megírható, azonban eltérő adatok esetén teljesen át kellene írni, míg a ciklust tartalmazó változatban csupán a kiindulási adatokat kell módosítani. A példában mindössze 4 számot és a négyzeteiket kellett kiíratni. Nagyobb intervallum esetén a ciklus tömörsége nyilvánvaló, szemben a ciklus nélküli program terjengősségével.
Típusai Feltételes ciklus (while) A feltételes ciklus olyankor használatos, amikor nem feltétlenül ismert előre, hogy futásidőben hányszor kell a ciklusnak lefutnia (például egy számítógép lemezkatalógusában lévő fájlok listázása). Az viszont ismert, hogy milyen feltétel teljesülése esetén kell a programnak tovább futnia vagy leállnia. (Ez a kettő egymással ekvivalens, hiszen a további futás, illetve a leállás feltétele egymás tagadása.) A feltétel teljesülését vizsgálhatjuk a ciklusfejben vagy a ciklusvégben is; a különbség abban fog állni, hogy legalább egyszer lefut-e a ciklus.
Elöltesztelő ciklus 200px|jobbra|bélyegkép Az elöltesztelő ciklus sémája: Ismételd, amíg a feltétel igaz utasítások ciklus vége Az elöltesztelő ciklus tehát először megvizsgálja, hogy a feltétel fennáll-e. Ha igen, akkor lefuttatja a ciklusmagot, és újból kezdődik; ha nem, akkor a program a ciklus utáni ponton folytatódik, azaz a ciklusmag kimarad. Lehetséges tehát, hogy az elöltesztelő ciklus egyszer sem fog lefutni.
Az elöltesztelő ciklus tipikus példája az adatállományok beolvasása; előfordulhat ugyanis, hogy az állomány üres, és ilyenkor nincs mit beolvasni. Hasonló a helyzet a könyvtárak listázásakor is, hiszen a könyvtárban nem biztos, hogy van állomány.
Az elöltesztelő ciklus tipikus kulcsszava a while. Általános alakja a következő: while (feltétel) { utasítás(ok); }
Példa (awk): i = 0; while (i < 10) { print i; i++; }
Hátultesztelő ciklus 200px|jobbra|bélyegkép A hátultesztelő ciklus sémája: Ismételd utasítások amíg a feltétel igaz Mivel a feltételvizsgálat a ciklusmag után áll, ezért a hátultesztelő ciklus legalább egyszer mindenképpen lefut.
A konkrét programnyelvi megvalósítástól függ, hogy a hátultesztelő ciklusban a folytatás vagy a kilépés feltételét kell-e megadni. A ciklus az első esetben addig fut, amíg a ciklusvégben megadott feltétel igaz (ennek tipikus kulcsszava a while), a másik esetben pedig addig, amíg igazzá nem válik (tipikus kulcsszava az until).
Általános alakja a következő: do { utasítás(ok); } while (feltétel);
Jellemző példa a hátultesztelő ciklusra az adatbevitel ellenőrzése. Tegyük fel, hogy regisztrálni szeretnénk egy ingyenes postafiókot az interneten. A szerveren futó programnak először ellenőriznie kell, hogy szabad-e még a választott azonosító, majd azt is, hogy kitöltöttük-e az összes kötelező adatmezőt, és addig kell ismételnie ezt a lépést, amíg az összes adat helyessé nem válik. Sok szolgáltató ezt egy lépésben végzi el, vagyis először ki kell töltenünk az űrlapot, aztán tudjuk meg, hogy szabad-e még az azonosító. Ebben az esetben a ciklus a következőképpen nézhet ki: Ismételd Olvasd be az adatokat Ha az azonosító foglalt, írd ki, és add vissza az űrlapot Ha egy kötelező mező hiányzik, írd ki, és add vissza az űrlapot amíg hibás az űrlap Mivel a feltételt csak az adatbeolvasás után ellenőrzi a program, ezért az adatok beolvasása legalább egyszer mindenképpen megtörténik. Ha sikerült elsőre jól kitölteni az adatlapot, akkor tovább lehet lépni.
Zavart okozhat a szöveges leírásokban a magyar amíg szó két, egymással lényegében ellentétes jelentése. Az algoritmusok leírásában az amíg szót mindig a 'mialatt', 'miközben' értelmében használjuk, ami az angol while-nak felel meg. A fenti eljárást úgy is fogalmazhatnánk, hogy „olvasd be az adatokat, ahányszor kell, amíg jók lesznek” – ebben az esetben az amíg szót az angol until értelmében használtuk, ami szintén lehet a hátultesztelő ciklus kulcsszava. Ezért előfordulhat, hogy a programkódban épp a szöveges algoritmusban látható feltétel tagadását kell megfogalmaznunk.
Számláló ciklus (for) 200px|jobbra|bélyegkép A számláló ciklus általánosságban olyan elöltesztelő ciklust jelent, amely egy felsorolható típus adott intervallumán léptet végig, speciálisan egész számokon. Üres intervallumra nem fut le.
C-ben a for ciklus majdnem teljesen ekvivalens az elöltesztelő ciklussal, de más nyelvekben nem feltétlenül van ez így: a for ciklus átírható más ciklussá, de visszafelé általában nem. A for ciklusnak kisebb lehet a kifejezőereje, de könnyebb olvasni: ezért a használata javasolt minden olyan esetben, ahol csak lehetséges.
Számok összegzése forciklussal C-ben: int sum = 0; // Kezdetben a szumma 0 int i; for (i = 0; i < 200; ++i) // Kezdőérték; feltétel; növelés sum += i; // Az i hozzáadása sum-hoz
Számok összegzése whileciklussal C-ben: int sum = 0; int i = 0; while (i < 200) { sum += i; ++i; }
Iteráló ciklus (for in, foreach) Egyes nyelvekben a foreach kulcsszót kell használni. Arra való, hogy egy egy tároló (pl. tömb, lista, bináris fa stb.) elemeit bejárják és minden egyes elemre végrehajtják a ciklus törzsében található utasítást illetve utasításokat. Erre egy egyszerű példa PHP-ban:
$arr = array(1, 2, 3, 4); // Tömb az 1, 2, 3, 4 számokból foreach ($arr as &$value) { // A tömb bejárása $value = $value * 2; // Minden elem megszorzása 2-vel } // A $arr elemei itt rendre 1, 2, 3 és 4
Egy 'lista' bejárása (AutoLISP): (foreach n '(a b c) (print n))
Egyenértékű a (print a) (print b) (print c) megadásával. Mivel egy lista elemszáma nem kötött, az előny nyilvánvaló. Ráadásul a végrehajtandó utasítások száma sincs korlátozva (a fenti példában csak egy print szerepel).
Néhány programozási nyelv a for inutasítással valósítja meg a tömbök, listák bejárását, pl. az awk: (már létezik a szavak nevű tömb) for (x in szavak) { if (length(x) > 12) { # ha a szó hosszabb, mint 12 karakter print x # akkor kiírjuk
Ez a példa egy MS-DOS kötegelt állományában szerepel, és törli az aktuális könyvtárból az ideiglenes állományokat néhány tipikus kiterjesztés alapján: for %%c in (*.bak *.tmp *.$$$ *.wbk) do del %%c
Beavatkozás a ciklus menetébe Egy ciklus menetébe beavatkozni többféleképpen lehet: a ciklusváltozó növelésével, ill. csökkentésével; a break utasítással; a continue utasítással. Mindhárom módszer használható a for és a while segítségével létrehozott ciklusokban. Az egyetlen követelmény, hogy a ciklus törzsén belül kell elhelyezni,különben hibajelzést kapunk.
Ciklusváltozó módosítása Gyakran előfordul, hogy n darab különböző számot kell generálnunk vagy bekérnünk. Ilyenkor kézenfekvő a for ciklus használata, ugyanakkor ismétlődés vagy hibás adat esetén szükség lehet a visszalépésre. using namespace std; set<int> lottoSzamok; for (int i = 0; i < 5; ++i) { int aktualis; cout << (i + 1) << ". lottó szám: "; cin >> aktualis; if (aktualis >= 1 && aktualis <= 90 && lottoSzamok.find(aktualis) == lottoSzamok.end()) { lottoSzamok.insert(aktualis); } else { cerr << "A szám nem megfelelő vagy már volt." << endl; --i; // visszalépés
Az ADA nyelvben ez nem áll fent, a for ciklus itt egy szigorúan kötött, ellenőrzött konstrukció, például nem lehet a ciklusváltozót változtatni: for I in 1..200 loop SUM := SUM + I; -- első 200 szám összege, ADA for ciklussal end loop;
A break utasítás Ez a speciális, ciklusok esetén használható utasítás azonnal megszakítja a ciklus futását, és függetlenül a ciklus vezérlő feltétel értékétől, függetlenül minden mástól azonnal (erőszakosan) befejezi a ciklus futását. A program futása ettől nm szakad meg, folytatódik a ciklus után következő utasítás végrehajtásával. A break és a ciklus vége közötti utasítások nem lesznek végrehajtva.
A continue utasítás A break-hez hasonló utasítás a continue. Ennek használata esetén a ciklus futása nem szakad meg (fejeződik be) mint a break, sőt, ellenkezőleg. A continue hatására a ciklusmag maradék utasításai ebben a menetben már nem hajtódnak végre, de a ciklus futása folytatódik, mintha elértük volna a ciklusmag végét, vagyis újra a vezérlő feltétel kiértékelése következik. Például szeretnénk kihagyni az 1234-es irányítószámot: kimarad = 1234; start = 1230; end = 1240;
for (i = start; i <= end; i++){ if (i == kimarad) {continue} else { print i, "\n";
A continue utasítás pl. menük „kiszürkítésére” is jól használható.
Néhány nevezetes alkalmazás Egymásba ágyazott ciklusok Egymásba ágyazott ciklusokat tipikusan akkor szokás használni, amikor egy területen (például egy kép képpontjain, vagy egy mátrixon) kell műveleteket végezni. Bizonyos rendező algoritmusok (pl. buborékrendezés) is egymásba ágyazott ciklusokat használnak. Egy számtáblázat celláinak az összegzése pl. így néz ki: int **t, n, m; // <-- Beolvasás n-be, m-be (méretek) és t-be int sum = 0; for (int i = 0; i < n; ++i) for (int j = 0; j < m; ++j) sum = sum + t[i][j]; std::cout << "A cellák összege: " << sum << std::endl;
A ciklusokat használó programok rendszerint korlátozzák a ciklusok egymásba ágyazhatóságának számát. Egyrészt a ciklusok egymásba ágyazásával a kiértékelések száma hatványozódik, ami drámaian növeli a program futásidejét (kibírhatatlanul lassúvá válik). Másrészt pedig a kiértékeléshez használt veremtár betelhet a sok átmeneti adattól, ami pedig a program működését akadályozza meg (elszáll a program).
A végtelen ciklus