Tsükkel programmis: while-lause

From Wikiversity
Jump to navigation Jump to search

< 4. nädala teemad

Tsükkel algoritmis[edit]

Tükkel on kolmas oluline ehituskivi algoritmide loomisel'. Tsükli struktuur lubab teatud tingimustest lähtudes otsustada algoritmi teiste lausete kordamise üle. Kordamine on vähemalt sama oluline võimalus kui valikute tegmine - näiteks suurte andmehulkde töötlemisel, kus kõigi andmeelementidega tuleb teha samu tegevusi, kogu algoitmi kordamisel, vigaste sisendite välistamisel, arvutusmeetodite läbiviimisel jne.

Tsükkel on keeletarind, mille abil saab osa programmi lauseid (nn lausete ploki) korduma panna. Võib öelda, et tsüklilause ise midagi ei tee, kuid ta juhib teiste, tema sees olevate lausete täitmist. Sarnaselt käitub ka valikulause: jagab täitmise erinevatesse harudesse, kuid otseselt ise ei arvuta.

Tsükliga ja tema kasutamisega on seotud mitu olulist küsimust:

  • Millal selgub korduste arv (kas on tsüklit alustades see teada või selgub tsükli töötamise käigus)?
  • Kes või mis määrab korduste arvu (tingimus)? Kui on enne tskli algust teada, siis kust? Kui selgub tsükli töö käigus, siis mis moodi?
  • Milliseid lauseid korratakse ehk mis moodustab tsükli keha?
  • Kuidas teha arvutile selgeks, et ühed laused lähevad kordamisele, teised aga mitte? Ehk kuidas määrata plokki.

Tsüklite liigid[edit]

Tsükleid jaotatakse klassikaliselt kolme tüüpi:

  • Eelkontrolliga tsükkel
  • Järelkontrolliga tsükkel
  • Kindla kordustearvuga tsükkel

Eelkontrolliga tsükkel[edit]

Eelkontrolliga tsükkel on programmeerimiskeeles kõige vajalikum. Selle tsükli olemasolul saab üldiselt kõik "vajadused rahuldada". Olulist rolli mängib kordamisel ja mittekordamisel loogikaavaldis, mille väärtus mäletatavasti võis olla tõene (True) või väär (False).

Eelkontrolliga tsükli puhul paikneb loogikaavaldis tsükli alguses ja tsükli algusesse jõudmise hetkel selle väärtus ka välja arvutatakse. Kui avaldise väärtus on tõene, siis tsükli sees olevaid lauseid täidetakse / korratakse. Kui väärtus on väär, siis ei täideta ning kogu algoritmi / programmi täitmine jätkub järgmise lausega peale tsükli lõppu. Et tsükkel üldse tööle hakkaks, peab loogikatingimus vähemalt esimesel korral tõene olema. See võib nõuda vastavate muutujate eelnevat algväärtustmist. Ei ole õige panna lausete kordumist juhitma muutujat, mille väärtus on programmi poolt eelnevalt määramata. Vastavalt olukorrale võib programmi täitmisel juhtuda ka nii, et tsüklis olevad lauseid ei täideta kordagi, sest tingimus oli algusest peale väär, kuid ka sel juhul peab olukorra looma muutuja, mis nö teadlikult väärtuse saab. Mis on võrdlustehte a > 10 väärtus, kui ei ole kindlaks määratud a väärtust?! Kas tõene või väär?

Eelkontrolliga tsükli üldine skeem on järgmine:

Eelkontroll skeem.png

Kui tsükli alustamise eelduseks on loogikaavaldise tõene väärtus, siis vastupidi - selleks, et tsükkel oma töö lõpetaks, peab see väärtus vääraks muutuma. Tsükli täitmise käigus peavad loogikatingimuses "osalevate" muutujate väärtused muutuma. Kui seda ei juhtu, siis võib karta lõputu tsükli tekkimist ehk olukorda, kus "programm läks tsüklisse".


Tsükli täitmine[edit]

while-tsükli täitmine toimub järgmiste sammudega, järgmise loogika kohaselt:

  1. Arvutatakse loogikatingimuse väärtus
  2. Kui väärtus on tõene, täidetakse tsükli sees olevad laused ja täitmine antakse tsükli algusesse uueks loogikatingimuse väärtuse leidmiseks.
  3. Kui loogikaavaldise väärtus on väär, lõppeb tsükli täitmine ja programmi jätkatakse käsuga peale tsüklit.

Järelkontrolliga tsükkel[edit]

Järelkontrolliga tsükli puhul täidetakse ühe korra tsüklis olevad laused ja seejärel kontrollitakse loogikatingimuse täidetust. Kui tingimus on tõene, täidetakse tsüklis olevaid lauseid uuesti, kui väär, siis tsükli täitmine lõpetatakse. Mõnes keeles võib aga tõene/väär olla nö vastupidi, st tsüklit täidetakse siis kui tingimus väär ning tsüklist lahkutakse siis, kui tingimus saab tõeseks. Seega oleks siis tegemist tsükli lõpetamise tingimusega. Tõe teadasaamiseks tuleb alati keele reegleid uurida!

Skeem järelkontrolliga tsükli jaoks on järgmine:

J2relkontroll skeem.png

Muus osas kasutamistingimused eelkontrolliga tsükliga kattuvad: tsükli täitmise käigus selgub korduste arv, tuleb jälgida, et muutujad oleksid sobivalt väärtustatud. Erinevuseks aga see, et antud tsüklit täidetakse alati vähemalt üks kord.


Kindla kordustearvuga tsükkel[edit]

Kui enne tsükli käivitumist on teada, mitu korda tema sisu täita tuleb, on mugav kasutada kindla kordustearvuga tsüklit. Tavaliselt omab selline tsükkel veel loendurit, nn tsüklimuutujat, mida mitmel erineval otstarbel kasutada saab (lisaks korduste loendamisele). Selle tsükli toimemehanism on keeleti üsna erinev. Põhimõte aga lühidalt on selline, et enne tsükli alustamist on mingil viisil selgunud korduste arv. Tsüklil on vahendid kordamiste arvu loendamiseks ja arve pidamine selle üle on jäetud tsükli enda kanda, st programmeerija ei pea ise midagi loendama. Programmi kirjutaja asi on määrata, mille järgi korduste arv teada saadakse. Reeglina ei ole seda tsüklit ilus kasutada siis, kui korduste arvus tsükli täitmise ajal mingid muudatused tekivad ja tsüklit nö jõuga katkestama tuleb hakata.

Pythoni while-tsükkel[edit]

Pythoni eelkontrolliga tsükkel on nn while-tsükkel. Keelesõna while on sellise tsüklitüübi jaoks üsna tavaline. Tsüklilause üldkuju on järgmine:

while loogikaavaldis:
    tegevused

Tsükkel toimib eelpool kirjeldatud eelkontrolliga tsükli põhimõttel. Sarnaselt if-lausega tuleb kasutada taanet, et näidata lausete kuulumist või mittekuulumist tsükli kehasse. Teisisõnu tuleb eraldada vastav lausete plokk. Sõna while järel olev loogikaavaldis vastab loogikaavaldiste materjalis kirjeldatule. Seega lisaks tüüpilistele võrdlus- ja loogikatehetele töötab tsükkel ka juhul, kui sel kohal on suvaline (arvuline) muutuja. Nullist erineva väärtuse korral tsükli sisu täidetakse. Aga nagu oli loogikatehete juures ka mainitud, siis seda võimalust ei ole soovitatav kasutada - seni kuni sa täpselt ei tea, mida teed. Targem on kasutada nö loetavamaid ja mitte väga eripäraseid konstruktsioone.

Järgnev näide demonstreerib, kuidas while-tsükkel pannakse tööle 10 korda.

Näide 1

# Muutuja loendur näitab, mitmendat korda tsüklit korratakse
# Ühtlasi peab ta arvet korduste arvu ning lõpetamise üle
# NB! seda programmi võiks pigem kindla korduste arvuga tsükli abil esitada.
loendur = 0
while loendur < 10:
    loendur = loendur + 1                     # Sama tehte teeks ka omistuslause loendur += 1
    print ("Tsüklit täidetakse %d. korda." % loendur)

Teine näide loendab samuti kordumiste arvu, kuid nn countdown-stiilis ja kasutab loogikatehte asemel täisarvu (st muutujat loendur) tsükli töö juhtimiseks.

Näide 2

# Järgnev näide kasutab võimalust tsükli täitmist kontrollida
# aritmeetikaavaldisega - loendurit vähendatakse, kuni väärtuseks saab 0.
loendur = 10
while loendur:
    loendur = loendur - 1  # Sama tehte teeks ka omistuslause loendur -= 1    
    print ("Tsüklit on jäänud veel täita %d korda." % loendur)
print ("See oli üks väga hea programm")

While-tsükleid on loomulikult võimalik asetada üksteise sisse, st panna ühe tsükli abil teine tsükkel korduvalt korduma. Tsüklitega saab siduda valikulaused - näiteks lisada valikulause tsükli sisse. Kuid miks ka mitte panna korduma mõni tegevus if-lause else osas.

Loendamine ja summeerimine[edit]

Loendamine ja summeerimine kuuluvad väikeste elementaaralgoritmide loetelusse. Kui programmi töö käigus on vaja leida näiteks positiivsete arvude hulk, lugeda üle arvestuse saanud üliõpilased või testis antud õiged vastused tuleb kasutada loendamist. Loendamise põhimõte on järgmine: võetakse üks muutuja, mis hakkab tööle loendurina (vali talle sobiv nimi). Loendur on reeglina täisarvu tüüpi. Enne loendamise algust algväärtustatakse loendur nulliga (loendur = 0). Järgneva tegevuse käigus (tavaliselt toimub tegevus tsüklis) suurendatakse loendurit iga kord, kui loendamist vajav sündmus toimus või sobiv väärtus leiti (vt 1. näidet, seal loendatakse tsükli kordumiste arvu). Loenduri suurendamine toimub klassikaliselt lausega

loendur = loendur + 1 
#loenduri hetkel kehtivale väärtusele liidetakse 1 ja tulemus pannakse samasse muutujasse tagasi).

Erinevad keeled pakuvad sedalaadi tehteks ka erivõimalusi. Pythonis on olemas tehtemärk +=, mida inglisekeeli kutsutakse augmented assignmenttäiendatud omistamine. Lause loenduri suurendamiseks näeb vastavalt välja:

loendur += 1

Summeerimine on sarnane tegevus, kui 1 asemel liidetakse juurde summasse minevaid arve ja peale iga liitmistehet kasvab summa lisatud arvu võrra:

summa = summa + arv
#või lühemalt
summa += arv

Summeerimine on siin kasutusel tinglikus tähenduses, sest samahästi võib ka lahutada, korrutada, jagada ja astendada. Ka selleks on Pythonil olemas oma spetsiaalsed täiendatud omistamise märgid, mida võib kasutada lisaks universaalsele vormile – kogu tehte väljakirjutamisele.

Täiendatud omistamise tehtemärgid on:

+=   -=   *=   /=   %=   **=

Tähendused tulenevad vastavate tehtemärkide tavakasutusest ja on loodetavasti arusaadavad.

Pythoni for-tsükkel[edit]

Nagu juba eespool mainitud, töötavad kindla kordustearvuga tsüklid keeleti erinevalt. Tihti kannavad need tsüklid nn for-tsükli nime. Samuti on see Pythonis, kuid tema käitumine ei ole seda tüüpi tsükli kohta päris klassikaline. Järgnev ülevaade saab edaspidi täiendust, kui teadmistesse lihtandmetüüpide kõrvale lisanduvad struktuursed andmetüübid. Esialgu for-tsükli lihtsamast variandist.

Üldkuju on järgmine:

for tsüklimuutuja in jada:
    korratavad laused

Siin "jada" on kõige nö kirevam osa kogu lauses ja nõuab teadmisi struktuursetest andmetüüpidest (mis varsti ka käsitlemist leiavad).

Järgmine näide näitab for-tsükli kasutamist tavalise kindla kordustearvuga tsüklina, kus korduste arv antakse ette täisarvuna (NB! Seda ei pea tegema tingimata programmi kasutaja)

Näide 3

kordusi = int(input("Mitu kordust teeme? "))
for i in range(kordusi):
    print (i)

Vaata ja võrdle eelnevat näidet while-tsüklite näidetega. Proovi realiseerida while-näited for-tsükli abil.

Funktsioon range()[edit]

Funktsiooni range() ülesandeks on väljastada/tagastada täisarvude loend (list). Funktsiooni täielik kirjeldus:

range([start,]stop[,step]) -> list of integer

Et sel viisil tekkiud loendit saab edukalt kasutada for-tsükli juhtimiseks, siis funktsioonist veidi põhjalikumalt. Kui parameetrina on määratud üks täisarv (stop), tähistab see täisarvude jada lõppu, kusjuures jada algab 0-ga.

range(5)
[0,1,2,3,4]

Kui on määratud algus ja lõpp (start, stop), siis väljastatakse täisarvud alguse (kaasaarvatud) ja lõpu (väljaarvatud) vahel:

range(1,5) 
[1, 2, 3, 4]

Kolmas parameeter annab sammu (step), mille võrra täisarvu suurendatakse järgmise arvu saamiseks (vaikimisi on samm 1):

range(1,10,2)
[1, 3, 5, 7, 9]

Loendisse satub 0 täisarvu, kui algus ja lõpp on võrdsed või lõpp on algusest väiksem (positiivse sammu korral). Samm võib olla ka negatiivne.

Nagu parameetrite puhul ikka, võib need etteanda muutujatena. Seega võib täisarvude loendi sisu sõltuda eelnevast programmi käitumisest, kasutaja soovist vms. Ning range()-funktsiooni abil saab erinevatest tingimustest sõltuma panna ka for-tsüklit, tema korduste arvu.

Järgnevas näites saab kasutaja otsustada, mitut arvu ta ruutu tõsta soovib, seejärel laseb programm tal täpselt nii mitu arvu sisestada ning annab vastuseks arvude ruudud:

# Programm tõstab ruutu kasutaja sisestatud arvud
# Arvude koguse otsustab kasutaja
# Sisend: mitu - arvude hulk
#         arv - suurus, mida astendada

print("Selle programmiga näed erinevate täisarvude ruute.")
print ("Mitut arvu soovid ruutu tõsta? ")
mitu = int(input("> "))
print ("Aitäh!\nSisesta täisarve, kohe näed ka tulemust.")
for i in range(mitu):                        # Tsükli algus, kus range abil luuakse eeldus kordamiseks
    arv = int(input(str(i+1) + ". arv> "))
    ruut = 
    print ("Ruut on %d" % (arv ** 2))
print("Kohtumiseni1")                        # Lause, mida ei korrata, mis jääb tspklist välja

Edaspidi for-tsükli võimaluste tutvustamine jätkub vastavalt vajadustele.

Järekontrolliga tsüklit Pythonis ei ole.

Pythoni break-lause[edit]

Tavaliselt on keeltes olemas lause(d), mille abil saab tsükli täitmise suvalisel hetkel pooleli jätta ja tsüklist välja tulla. Programmi täitmine jätkub tsüklile järgneva lausega. Kui tegemist on sisemise tsükliga mitmest üksteise sees paiknevast tsüklist, tullakse välja vaid sisemisest tsüklist. Pythonis (ja ka teistes keeltes) on lause nimeks break. Lause kasutamine peaks olema sõltuv mingitest tingimustest (st ta peaks olema seotud if-lausega). Programmeerimise heast stiilist lähtudes on parem, kui break'i võimalusi liiga kergekäeliselt ei kasutata. Tegemist peaks olema ennekõike hädaolukorraga. Samas võib ta teinekord anda tulemuseks kergemini loetava koodi.

Tsüklite kasutamisest üldiselt[edit]

Tähelepanekuid tsüklite disainimiseks:

  1. Millist tsüklit kahest võimalikust on otstarbekas kasutada? Kui korduste arv on enne tsüki algust teada ja mitte miski tsükli sees toimuv seda segada ei saa, tasub kasutada for-tsüklit. Lihtsam on kirja panna kordamise tingimusi. Lisaväärtust omab tsüklimuutuja massiivide töötlemisel indeksi(te) genereerimiseks. Ülejäänud juhtudel on siis sobiv while-tsükkel.
  2. Leia üles laused, mis peavad tsükli sisse kuuluma. Ehkki see tundub triviaalsena, on õige lausetekomplekti tuvastamine sageli (eriti alguses) probleeme tekitav ning tulemused "põnevad".
  3. Koosta sobiv loogikatingimus. Tihti ei aita ühest võrdlustehtest, vaid on vaja kontrollida erinevate muutujate väärtusi korraga. Sel juhul võib vigu tekitada loogikatehete and ja or kasutamine. Samal ajal tuleks ka jälgida, et loogikatingimuse väärtus tsükli sees üldse muutuda saab. Kui kasutada loogikaavaldises muutujat, millega tsükli sees midagi ei tehta, on ilmselt kusagil viga. Vigane ei pruugi kindlasti olla tingimus, vaid tsükli kehas võivad sel juhul puududa mõned olulised laused.
  4. Õigete võrdlustehete kasutamine loogikatingimuses võib olla määrava tähtsusega tsükli korrektsuse tagamisel, õige korduste arvu saavutamisel, aga ka lõputu tsükli vältimisel.
  5. Enne tsükli alustamist (eriti while-tsükli puhul) tuleb jälgida ka loogikatingimuses osalevate muutujate algväärtustamist, sest nende muutujate väärtusi asutakse kohe kasutama. Kui õiged väärtused on jäänud kogemata omistamata, võib tsükkel mitte toimuda.

< 4. nädala teemad

< 5. nädala teemad