Tsüklid

Sissejuhatus

Tsükkel on üks kõige olulisematest programmeerimise alustaladest, mille abil saame endale sobiva koodiploki sisu korrata – st me saame valida millised laused koodis korduvad ning mis tingimustel. Korratavat koodiplokki tuntakse ka kui tsükli keha. C keeles ümbritsetakse tsükli keha tavapäraselt loogeliste sulgudega { } .

Tsükli keha kordub senikaua kuniks vastavat tsüklit kontrolliv tingimus on tõene. Tsükli tingimuse korrektne koostamine on äärmiselt oluline, et tsükkel töötaks ootuspäraselt ja seeläbi ka meie programm töötaks nii nagu vaja.

Tsükleid on võimalik grupeerida eelkontrolliga  (entry-controlled) ja järelkontrolliga (exit-controlled) tsükliteks. Tsükli liigi otsustab millal tsükli tingimust kontrollitakse – st kas enne või pärast tsükli kehasse kuuluvate koodilausete käivitamist.

Erinevates programmeerimiskeeltes on kasutusel erinevat liiki tsükleid. C keeles on meile saadaval kolm – while , do ... while  ja for  tsükkel.

Igal tsükli tüübil on omad eelised, mis tingivad ühe või teise tsükli eelistamise vastavalt kasutusjuhule. Sobiliku tsükli tüübi kasutamine annab meile eeliseid nii koodi kirjutamise mugavuses kui ka selguses (loetavuses). Kusjuures see ei peata meid teist tsükli tüüpi kasutamast, sama tulemus on võimalik saavutada ükskõik millise tsükli tüübiga, lihtsalt tulemus võib olla kohmakam.

While tsükkel

Tegu on kõige lihtsama ja levinuima tsüklitüübiga. while  tsükkel on eelkontrolliga tsükkel, st esimese asjana kontrollitakse tsükli tingimust ja vaid siis kui see on tõene, käivitatakse tsükli keha. See tekitab võimaluse, et tsükkel ei käivitu kunagi.

Ainuke kohustuslik element while  tsükli struktuuris on selle tingimus – st kas tsükli keha tohib käivitada või mitte. Tingimust kontrollitakse iga tsükli korduse eel.

while tüüpi tsükleid kasutatakse peamiselt siis kui vajalik korduste arv ei ole teada koodi kirjutamise ajal. See hõlmab ka endas olukordi, kus tsüklit korratakse lõpmata arv kordi.

Näide: Kuva kõik kolme astmed alla piirväärtuse.

For tsükkel

for  tsüklit on kõige sobilikum kasutada olukorras, kus iteratsioonide ehk korduste arv teada eelnevalt – teisisõnu on tegu loendamiseks hästi sobiva tsükliga. for  on samuti eelkontrolliga tsükkel – st ka selle tsükli puhul on võimalik, et tsükli keha kunagi ei käivitata.

Tsükli struktuuris on eraldi reserveeritud kohad algväärtustamiseks, tingimuseks ja avaldiseks. Kõik need on eraldatud üksteisest semikooloniga. Just see võimaldab seda tüüpi tsükleid kasutada mugavalt loendamiseks, jättes koodi puhtaks ja hästi loetavaks. NB! Jälgi kindlasti mis ajal teostatakse kõik need 3 komponenti for  tsüklist!

Näide: Järgnev tsükkel loendab nullist üheksani. Märka ka, et tsükkel on kirjutatud C90 standardile vastavalt. C99 stiilis deklaratsiooniga tutvu järgnevates peatükkides..

Do … while tsükkel

do ... while  on ainuke tsükli tüüp C keeles, mis kasutab järelkontrolli. St selle tsükli keha käivitatakse esimene kord ilma tingimust kontrollimata ning alles pärast tsükli keha täitmist kontrollitakse kas tsüklit tuleks korrata.

 

Sedatüüpi tsüklit on mugav kasutada kui tegevust tuleb alati vähemalt ühe korra teostada, kuid võib esineda vajadus tegevuse kordamisel – nt kui mõni kontrolltingimus ei saa täidetud. Hea näide sellest on kasutaja sisestus, mis peab olema programmile sobilik enne programmi jätkumist (nt kasutaja peab sisestama positiivse täisarvu).

Näide:  Tsüklit korratakse kuni kasutaja sisestab numbri  . Märka, ka et kuigi userInp  on jäetud algväärtustamata, siis antud olukorras pole see probleem. Muutuja väärtust kontrollitakse pärast kasutaja poolt väärtuse sisestamist antud muutujasse.

C90 vs C99 tsükli deklaratsioon

Standard ISO C90 keelab koodilausete ja muutujate deklaratsioonide omavahelist segamist – st esmalt peab deklareerima kõik muutujad funktsiooni alguses, misjärel võivad tulla koodilaused.

See tähendab, et ka kõik tsükli loendurid tuleb deklareerida funktsiooni alguses.

Alates C99 standardist tohib muutujaid deklareerida seal kus vaja. Sealjuures on lubatud muutujate deklareerimine tsükli sees. See aitab koodi veidi puhtamana hoida.

Olulised märkused

  • Selle aine raames on sul lubatud kasutada nii C90 kui C99 standardit.
  • Osa riistvara puhul võib tekkida olukordi, kus on vaid C90 kompilaatorid toetatud. sellisel juhul tsüklis muutuja deklareerimine lõppeb kompileerimise veaga.
  • C99 stiilis tsüklit deklareerides jääb tsükliloenduri i  skoobiks vaid tsükli keha – st i väärtusele ligipääsu pärast tsükli lõppu ei ole!

Lõpmatud tsüklid

Lõpmatuid tsükleid kohtab kõige sagedamini sardsüsteeme (mikrokontrollereid) programmeerides – kontroller kordab sama asja kogu oma eluaja vältel (milleks võib olla kümneid aastaid).

Nagu tsüklitega ikka, siis ükskõik millisest tsüklist on võimalik teha lõpmatu tsükkel. Kõige paremini loetava koodi aga saad enamjaolt kasutades  while  tsüklit, mille tingimuseks on  1 . C keeles annab ükskõik milline mittenulline väärtus tingimusena tõese ehk  true  tulemuse.

Aegajalt võib tekkida vajadus koostada lõpmatu tsükkel, millest on võimalik teatud juhtudel väljuda. Väljumiseks kasutame me tingimuslauset selle sündmuse tuvastamiseks ning break  lauset tsükli katkestamiseks.

Kui väljumise tingimus paikneb sellise lõpmatu tsükli lõpus, siis sageli võib olla mõistlikum see ümber kirjutada   do ... while  tsüklina! Selline struktuur võiks välja näha järgnev:

Pesastatud tsüklid

Tsüklite pesastamine on tegevus, kus paigutame ühe tsükli kehasse teise tsükli. Otseseid piiranguid mitu tsüklit pesastada tohib ei ole, kuid koodi haldamise mõttes tasub vältida rohkem kui kahe tsükli kasutamist üksteise sees. Kui tekib vajadus rohkem tsükleid pesastada tuleks kaaluda koodi ümber struktureerimist täiendavateks funktsioonideks.

Tsüklite pesastamisel tohib kasutada ükskõik milliseid tsükli tüüpe, sh vaheldumisi. Näiteks võib olla  while  tsükli sees  for  tsükkel.

Järgnevas näites oleme pesastanud kaks  for  tsüklit ning tsüklid on kirjutatud C99 stiilis.

Märka, et välimise tsükli kehaks on kaks print lauset ning   for  tsükkel. Mõlemat  printf  lauset ja  for  tsüklit käivitatakse  n  korda.

Sisemine tsükkel kasutab välimisest erinevat tsükliloendurit  j . Sisemises tsüklis olevat väljastuse lauset korratakse järjest  m  korda. Küll aga kuna tegu on pesastatud tsüklitega, siis tegelik sisemise printf  lause korduste arv saab olema  n * m  korda!

Tsüklite pesastamisel pööra erilist tähelepanu tsüklite tingimustele ning loenduritele! Vead kus ajad kogemata loenduri i  ja j omavahel segamini on kerged tulema ning see võib põhjustada kas olukorra, kus tsükkel ei jookse kordagi või muutub hoopis lõpmatuks tsükliks.

Lisaks tasub meeles pidada, et kuigi erinevate loendurite ja tingimuste kasutamine on tavapärane, siis tegu ei ole reegliga. Oluline on koostada kood vastavalt eesmärgile mida see täitma peab.

Tsüklid millel puudub tsükli keha

C keeles lõppevad kõik laused semikooloniga. Seda on võimalik ära kasutada ka tsüklite puhul. Näiteks saame panna semikooloni kohe pärast  for  või while  tsükli päist. See tekitab olukorra, kus vastaval tsüklil puudub tsükli keha. Ühest küljest võib olla tegu tahtliku programmeerimisvõttega, kuid selline olukord on ka väga tavapärane alustava programmeerija tüüpviga.

Erandina pane tähele, et do ... while  tsükkel nõuab semikoolonit pärast tingimust ning antud peatükk selle tsüklitüübi kohta ei käi!

Keha puudumine kui koodimisviga

Kirjutades  while  tüüpi tsükli tingimuse lõppu semikooloni tekitame me tsükli, millel puudub keha – semikoolon lõpetab selle tsükli lause. Enamasti teki i  igavesti väärtusele  . Kuigi suurendamise operatsioon i++  on loogeliste sulgude vahel, ei ole see koodiplokk antud tsükli kehaks – tsükkel lõppes kogemata pandud semikooloniga. Loogeliste sulgude vahel olev koodiplokk täidetakse pärast tsükli lõppu (mis on siinpuhul  lõputu).

Kirjutades for  tüüpi tsükli semikooloniga tekitame samamoodi tsükli, millel puudub keha. Enamjaolt võib meile jääda mulje, et tsükkel jäetakse vahele või selle sisu teostati vaid korra, kuigi tegelikult tsükkel toimis ja läbiti täies mahus.

Järgneva näite puhul kordab tsükkel end 5 korda, mis on ootuspärane. Küll aga on ainuke tsükli kehas olev lause  i++ , mistõttu erinevalt while  tsüklist, ei ole siin tegu lõpmatu tsükliga. Küll aga ei ole ka väljastus antud tsükli keha sees, mistõttu tsüklis puudub igasugune väljastus. Print lause ise paikneb tsükli järel paiknevas koodiplokis ja seetõttu käivitatakse ühekordselt.

Tahtlikult puuduv tsükli keha

Mõningatel juhtudel meeldib programmeerijatele teha võimalikult lühikesi programme – st hoida oma koodiridade arv minimaalsena. Selle saavutamiseks kirjutatakse aegajalt üherealisi tsükleid tahtlikult. Seda on võimalik teha nii while  kui for  tsüklite puhul. Küll aga ei ole see soovitatav ja sageli isegi peetakse seda kohatuks. Sellise koodi probleemiks peetakse tahtlikku koodi sisu peitmist (nt midagi sellist mida arvutiviiruse kirjutaja võiks teha). Koodi peitmine põhjustab hiljem asjatut ajakulu ja probleeme koodi ülevaatustel ja silumisel.

Tegu on võttega, millest peaksid teadlik olema, kuid samal ajal peaksid ise vältima selle kasutamist! Kirjuta tsüklid välja täies mahus, et neid oleks hiljem lihtsam siluda ja hallata.

Märka ka, et tegelikult viimane programm jookseb kokku kui sõnes puuduvad tühikud – ehk tegu on ka peidetud veaga!