Tsükkel ehk korduslause. While-tsükkel
Selles peatükis tutvume tsükliga. Uurime tsükli osasid (tsüklimuutujat ning tsükli sisu) ja vaatame esimesi tsükleid sisaldavaid programme. Vaatame ka, kuidas katkestada käivitatud programmi töö ning kuidas vaadelda muutujate olekut programmi erinevates olekutes.
Korduv tegevus
Tihtipeale esineb programme kirjutades olukordi, kus mingisugust tegevust on tarvis korrata. Näiteks kui tahame, et programm väljastaks ekraanile viis korda üksteise alla "Tere!", siis sobiks selline programm:
print("Tere!") print("Tere!") print("Tere!") print("Tere!") print("Tere!")
Kui me tahame väljastada "Tere" sada korda, peaksime ettevaatlikult loendades lisama veel 95 rida. Miljoni kordusega muutuks olukord lausa naeruväärseks. Tegelikult on programmeerimiskeeltes olemas võimalused korduste lühemaks esituseks. Neist levinuim on while-tsükkel.
while-tsükkel
while-tsükliks nimetame struktuuri, mis koosneb jätkamistingimusest ning tsükli sisust. Tsükli sisu täidetakse vaid siis, kui päises antud jätkamistingimus kehtib. Selles suhtes on while väga sarnane üheharulise if-lausega.
Kui jätkamistingimus on tõene, siis täidetakse tsükli sisu, seejärel tullakse tagasi jätkamistingimuse kontrolli juurde. Nii täidetakse tsükli sisu vastav arv kordi kuni jätkamistingimus kehtib.
Joonis 1. Eelkontrolliga while-tsükli plokkskeem
Kui jätkamistingimust kontrollitakse enne tsükli sisu esmakordset täitmist, nimetame seda eelkontrolliga tsükliks. Olemas on ka järelkontrolliga tsüklid, kuid siin (ning paljudes teistes materjalides) peetakse tsükli puhul vaikimisi silmas eelkontrolliga tsüklit.
Vaatame näidet, kuidas while-tsükliga print
-käsklust viist korda välja kutsuda saaks:
i = 0 while i < 5: # tsükli algus ning jätkamistingimus print("Tere!") # tsükli sisu i = i + 1
Jätkamistingimus
Tsükli jätkamistingimus (nagu ka if
-lause tingimus) on tõeväärtustüüpi. Kui tingimus on täidetud (tingimusavaldise väärtus on tõene), siis täidetakse tsükli sisu. Kui tingimus ei ole täidetud, siis tsükkel lõpeb ning tsükli sisu ei täideta.
Tavaliselt on tingimus esitatud võrdlemisena (näiteks i < 5
), aga selleks võib olla ka puhas tõeväärtus True
või False
. Viimane on üsna tarbetu: nii karmi tingimusega ei täideta tsükli sisu kunagi. Jätkamistingimuse True
puhul on tegemist lõpmatu tsükliga, sest tingimusavaldis on alati tõene. Siis täidetakse tsükli sisu piiramatu arv kordi ning programm ei lõpeta oma tööd kunagi. Lõpmatut tsüklit kasutatakse ka sihipäraselt, kuid sellisel juhul on tsüklist väljumine teisiti korraldatud.
Tsükli sisu
Tsükli sisu on tegevus, mida soovime korrata. “Tere!” trükkimise ülesandes on selleks rida print("Tere!")
. Tsükli sisu võib olla kui tahes keeruline. Seal võib sisalduda erinevaid käske ja ka teisi tsükleid.
Programmi katkestamine
Kui me käivitame Pythonis meelega või kogemata sellise programmi, mis oma tööd ei lõpeta, siis saame Thonnys programmi töö katkestada Stop-märgiga nupu või klahvikombinatsiooni Ctrl + F2 abil. (Keskkonnas IDLE katkestatakse programmi töö Ctrl + C abil.)
Korduv tegevus while-tsüklit kasutades
Lähme tagasi algse ülesande juurde. Me tahame tsükli abil trükkida ekraanile "Tere!" viis korda. Selleks peab tsükli sisus olema midagi, mis muutub selliselt, et pärast viiendat korda tsükli sisu enam ei täideta. Lahendus on loendada, mitu korda on tsükli sisu käivitatud. Kokkuleppeliselt on sellise loendaja ("tsüklimuutuja") nimeks i
. Loome muutuja nimega i
. Olgu i
väärtus esialgu 0: i = 0
. Igal tsükli sammul liidame väärtusele 1. Seda saame teha avaldisega:
i = i + 1
Selle rea jooksutamisel kasvab i
väärtus ühe võrra. Pane tähele, et programmeerides on võrdusmärgil erinev tähendus kui matemaatikas. Siin tähistab võrdusmärk väärtuse omistamist võrdusmärgist vasakul asuvale muutujale. Võrdusmärgi parem pool on avaldis, mille tulemus muutujale omistatakse. Hetkel arvutatakse esmalt avaldis i + 1
ning saadud tulemus määratakse muutuja i
uueks väärtuseks.
Teiseks peame määrama jätkamistingimuse. Selleks sobib i < 5
. Kui i
on esialgu 0 ja igal sammul liidetakse 1, siis just 5 sammuga jõuame olukorda, kus tingimus i < 5
ei ole enam täidetud. Paneme nüüd programmi kokku. Endiselt on olulisel kohal koolon ja taane:
i = 0 while i < 5: print("Tere!") i = i + 1
Joonis 2. "Tere!"-programmi plokkskeem.
Analüüsime programmi tööd sammude kaupa:
- Jätkamistingimus (
i < 5
) on täidetud, kui esimest korda tsükli juurde jõuame, sest0 < 5
. - Pärast esmakordset sisu täitmist on
i
väärtus 1 ja jätkamistingimus ikkagi täidetud, sest1 < 5
. - Pärast teist korda on
i
väärtus 2 ja ikka saame jätkata, sest2 < 5
. - Ja siis
i
on 3 ja ikka3 < 5
. - Ja siis
i
on 4 ja ikka4 < 5
. - Ja siis
i
on 5 ja kontrollime, kasi < 5
? Kas5 < 5
? Ei ole, sest 5 ja 5 on võrdsed, seega võrratus5 < 5
ei kehti ja jätkamistingimus on väär.
Väärtuse muutmise lühivariandid
Kuna muutuja väärtuse muutmist eelmise väärtuse alusel tuleb sagedasti ette, siis on selleks ka lühemad variandid olemas. Näiteks a = a + 3
asemel võime kirjutada a += 3
. Samasugused variandid on ka lahutamise (-=
), korrutamise (*=
), jagamise (/=
), täisarvulise jagamise (//=
), jäägi leidmise (%=
) ja astendamise (**=
) jaoks.
Muutuja vaatlemine print
abil
Lisame tsükli kehasse ühe rea, mis i
väärtuse ekraanile tooks, et seda paremini jälgida.
Näiteprogramm. Muutuja vaatlemine print
abil
i = 0 while i < 5: print(i) print("Tere!") i = i + 1
Käivita programm ja näed, kuidas i
väärtused muutuvad.
Muutujate vaatlmine Thonnys
Kui me programmi alles teeme, siis on meil kasulik teada, mis väärtus on muutujal programmi mingites kohtades. Thonnys on muutuja väärtuste jälgimine sisse ehitatud ja seda saab nähtavale tuua View-menüüst valikuga Variables.
Kuna programm töötab kiiresti, siis tavaliselt käivitades jäävad näha ainult programmi töö lõpus kehtivad väärtused. Selleks, et samm-sammult programmi tööst ülevaadet saada, tuleb programm käivitada silumisrežiimis (debug mode) – putukaga nupuga või Run-menüüst Debug current script. Seejärel saab erineva ulatusega samme teha vastavate nuppude või Run-menüü valikute abil.
Joonis 3. Silumisrežiim Thonnys
Veel while-lausest
while-lause keha täidetakse vaid siis, kui päises antud tingimus kehtib. Selles suhtes on while väga sarnane üheharulise if-lausega. Erinevus on selles, et kui kehas olevad laused on täidetud, siis minnakse uuesti päises näidatud tingimust kontrollima – kui tingimus kehtib ikka veel, siis täidetakse kehas olevad laused uuesti jne. Kui lõpuks tingimus enam ei kehti (eelnevas näites kui 5 korda on ekraanile "Tere!" väljastatud), minnakse edasi while-lausele järgnevate lausetega.
Korduslauseid kasutades tuleb kindlalt ära määrata sündmus, mille tagajärel tsükkel tegevuse lõpetab. Vastasel juhul võib programm lõpmatuseni sama tegevust korrata.
Proovige, mis juhtub kui käivitate lõpmatu tsükli Thonnys
i = 1 while i > 0: print(i) i *= 2
Selle programmi puhul on tingimus selline, mis kehtib iga positiivse i
väärtuse puhul ja tsükli sisus i
-väärtus kasvab lõpmatult. While-lause päises kontrollitakse, kas 1 on suurem kui 0. Kuna see vastab tõele, siis korratakse tsükli kehas toimuv tegevus (kahega korrutamine) lõpmatult. Sellise lõpmatu tsükli katkestamiseks kasutage Thonnys Stop-märgiga nuppu või klahvikombinatsiooni Ctrl + F2.
Selle olukorra parandamiseks peaks tsükli tingimuse panema nii, et tsükkel lõpetatakse muutuja teatud väärtuse saamisel. Näiteks
i = 1 while i < 30: print(i) i *= 2
Programmi on muudetud nii, et iga tsükli korduse ajal kahekordistub muutuja i
väärtus. Lisaks on nüüd while-lause päis ära muudetud. Iga kord kontrollitakse, kas muutuja on väiksem kui 30. Kui muutuja muutub suuremaks kui 30, siis tsükli sisu enam ei täideta.
Kui soovite kõik muutuja väärtused kokku liita, siis saab luua selleks eraldi muutuja summa
, mille väärtuse saab väljastada erkaanile eraldi real väljastpool tsüklit. Näiteks
summa = 0 i = 1 while i < 30: print(i) summa += i i *= 2 print(f"Summa on: {summa}")
Enne katsetamist proovi ära arvata, mis arvud ekraanile väljastatakse.
Tähelepanu!!! Kui arvu- või sõneoperatsioonides (e tehetes) kasutada muutujaid (ntn + 1
võitekst.upper()
), siis võib avaldise kujust jääda mulje, et operatsiooni käigus muudetakse muutuja väärtust. Tegelikult genereeritakse tehte tulemusena hoopis uus väärtus ja kasutatud muutujaga midagi ei juhtu.
Selles veendumiseks uuri järgmisi käsurea näiteid, kus kõigepealt omistatakse muutujale mingi väärtus, seejärel kasutatakse muutujat mingis tehtes (mis konstrueerib uue väärtuse) ning lõpuks demonstreeritakse, et see ei mõjutanud muutuja väärtust:
>>> n = 3 >>> n + 2 5 >>> n 3 >>> sõna = ' kala ' >>> sõna.strip() 'kala' >>> sõna ' kala ' >>> tekst = '3' >>> int(tekst) 3 >>> tekst '3'
While-tsükli video
Enesekontroll
break
Tsükli lõpetamise määrab tavaliselt tsükli päises olev tingimus. Sellele lisaks on Pythonis veel üks võimalus tsükli töö lõpetamiseks – selleks tuleb tsükli kehas anda sobival hetkel käsk break
.
summa = 0 arvutusi = 0 while summa < 1000: if arvutusi >= 5: print("Ei saanud 1000 kokku") break liidetav = int(input("Sisesta arv, mida liita: ")) summa = summa + liidetav # Võib kirjutada ka: summa += liidetav print(f"Praegune summa on: {summa}") arvutusi += 1
Selles näites soovime, et programm küsiks kasutajalt arve, et arvutada kõikide arvude summat. Programm lõpetab, kui summa läheb üle 1000 või kui kasutaja on andnud 5 arvu.
Tegelikult pole break
-lause Pythoni programmides hädavajalik - tsükli saab alati ümber kirjutada nii, et kõiki jätkamise/lõpetamise tingimusi kontrollitakse tsükli päises, aga vahel on break
-iga lahendus lihtsam.
Mõnikord on mugav tsükli lõpetamise tingimust kontrollida ainult tsükli kehas, sel juhul pannakse tsükli päisesse alati kehtiv tingimus True
. Järgnev näide arvutab ringi pindala etteantava raadiusega.
while True: raadius = input("Mis on ringi raadius? Lõpetamiseks vajuta ENTER: ") if raadius == "": break else: pindala = 3.14 * int(raadius) * int(raadius) print(f"Ringi pindala on: {pindala}")
Kasutaja saab ise valida, kui kaua ta programmi kasutada tahab.
continue
Vahel võib juhtuda, et peale lõpetamise tingimuse võib ette tulla olukord, kus me soovime tsükli täitmist kindlate andmete puhul vältida. Sõna continue
katkestab tsükli jooksva sammu täitmise ja jätkab tsükli täitmist muutuja järgmise väärtusega.
Soovime leida paaritud arvud kuni arvuni 10.
for arv in range(10): if arv % 2 == 0: continue print(f"Tegu on paaritu arvuga {arv}")
Enesekontroll
Tsüklis erindite püüdmine try-lausega
Vaatame ühte lühikest ja lihtsat programmi:
arv1 = float(input("Sisesta esimene arv: ")) arv2 = float(input("Sisesta teine arv: ")) print(f"Nende arvude jagatis on {arv1 / arv2}")
Mis juhtub siis, kui kasutaja sisestab ühe soovitud arvu (nt. 45) asemel kogemata midagi, millest Python ei oska arvu välja lugeda (näiteks 45t)? Proovi järele!
Ilmselt nägid, et Python väljastas ekraanile veateate, ning lõpetas programmi töö. Selle programmi puhul polnud see suur õnnetus, aga keerulisemates programmides, kus kasutaja on suure ülesandega poole peale jõudnud, on kahju kui selline viga programmi veateatega sulgeb. Seetõttu on Pythonisse loodud võimalused täitmisaegsete vigade e erindite „püüdmiseks“.
Vigade püüdimiseks tuleb kasutada try-lauset. Alustame näitest:
try: arv1 = float(input("Sisesta esimene arv: ")) arv2 = float(input("Sisesta teine arv: ")) print(f"Nende arvude jagatis on {arv1 / arv2}") except: print("Hmm..., midagi läks vussi.")
Nii nagu if-else-lause, koosneb ka try-lause mitmest osast – võtmesõna try alla kirjutatakse laused, mida soovitakse normaalsel juhul täita ning võtmesõna except alla laused, mida täidetakse siis, kui try-osa lausete täitmisel tekib mingi viga (siit ka võtmesõna except – neid lauseid soovime täita vaid erandjuhtumitel).
Selle näite puhul on küsitav, kuivõrd try-lause lisamine midagi paremaks tegi – me küll peitsime kasutaja eest ära koleda mitmerealise veateate (kas see peitmine oli üldse hea?), aga vigase sisestuse korral jäi kasutaja ikkagi vastusest ilma. Koodi ümber paigutades saame me aga programmi, mis küsib kasutajalt arve niikaua, kuni lõpuks teisendamine ja jagamine õnnestub. Püüame ka kinni konkreetse veateate ValueError
:
jätkub = True while jätkub: try: arv1 = float(input("Sisesta esimene arv: ")) arv2 = float(input("Sisesta teine arv: ")) print(f"Nende arvude jagatis on {arv1 / arv2}") jätkub = False except ValueError: print("Sisestage korrektne arv.") print("Proovime uuesti!")
Siin me panime kogu programmi loogika tsüklisse, millest pääseb välja muutes tsükli jätkutingimuse väärtuseks False
. Selle käsuni jõuab Python aga ainult siis, kui kõik try-osa laused edukalt läbitakse – niipea, kui kusagil eespool tekib mingi viga, hüpatakse kohe except-osasse, kus püütakse kinni ValueError ning peale selle täitmist jätkatakse uuesti tsükli algusest.
Kui me tahame selle tsükliprogrammi kenasti põhifunktsiooni sisse panna, siis peaksime koostama main
-funktsioonia ja kutsuma selle välja programmi lõpus. Selliselt tuleb meeles pidada, et kõik read, mis lähevad main
-funktsiooni sisse, peavad algama taandega:
def main(): jätkub = True while jätkub: try: arv1 = float(input("Sisesta esimene arv: ")) arv2 = float(input("Sisesta teine arv: ")) print(f"Nende arvude jagatis on {arv1 / arv2}") jätkub = False except ValueError: print("Sisestage korrektne arv.") print("Proovime uuesti!") if __name__ == "__main__": main()
Hoiatus!
Vigade kinnipüüdmine on põhjendatud ainult siis, kui sa tõesti oskad selle veasituatsiooniga midagi ette võtta. Vigu ei tohiks püüda kinni lihtsalt selleks, et vältida veateate ekraanile jõudmist – see võib tekitada situatsiooni, kus programmis on mingi probleem, aga ei programmeerija ega kasutaja ei saa sellest teada ning programm annab süüdimatult välja valed tulemused. Seepärast ongi hea harjumus püüda kinni konkreetsed veateated (nt ValueError
) ja anda vastav tagasiside. Näiteks nulliga jagades võib juhtuda ka ZeroDivisionError
, mille võiks eraldi kinni püüda ja anda ka vastav tagasiside.