2. Harjoitukset: Ehdottoman merkilliset poikkeukset¶
Tässä harjoituksessa päästään tekemään ohjelmia, jotka ovat muutakin kuin suoraviivaisia laskuautomaatteja. Suorituskerrat voivat olla erilaisia, koska nyt käyttäjälle annetaan mahdollisuus olla suurammassa vuorovaikutuksessa ohjelman kanssa. Viime kerran harjoituksiin nähden näissä tehtävissä on huomattavasti enemmän haastetta, sillä kaikissa joutuu miettimään miten toimitaan erilaisissa tilanteissa. Viikon viimeiset tehtävät antavat myös eväitä lopputyön tekemiseen ja antavat hieman esimakua siitä, millaisia haasteita lopputyön kanssa tulee olemaan.
Osaamistavoitteet¶
Näiden harjoitusten jälkeen pystyt tekemään ohjelmia, joiden suoritus haarautuu käyttäjän toiveiden mukaan. Lisäksi osaat kommunikoida käyttäjälle tämän virheellisistä
syötteistä
ja ohjelmasi osaavat antaa miellyttävän näköisiä tulosteita. Keskeisenä tekijänä ovat materiaalin kolme konseptia: merkkijonot
, poikkeusten
käsittely ja ehtorakenteet
. Myös sanakirjoja
käytetään. Kukin tehtävä omalta osaltaan opettaa myös ohjelmien suunnittelua - eipä siltä oikein voi välttyäkään. Esimerkki¶
Tehtävänanto¶
Toteuta ohjelma, joka kysyy käyttäjältä kaksi kokonaislukua ja kertoo, onko toinen niistä toisen tekijä. Virheellisistä syötteistä huomautetaan erikseen. Alla on esimerkkejä ohjelman toiminnasta:
Anna ensimmäinen luku: 5 Anna toinen luku: 10 5 on 10:n tekijä.
Anna ensimmäinen luku: 9 Anna toinen luku: -3 -3 on 9:n tekijä.
Anna ensimmäinen luku: 7 Anna toinen luku: 4 Kumpikaan luvuista ei ole toisen tekijä.
Anna ensimmäinen luku: 2 Anna toinen luku: -2 2 on -2:n tekijä.
Anna ensimmäinen luku: 0 Anna toinen luku: 3 3 on 0:n tekijä.
Anna ensimmäinen luku: 0 Anna toinen luku: 0 0 on 0:n tekijä.
Anna ensimmäinen luku: aasi Ei näytä luvulta
Anna ensimmäinen luku: 0 Anna toinen luku: aasi Ei näytä luvulta
Ratkaisu¶
Lähdetään liikkeelle helpoimmasta, eli lukujen kysymisestä. Tämä menee jo viime kerralla opitulla tavalla, joten ei siitä sen enempää:
luku_1 = int(input("Anna ensimmäinen luku: "))
luku_2 = int(input("Anna toinen luku: "))
Tekijyyttä voi testata jakojäännöksellä. Jos jakojäännös on nolla, jakaja on jaettavan tekijä.
Mietitään sitten miten ohjelma voisi haarautua. Tulostusvaihtoehtoja on kaksi: joko toinen luvuista on toisen tekijä, tai kumpikaan ei ole. Tämä voisi viitata kahteen
haaraan
, alla olevaan tyyliin. Käytetään luonnosteluvaiheessa pass-komentoa korvaamaan ehtolauseen
sisälle tuleva koodi. if luku_1 % luku_2 == 0 or luku_2 % luku_1 == 0:
pass
else:
pass
Tästä seuraa kuitenkin ongelma: mistä tiedetään kumpi oli kumman tekijä? Suunnittelemamme ehtolause ei tätä kerro. Pitäisi siis lisätä toinen tarkistus tämän ehtolauseen sisälle, jossa selvitetään kumpi oli kumpi. Tämä onnistuisi katsomalla kumpi luvuista on pienempi, koska suurempi luku ei voi olla pienemmän tekijä. Tämä on kuitenkin tarpeettoman hankalaa. On helpompaa purkaa tämä yksi haara kahdeksi:
if luku_1 % luku_2 == 0:
pass
elif luku_2 % luku_1 == 0:
pass
else:
pass
Nyt 1. haarassa tiedetään, että juuri luku_2 on luku_1:n tekijä, ja vastaavasti 2. haarassa tiedetään, että asia oli toisin päin. Lisätään tehtävänannon mukaiset tulostukset näihin haaroihin. Se kannattaa tehdä kopioimalla teksti tehtävänannon esimerkeistä, ja korvaamalla sitten lukuarvot
aaltosulkupaikanpitimillä
. Huomaa argumenttien
muuttuva järjestys 1. ja 2. haaran välillä. if luku_1 % luku_2 == 0:
print(f"{luku_2} on {luku_1}:n tekijä.")
elif luku_2 % luku_1 == 0:
print(f"{luku_1} on {luku_2}:n tekijä.")
else:
print("Kumpikaan luvuista ei ole toisen tekijä.")
Yhdistetään tämä syötteen kysymiseen ja kokeillaan:
Error in page markup:
Description: Reverse for 'save_base_file' not found. 'save_base_file' is not a valid view function or pattern name.
Yritä syöttää toiseksi luvuksi nolla.
Tekijyydestä tiedetään, että kaikki luvut ovat nollan tekijöitä ja nolla on itsensä tekijä. Eli jos jompi kumpi luvuista on nolla, toinen on sen tekijä. Aiemmasta viisastuneena ei yritetä laittaa tätä yhden
haaran
alle, vaan lisätään kaksi uutta haaraa. Nyt on tärkeää miettiä myös mihin kohtaan uudet haarat sijoitetaan. Mikäli käyttäjä syöttää
nollan, koodin suoritus ei saa päästä niihin ehtolauseisiin
joissa suoritetaan jakamista. Niinpä nämä uudet ehdot tulee sijoittaa alkuun. Muistetaan siis, että ehtorakenteesta
suoritetaan ainoastaan ensimmäinen haara, jonka ehto
toteutuu - muita haaroja ei edes tutkita. Koko ohjelma tähän asti:Error in page markup:
Description: Reverse for 'save_base_file' not found. 'save_base_file' is not a valid view function or pattern name.
Voit nyt kokeilla syöttää nollaa uudestaan ja todeta, että koodi toimii.
Vielä puuttuu virheellisten
syötteiden
käsittely. Tämä onnistuu try-except-rakenteella, jossa otetaan kiinni materiaalissa pariin kertaan pyöritelty ValueError-poikkeus
:try:
luku_1 = int(input("Anna ensimmäinen luku: "))
luku_2 = int(input("Anna toinen luku: "))
except ValueError:
print("Ei näytä luvulta")
Tämä on muuten hyvä, mutta jos se lyödään ohjelman alkuun sellaisenaan saadaan vähemmän toivottuja tuloksia. Kokeile syöttää ensimmäiseen kysymykseen vaikka "aasi" niin näet itsekin miten koodi hajoaa.
Error in page markup:
Description: Reverse for 'save_base_file' not found. 'save_base_file' is not a valid view function or pattern name.
Virheilmoitus kyllä tulostuu, mutta rivin 6 ehtolauseesta saadaankin NameError! On tämä työmaa... Ongelman ydin on siinä, että rivin 2 suoritus loppuu heti kun int-funktiota kutsutaan, joten luku_1-muuttujaa ei koskaan luoda. Tässä tilanteessa olisikin toivottavaa, että ohjelman suoritus ei jatku, jos jompikumpi luvuista on virheellinen - eihän siinä ole edes mitään järkeä jatkaa. Ongelman voi ratkaista lyömällä try-except-rakenteeseen else-osan, ja työntämällä loput koodit sinne:
Error in page markup:
Description: Reverse for 'save_base_file' not found. 'save_base_file' is not a valid view function or pattern name.
Tämän rakennelman ymmärtäminen on keskeistä tämän kerran harjoitustehtävien suorittamiselle. Käytä siis hetki sen opiskeluun. Nyt ohjelma toimii oikein:
Anna ensimmäinen luku: aasi Ei näytä luvulta
Ohjelmien lopettaminen¶
Tässä harjoituksessa on tehtäviä, joissa pyydetään päättämään ohjelman suoritus esim. virhetilanteessa. Tämä tarkoittaa aina sitä, että virheestä ilmoittaminen jää ohjelman viimeiseksi suoritettavaksi riviksi siinä haarassa missä se on. Ohjelman rakenne tulee siis suunnitella siten, että kukin virheilmoitus on jonkun rakenteen sisällä siten, ettei sen jälkeen ole enää mitään suoritettavaa. Internetin ihmemaa löytää helposti quit- ja exit-funktiot, joilla ohjelman suorituksen voi lopettaa. Näitä ei kuitenkaan voi kurssilla käyttää. Syitä on kaksi.
Ensimmäinen syy liittyy oppimiseen: tässä harjoituksessa on tarkoitus oppia ohjaamaan ohjelman suorituksen kulkua ohjausrakenteilla - ei käyttämällä oikopolkuja. Oikopolkuja voi käyttää sitten, kun ohjelman suorituksen hallinnan perusteet ovat hallussa. Toinen syy on tekninen: nämä funktiot sammuttavat koko Python-prosessin. Tarkistukset toimivat siten, että tarkistinohjelma lataa palautetun koodin suoritettavaksi osaksi itseään (importilla). Näin ollen em. funktiot sammuttavat siis palauttamasi ohjelman lisäksi myös tarkistimen, ja saat tästä ilmoituksen "Tarkistin kaatui kesken tarkistuksen".
Yhteiset tehtävät¶
Kaikki nämä tehtävät kuuluvat osaksi kurssisuoritusta.
Lämmittelytehtävä¶
Error in page markup:
Description: Reverse for 'save_base_file' not found. 'save_base_file' is not a valid view function or pattern name.
Päätehtävät¶
Error in page markup:
Description: Reverse for 'save_base_file' not found. 'save_base_file' is not a valid view function or pattern name.
Error in page markup:
Description: Reverse for 'save_base_file' not found. 'save_base_file' is not a valid view function or pattern name.
Error in page markup:
Description: Reverse for 'save_base_file' not found. 'save_base_file' is not a valid view function or pattern name.
Error in page markup:
Description: Reverse for 'save_base_file' not found. 'save_base_file' is not a valid view function or pattern name.
Lopputyötehtävät¶
Seuraavista tehtävästi tulee tehdä yksi. Valinta kannattaa tehdä sen perusteella minkä lopputyöaiheen olet valinnut. Nämä tehtävät eivät ole pakollisia minimikurssisuoritukseen.
Error in page markup:
Description: Reverse for 'save_base_file' not found. 'save_base_file' is not a valid view function or pattern name.
Error in page markup:
Description: Reverse for 'save_base_file' not found. 'save_base_file' is not a valid view function or pattern name.
Error in page markup:
Description: Reverse for 'save_base_file' not found. 'save_base_file' is not a valid view function or pattern name.
Error in page markup:
Description: Reverse for 'save_base_file' not found. 'save_base_file' is not a valid view function or pattern name.
Anna palautetta
Kommentteja harjoituksista?