Toistokäsky while

While-käskyn yleinen rakenne on

while ehto:
    käsky

Kun ohjelmassa suoritetaan tällaistä käskyä, toimitaan seuraavasti: Ensin tarkastetaan, onko ehto tosi. Jos se on epätosi, siirrytään suoraan ohjelmassa eteenpäin while-rakenteen sisällä olevan käskyn (tai käskyjen) ohi sen jälkeen tulevaan kohtaan. Jos taas ehto on tosi, suoritetaan käsky. Tämän jälkeen tarkastetaan uudelleen, onko ehto tosi. Jos se on tosi, suoritetaan käsky jälleen. Sen jälkeen tarkastetaan taas, onko ehto tosi. Näin jatketaan, kunnes tullaan siihen tilanteeseen, että ehto on epätosi. Silloin lopetaan while-käskyn suoritus ja siirrytään ohjelmassa eteenpäin.

Toistettavia käskyjä voi olla joko yksi tai useita peräkkäin. Jälleen sisennysten avulla osoitetaan, mitkä käskyt kuuluvat toistettavien käskyjen sarjaan. Ehdon totuusarvo tarkistetaan kuitenkin vain ennen ensimmäisen käskyn suoritusta ja aina koko käskysarjan suorituksen jälkeen, ei siis käskysarjaan kuuluvien käskyjen välissä.

Ensimmäinen esimerkkiohjelma pyytää käyttäjältä neliön sivun pituuden ja laskee ja tulostaa sitten pinta-alan. Ohjelma kuitenkin tarkistaa ennen laskemista, että käyttäjän antama sivun pituus ei ole negatiivinen. Jos käyttäjä antaa negatiivisen sivun pituuden, ohjelma pyytää käyttäjältä uutta sivun pituutta niin kauan, että käyttäjä antaa ei-negatiivisen arvon.

Sivun pituuden tarkistuksen voisi periaatteessa tehdä if-käskyn avulla. Siinä on kuitenkin se ongelma, että käyttäjä voi antaa toisenkin kerran negatiivisen sivun pituuden. Kun tarkistus tehdään toistokäskyn avulla, voidaan käyttäjältä pyytää uutta sivun pituutta niin monta kertaa kuin on tarpeen.

def main():
    rivi = input("Anna nelion sivun pituus (cm): ")
    sivu = float(rivi)
    while sivu < 0:
        print("Sivun pituus ei saa olla negatiivinen!")
        rivi = input("Anna sivun pituus (cm): ")
        sivu = float(rivi)
    ala = sivu * sivu
    print("Nelion pinta-ala on", ala, "cm2.")

main()
Pyydetään käyttäjältä neliön sivun pituus, luetaan se ja muutetaan kokonaisluvuksi.
Tarkistetaan, onko sivun pituus negatiivinen.
Jos on, pyydetään käyttäjältä uutta sivun pituutta. Palataan sen jälkeen tarkistamaan, onko uusi sivun pituus negatiinen.
Kun tarkistuksessa havaitaan, että käyttäjä on antanut ei-negatiivisen sivun pituuden, lopetetaan toistokäskyn suoritus ja siirrytään tänne. Lasketaan neliön pinta-ala ja tulostetaan se.

Tutki ohjelman toimintaa vielä seuraavan animaation avulla. Anna ohjelmalle useita kertoja peräkkäin negatiivinen sivun pituus ja tarkkaile, miten ohjelman suoritus etenee.

Tarkastellaan seuraavana esimerkkinä ohjelmaa, joka pyytää käyttäjältä 10 kokonaislukua ja laskee niiden keskiarvon.

def main():
    print("Anna 10 kokonaislukua, lasken niiden keskiarvon")
    i = 0
    summa = 0
    while i < 10:
        rivi = input("Anna seuraava luku: ")
        luku = int(rivi)
        summa = summa + luku
        i = i + 1
    keskiarvo = summa / 10
    print("Niiden keskiarvo on", keskiarvo)

main()

Ohjelmassa käytetään muuttujaa i pitämään kirjaa siitä, montako lukua on jo luettu. Ennen toistokäskyn alkua i:lle annetaan arvo 0 ja jokaisella kierroksella i:n arvoa kasvatetaan yhdellä. Koska lukuja halutaan lukea 10 kappaletta, on toistokäskyn suorittamista jatkettava niin kauan kuin i:n arvo on pienempi kuin 10.

Tarvitaan myös muuttuja, johon kerätään jo luettujen lukujen summa. Tähän tarkoitukseen ohjelmassa käytetään muuttujaa summa, joka myöskin alustetaan nollaksi ennen toistokäskyn alkua. Toistokäskyn jokaisella kierroksella käyttäjältä luettu luku lisätään muuttujan summa vanhaan arvoon.

Toistokäskyn päätyttyä lasketaan keskiarvo jakamalla summa luettujen lukujen määrällä.

Esimerkki ohjelman suorituksesta:

Anna 10 kokonaislukua, lasken niiden keskiarvon
Anna seuraava luku: 12
Anna seuraava luku: 15
Anna seuraava luku: 42
Anna seuraava luku: 33
Anna seuraava luku: 76
Anna seuraava luku: 45
Anna seuraava luku: 22
Anna seuraava luku: 12
Anna seuraava luku: 34
Anna seuraava luku: 33
Niiden keskiarvo on 32.4

Edellisessä ohjelmassa luettavien lukujen määräksi on määrätty 10. Jos lukumäärää halutaan muuttaa, pitää muuttaa itse ohjelmaa. Ohjelma on kuitenkin kirjoitettu niin, että muutos pitää tehdä kolmeen eri paikkaan: käyttäjälle annettavaan alkuohjeeseen, toistokäskyn ehtoon ja keskiarvon laskevaan lausekkeeseen. On paljon parempi kirjoittaa ohjelma niin, että luettavien lukujen määrä tallennetaan muuttujaan, jota sitten käytetään ohjelmassa aina siellä, missä määrää tarvitaan. Tällöin lukumäärää on tarvittaessa helppo muuttaa. Muutos tarvitsee tehdä vain yhteen paikkaan, ja tuon paikan löytäminen on helppoa silloinkin, kun ohjelma on pitkä.

Tällaista muuttujaa kutsutaan vakioksi ja sen nimi kirjoitetaan isoilla kirjaimilla kertomaan siitä, että ohjelman suorituksen ei ole tarkoitus muuttaa tämän muuttujan arvoa sen jälkeen, kun ohjelmassa on annettu muuttujalle vakioalkuarvo. Muutettu ohjelma on esitetty alla.

def main():
    LKM = 10
    print("Anna", LKM, "kokonaislukua, lasken niiden keskiarvon")
    i = 0
    summa = 0
    while i < LKM:
        rivi = input("Anna seuraava luku: ")
        luku = int(rivi)
        summa = summa + luku
        i = i + 1
    keskiarvo = summa / LKM
    print("Niiden keskiarvo on", keskiarvo)

main()

Seuraavan animaation avulla voit tutkia vielä ohjelman etenemistä vaiheittain. Animaatiossa lukujen määräksi on asetettu kolme, jotta animaatiosta ei tulisi liian pitkä.

Seuraava versio ohjelmasta on sellainen, että käyttäjä voi itse kertoa, kuinka monta lukua hän antaa. Ohjelma pyytää aluksi käyttäjältä annettavien lukujen lukumäärän ja tallentaa sen muuttujaan lukujen_maara. Sen jälkeen ohjelma pyytää toistokäskyn avulla näin monta lukua ja laskee lopuksi niiden keskiarvon. Koska lukujen määrä pyydetään käyttäjältä, on ohjelmaan lisätty tarkistus, jolla vältetään ohjelman kaatuminen jakolaskussa siinä tapauksessa, että lukujen määrä on 0.

def main():
    print("Lasken keskiarvon antamistasi kokonaisluvuista.")
    rivi = input("Anna lukujen maara: ")
    lukujen_maara = int(rivi)
    i = 0
    summa = 0
    while i < lukujen_maara:
        rivi = input("Anna seuraava luku: ")
        luku = int(rivi)
        summa = summa + luku
        i = i + 1
    if lukujen_maara > 0:
        keskiarvo = summa / lukujen_maara
        print("Niiden keskiarvo on", keskiarvo)
    else:
        print("Et antanut yhtaan lukua.")

main()
Lasken keskiarvon antamistasi kokonaisluvuista.
Anna lukujen maara: 5
Anna seuraava luku: 2
Anna seuraava luku: 4
Anna seuraava luku: 8
Anna seuraava luku: 8
Anna seuraava luku: 10
Niiden keskiarvo on 6.4

Tässäkin versiossa on kuitenkin se ongelma, että käyttäjän on etukäteen tiedettävä, montako lukua hän haluaa antaa. Jos lukuja on paljon, niiden laskeminen voi olla työlästä. Tällöin käyttäjän kannalta olisi parempi, että hän voisi antaa lukuja niin kauan kuin niitä riittää ja sitten kun luvut ovat lopussa, osoittaa jollain sopivalla arvolla lukujen loppuneen.

Jos esimerkiksi tiedetään, että kaikki annettavat luvut ovat suurempia tai yhtäsuuria kuin nolla, voi käyttäjä osoittaa lukujen loppuneen antamalla negatiivisen luvun. Ohjelma kirjoitetaan niin, että se lopettaa lukujen lukemisen ensimmäisen tällaisen luvun saatuaan. Viimeiseksi luettua negatiivista lukua ei tällöin oteta mukaan keskiarvoa laskettaessa. Ohjelma toteutetaan siten, että ensimmäinen luku luetaan jo ennen toistokäskyn alkua ja toistokäskyä jatketaan niin kauan kuin viimeiseksi luettu luku on vähintään nolla. Koska luettavien lukujen määrää ei tiedetä etukäteen, on se laskettava. Sitä varten ohjelmassa on muuttuja lukujen_maara, jota kasvatetaan yhdellä joka kierroksella.

def main():
    print("Lasken keskiarvon antamistasi ei-negatiivisista kokonaisluvuista.")
    print("Lopeta negatiivisella luvulla.")
    lukujen_maara = 0
    summa = 0
    rivi = input("Anna ensimmainen luku: ")
    luku = int(rivi)
    while luku >= 0:
        lukujen_maara = lukujen_maara + 1
        summa = summa + luku
        rivi = input("Anna seuraava luku: ")
        luku = int(rivi)
    if lukujen_maara > 0:
        keskiarvo = summa / lukujen_maara
        print("Niiden keskiarvo on", keskiarvo)
    else:
        print("Et antanut yhtaan ei-negatiivista lukua.")

main()

Esimerkki ohjelman suorituksesta:

Lasken keskiarvon antamistasi ei-negatiivisista kokonaisluvuista.
Lopeta negatiivisella luvulla.
Anna ensimmainen luku: 24
Anna seuraava luku: 12
Anna seuraava luku: 30
Anna seuraava luku: 13
Anna seuraava luku: -5
Niiden keskiarvo on 19.75

Huomautus: else-osa while-käskyn yhteydessä

While-käskyn yhteyteen on periaatteessa mahdollista liittää myös else-osa, joka suoritetaan siinä vaiheessa, kun while-käskyn ehto on epätosi. Normaalisti else-osan liittämiseen ei ole mitään syytä, koska ohjelmassa siirrytään joka tapauksessa while-käskyä seuraavaan käskyyn siinä vaiheessa, kun while-käskyn ehto on epätosi. Jos kuitenkin while-käskyn sisällä on käsky break (jota tällä kurssilla ei opeteta), else-osalla on merkitystä. Tällä kurssilla ei ole mitään tarvetta liittää while-käskyyn else-osaa eikä sitä tällä kurssilla suositella, koska aloittelijalla while-käskyn else-osa menee helposti sekaisin jonkin ohjelmassa olevan if-käskyn else-osan kanssa.