1. Harjoitukset: Geomaagista koodimystiikkaa¶
Ensimmäisissä varsinaisissa harjoituksissa treenataan ohjelmoinnin perusasioita: ongelmanratkaisua, ratkaisun osittamista ja Pythonin käyttöä ratkaisun toteuttamiseen. Vaikka ei tässä vaiheessa juuri mitään osattaisikaan, se ei estä meitä etenemästä ammattitaitoisen järjestelmällisesti kohti voittoa. Hyvä ohjelmointiprosessi on parasta kehittää mahdollisimman aikaisessa vaiheessa opettelua, kun ongelmat ovat vielä yksinkertaisia eivätkä yksityiskohdat vaivaa läheskään yhtä paljoa. Tämän kerran tehtävät ovatkin melkein puhdasta matematiikkaa suoraan peruskoulun penkiltä. Ainoastaan se, että lähestymme niitä ohjelmoinnin kautta on uutta.
Prosessia ei tarvitse kehittää tyhjästä. Tämän kerran tehtävät on rakennettu siten, että puhtaasti punaista lankaa seuraamalla saat käsityksen siitä, miten ohjelmoinnissa kannattaa edetä. Tämän harjoituksen eväillä pärjää aivan hyvin kurssin alkupuoliskon kanssa. Puhutaan sitten luennolla lisää asiasta, kun oikea aika koittaa.
Osaamistavoitteet¶
Keskeinen osaamistavoite tälle viikolle on vihkiytyä ohjelmointiprosessiin. Tähän kuuluu ongelman ratkaisun jäsentäminen siten, että sen kulku voidaan kuvata yksiselitteisesti vaihe vaiheelta. Juuri yksiselitteisyys on avainsana ohjelmoinnissa, sillä tietokone ottaa vastaan ainoastaan täsmällisiä ohjeita. Toinen vaihe prosessia on tunnistaa, millä tavalla käytössä olevat työkalut - tässä tapauksessa Python - sopivat kuhunkin ratkaisun vaiheiseen.
Hieman konkreettisempia, teknisempiä tavoitteita tälle kerralle ovat kaksi keskeistä ohjelmointikäsitettä:
muuttujat
ja funktiot
; miten informaatio liikkuu muuttujien avulla ohjelman osasta toiseen - ja erityisesti miten ne liikkuvat funktioiden välillä. Lisäksi opitaan tietenkin syntaksi
sille, miten kaikki nämä perusasiat tehdään. Kurkistamme myös hieman syötteiden
ottamiseen käyttäjältä.Esimerkki¶
Tehtävänanto¶
Palloja on hieman hankala mittailla. Mittanauhalla helpoiten pystyy mittaamaan pallon ympärysmitan, mutta mites sitten suu pannaan, kun kysytäänkin pallon tilavuutta ja pinta-alaa? Mitattavia pallojakin on lyöty eteen kymmeniä erikokoisia... Parempi siis tehdä ohjelma joka laskee nämä tiedot kun siihen lyödään ympärysmitta.
Ratkaisu¶
Tavoitteena on siis ratkaista kaksi mysteeriarvoa: pallon pinta-ala ja tilavuus. Ensimmäinen askel kohti voittoa on siis selvittää, miten nämä saadaan laskettua. On tärkeää lähteä liikkeelle nimenomaan siitä, miten varsinaiseen kysymykseen voidaan vastata, jotta tiedämme, mitä muilla annetuilla tiedoilla voi tehdä. Niin tilavuudelle kuin pinta-alallekin löytyvät olemassaolevat matemaattiset kaavat, jotka voi palautella mieleen koulugeometriasta tai katsoa Wikipediasta.
V = 4 / 3 * pi * r^3 A = 4 * pi * r^2
Nämähän näyttävät oikeastaan jo valmiiksi ohjelmakoodilta. Molemmissa kaavoissa tarvitaan kaksi "tuntematonta": pi ja r. Tiedämme kuitenkin mikä pi on, ja lisäksi tiedämme että sille saa oikein hyvän arvon math-
moduulista
. Selvitettäväksi jää siis r, eli pallon säde. Tätä ennen voimme kuitenkin jo kirjoittaa hieman koodia. Aloitetaan materiaalin loppupuolen asiasta, eli importin
käytöstä. Kaikki importit tulee tehdä kooditiedoston alussa, joten siitä on hyvä aloittaa. import math
Voimme kirjoittaa myös valmiiksi kaksi
funktiota
: yhden pinta-alan laskemiseen ja toisen tilavuuden laskemiseen. Näistä on hyvä tehdä omat funktionsa, koska niiden avulla voidaan helposti laskea minkä tahansa pallon arvot - tätähän tehtävänannossa haluttiin. Molempien kaavojen lopputulos riippuu ainoastaan pallon säteestä, joten se on luonnollinen valinta funktion parametriksi
. Funktioiden luonnollisesti tulisi palauttaa
kaavoista saadut tulokset. Saamme siis aikaan kaksi funktiota, jotka liitetään importin perään: import math
def laske_ala(sade):
return 4 * math.pi * sade ** 2
def laske_tilavuus(sade):
return 4 / 3 * math.pi * sade ** 3
Muistetaan vain, että toisin kuin yllä kuvatuissa kaavoissa, Pythonissa potenssiin korotus tapahtuu **-
operaattorilla
. Nyt pitäisi vielä keksiä, mistä saadaan tuo mystinen säde. Tiedossa siis on pallon ympärysmitta, eli toisin sanoen pallon läpileikkauksen piiri. Pallon muodosta johtuen sen läpileikkauksen piirin säde on sama kuin pallon säde. ![](/media/images/pallo.png)
Ympyrän piirin laskemiseen on sillekin oma kaavansa:
p = 2 * pi * r
Tästä voidaan pyöritellä miten lasketaan r, jos tiedetäänkin p:
r = p / (2 * pi)
Aiempaa periaatetta mukaillen tästäkin voidaan tehdä
funktio
, joka laskee siis ympyrän säteen, kun sille annetaan argumenttina
ympyrän piiri. Lisätään funktio muiden joukkoon, jolloin meillä on jotain tämän näköistä:import math
def laske_ala(sade):
return 4 * math.pi * sade ** 2
def laske_tilavuus(sade):
return 4 / 3 * math.pi * sade ** 3
def laske_sade(piiri):
return piiri / (math.pi * 2)
Tässä vaiheessa kaikki osaongelmat on ratkaistu, ja kukin on nätisti omassa funktiossaan. Jäljelle jää ainoastaan kuvata, miten varsinaisen pääongelman ratkaisu etenee. Tätä varten voidaan tehdä oma funktionsa laske_pallon_ominaisuudet:
def laske_pallon_ominaisuudet(piiri):
sade = laske_sade(piiri)
ala = laske_ala(sade)
tilavuus = laske_tilavuus(sade)
return ala, tilavuus
Funktio
saa pallosta mitatun piirin parametrina
, ja tämän jälkeen edetään varsin järjestelmällisesti laskemalla ensin pallon säde sitä varten tehdyllä funktiolla, ja sitten pallon muut ominaisuudet käyttämällä laskettua sädettä. Kiinnitä huomiota erityisesti siihen, kuinka hyvin koodi kertoo prosessin kulun kun se koostuu pelkistä hyvin nimettyjen funktioiden kutsumisista. Jäljelle jää enää tulosten pyöristäminen ja näyttäminen. Pyöristys on hyvä tehdä vasta tulostaessa, koska arvoja
joilla mahdollisesti vielä voitaisiin laskea jotain ei koskaan pitäisi pyöristää. Se on siis hyvä tehdä pääohjelmassa
, jossa tätä funktiota kutsutaan. Piiri saadaan materiaalin lopussa esitetyllä tavalla kysyä käyttäjältä liukuluku:mitattu_piiri = float(input("Anna pallon ympärysmitta: "))
laskettu_ala, laskettu_tilavuus = laske_pallon_ominaisuudet(mitattu_piiri)
print("Tilavuus:", round(laskettu_tilavuus, 4))
print("Pinta-ala:", round(laskettu_ala, 4))
Kun kaikki osat yhdistetään, saadaan tältä näyttävä kooditiedosto:
Tiedostojen nimeämisestä¶
Python-kooditiedosto merkitään .py-päätteellä. Muuten tiedostojen nimeämiseen pätee sama kuin muuttujien: vain kirjaimia ja numeroita ja alaviivoja. Muunlaisia nimiä ei voi ottaa käyttöön
importilla
, mikä on erityisen ongelmallista tarkistamisen kautta koska koodisi otetaan juuri importilla käyttöön tarkistimessa. Tällä kurssilla on myös huomioitavaa, että Lovelace-palvelin ei osaa käsitellä tiedostoja, joiden nimessä on skandinaavisia kirjaimia. Nämä eivät siis ole hyviä tiedoston nimiä ainakaan tällä kurssilla: ensimmäinen harjoitus.py jännää.py t1.5.py aasi-svengaa++.py
Suotavampaa on käyttää alaviivoja sanojen erotteluun. Skandinaaviset kirjaimet korvataan a:lla ja o:lla. Korjattuna em. nimet voisivat olla:
ensimmainen_harjoitus.py jannaa.py t1_5.py aasi_svengaa.py
Tarkistimista¶
Tällä kurssilla kaikki ohjelmointitehtävät tarkastetaan erityisillä tarkistusohjelmilla. Tyypillinen tehtävänanto koostuu yhden tai useamman
funktion
määrittelystä sekä pääohjelmasta
joka käyttää niitä. Tärkeää on ymmärtää, että nämä testataan erikseen. Tämä on erityisen tärkeä ymmärtää funktioiden suhteen, koska vääränlaiset funktiot eivät mene tarkistimista läpi. Funktion on aina tehtävä täsmälleen kuten tehtävänannossa pyydetään. Lisäksi niiden on näytettävä muun ohjelman näkökulmasta juuri sellaisilta kuin tehtävänannossa kuvataan. Tämä siis tarkoittaa, että funktiolle on annettava juuri se nimi joka tehtävänannossa vaaditaan ja sillä on oltava tasan ne parametrit
, jotka vaaditaan ja niiden on oltava vaaditussa järjestyksessä. Lopulta funktion on myös palautettava
juuri sellainen arvo
kuin on määritelty. Jos funktio on väärin nimetty, testausohjelma ei yksinkertaisesti löydä sitä. Jos taas funktiolla on väärä määrä parametreja, testausohjelma ei pysty kutsumaan sitä testiarvoilla. Jos funktion parametrit ovat väärässä järjestyksessä, se toimii väärin testiarvoilla. Esimerkkinä tämän harjoituksen neljäs tehtävä jossa pyydetään laskemaan ympyräsektorin pinta-ala käyttämällä kahta parametria. Jos parametrit ovat funktion määrittelyssä toisin päin kuin tehtävänannossa, funktio tulee antamaan vääriä tuloksia, koska se käyttää säteenä annettua kulmaa ja päinvastoin. Tyypillisesti funktiota testaan vertaamalla sen palauttamaa arvoa tai arvoja referenssifunktion tulokseen. Mikäli opiskelijan funktio palauttaa vääräntyyppisiä arvoja, nämä vertailut epäonnistuvat ja ohjelma ei mene tarkistimesta läpi.
Tyypillisesti testit on laadittu siten, että ohjelmaa testataan sekä satunnaisilla arvoilla että rajatapauksilla (esim. virheelliset syötteet, nolla, todella suuret tai todella pienet luvut jne.) Usein juuri kutakin opiskelijan
funktiota
kutsutaan eri argumenteilla
toistuvasti tarkistuksen aikana (yleensä 10 kertaa). Tästä johtuen on oltava tarkkana sen kanssa, että funktio todella toimii kuten funktion kuuluu eikä ole riippuvainen mistään sellaisista arvoista
, joita sille ei anneta parametreina
. Erityisesti tämän viikon päätehtävässä on syytä olla tarkkana tämän kanssa. Tehtävässä pyydetään funktiota joka laskee kuvion pinta-alan kun sille annetaan x:n arvo. Kaikki laskut on tehtävä funktion sisällä! Mikäli osa laskuista tehdään pääohjelmassa
, funktio ei tule toimimaan oikein kun se irrotetaan pääohjelmasta.Tarkistimet pyrkivät aina kertomaan mitä ne ovat tekemässä jotta opiskelija pystyy selvittämään mistä virhe johtui. Tyypillisesti ne antavat tietoa siitä mitä arvoja ne koittivat opiskelijan ohjelmalle/funktiolle antaa, mitä koodi palautti sekä mitä sen olisi pitänyt palauttaa. Joissain tapauksissa palautettuja
arvoja
saatetaan myös verrata yleisiin virheellisiin toteutuksiin jolloin voidaan antaa lisäinformaatiota siitä mikä todennäköisimmin meni pieleen. Tarkistimet eivät kuitenkaan ole hyviä ratkomaan opiskelijan koodissa olevia puhtaita ohjelmointivirheitä joiden takia koodia ei edes voi suorittaa. Varmista siis aina ensin että koodisi ylipäätään toimii, koska viallisten ohjelmien ajaminen omalla koneella komentorivillä
kertoo tyypillisesti tarkemmin mikä on vialla. Tarkistimissa on myös poikkeusten
käsittelyä siten, että se saattaa luulla opiskelijan ohjelmointivirheestä johtunutta poikkeusta joksikin muuksi, koska se olettaa saavansa tarkistettavaksi ohjelman joka toimii. Oman koodin testaus tulkissa¶
Koska tarkistimet testaavat nimenomaan
funktioita
, ongelmien ratkominen on usein helpompaa jos pystyt itsekin kutsumaan koodisi funktioita eri arvoilla. Tämän voi tietenkin aina tehdä kopioimalla funktion määrittelyn tulkkiin, mutta kätevämpi tapa pitkällä aikavälillä on opetella oman koodin käyttöönotto import
-lauseella tulkissa
. Otetaan esimerkkinä yllä luodun pallokoodin funktioiden käyttö. Ladataan siis pallokoodi johonkin sopivaan kansioon nimellä pallo.py
. Tässä vaiheessa ohjelmoinnin opettelua on helpointa olla samassa kansiossa kuin kooditiedosto
joka halutaan suorittaa. Siirrytään siis sinne (ks. ohje tarvittaessa) ja käynnistetään tulkki komennolla ipython
.Koodista voi ottaa funktioita käyttöön joko tavallisella import-lauseella tai from-import-lauseella. Riippumatta siitä miten koodin ottaa käyttöön, sen sisältö suoritetaan. Tästä johtuen tulkkiin tulee välittömästi
pääohjelmassa
olevat kyselyt ja tulostukset. Myöhemmin opimme välttämään tämän, mutta toistaiseksi riittää, että siihen syötetään jotain (niin tarkistinkin tekee). Tämän jälkeen funktioita voidaan kutsua suoraan tulkista eri argumenteilla, alla olevan kuvan mukaisesti:Laatutarkistuksesta¶
Tarkistimiin on myös liitetty koodin laatutarkistus johon käytetään Pylint-nimistä työkalua. Pisteytys pohjautuu Pythonin PEP8-tyylistandardiin. Muutamia ilmoituksia on poistettu käytöstä, mutta muutoin pisteytykseen ei ole koskettu. Koodin laatutarkistusta käytetään kurssin pisteytyksessä siten, että tehtävästä saa lisäpisteen jos toimivan ohjelman lisäksi tyylitarkistus antaa vähintään arvosanan 9. Huomautuksiin huomion kiinnittäminen myös yleisesti auttaa kirjoittamaan koodia, joka ei ole pelkästään miellyttävämpää lukea vaan myös vähemmän virheherkkää. Virheitä on usein myös mukavampi korjata koodista, joka on saanut Pylintistä korkeat pisteet.
Lämmittelytehtävä¶
Tehtävät¶
Lopputyöpohdinta¶
Miten nämä tehtävät liittyivät kurssin lopputyön tekemiseen? Tietenkin ilman perusteita on mahdoton tehdä yhtään mitään. Sen lisäksi kuitenkin opittiin myös lopputyön suunnitteluun liittyvä ensisijaisen tärkeä taito: ohjelman jakaminen funktioihin. Pelkästään se, että miettii minkä nimisiä funktioita ohjelmaan voisi tulla, saattaa olla yllättävän pitkä askel kohti ohjelman valmistumista. Funktiojaon ei tarvi olla mitenkään lopullinen - kaikesta huolimatta se on kuitenkin ensimmäinen, konkreettinen esitys siitä, mitä koodi voisi sisältää, tai millaisia asioita siinä voisi tapahtua.
Anna palautetta
Kommentteja harjoituksista?