Ohjelmoinnin termejä
Emme vielä kurssin ensimmäisessä osassa kiinnittäneet kovin tarkasti huomiota ohjelmoinnin terminologiaan. Nyt on hyvä hetki tutustua joihinkin käsitteisiin.
Lause
Lause (engl. statement) tarkoittaa ohjelman osaa, joka suorittaa jonkin toiminnon. Usein lause viittaa yksittäiseen komentoon.
Esimerkiksi print("Moi!")
on lause, joka tulostaa rivin tekstiä,
ja luku = 2
on lause, joka asettaa muuttujalle arvon.
Lause voi olla myös monimutkaisempi, ja sen sisällä voi olla muita lauseita. Esimerkiksi seuraava ehtolause muodostuu kolmesta rivistä:
if nimi == "Anna":
print("Moi!")
luku = 2
Tässä tapauksessa ehtolauseen sisällä on kaksi lausetta.
Lohko
Lohko (engl. block) on joukko peräkkäin sijoitettuja lauseita, jotka ovat samalla tasolla ohjelman rakenteessa. Esimerkiksi ehtolauseessa lohkossa ovat lauseet, jotka suoritetaan ehdon ollessa tosi.
if ika > 17:
# ehtolauseessa oleva lohko alkaa
print("Olet täysi-ikäinen!")
ika = ika + 1
print("nyt vuoden vanhempi...")
# lohko loppuu
print("tämä on eri lohkossa")
Pythonissa lohko ilmaistaan sisentämällä lohkon koodi eli lauseet samalle tasolle.
Kannattaa huomata, että Python-ohjelman "päälohkon" on oltava sisennetty tiedoston vasempaan reunaan:
# tämä ohjelma ei toimi sillä koodia ei ole sisennetty vasempaan reunaan
print("hei maailma")
print("huono ohjelma...")
Lauseke
Lauseke (engl. expression) on koodin osa, jolla on jokin tyyppi. Ohjelman suorituksen aikana lauseke saa arvon, jota voidaan käyttää ohjelmassa.
Tarkastellaan muutamaa esimerkkiä lausekkeista:
Lauseke | Arvo | Tyyppi | Tyyppi Pythonissa |
---|---|---|---|
2 + 4 + 3 | 9 | kokonaisluku | int |
"abc" + "de" | "abcde" | merkkijono | str |
11 / 2 | 5.5 | liukuluku | float |
2 * 5 > 9 | True | totuusarvo | bool |
Koska lausekkeella on arvo, voi sen sijoittaa muuttujaan:
# muuttuja x saa arvoksi lausekkeen 1 + 2 arvon
x = 1 + 2
Yksinkertaisesta lausekkeesta saa muodostettua monimutkaisempia lausekkeita esim. laskuoperaattorien avulla:
# muuttuja y saa arvoksi lausekkeen '3 kertaa x plus x toiseen' arvon
y = 3 * x + x**2
Funktio
Funktio (engl. function) suorittaa jonkin toiminnon. Funktiolla voi olla yksi tai useampi parametri (engl. parameter), jotka ilmaisevat, mitä funktion tulee tehdä tarkalleen.
Funktio suoritetaan, kun sitä kutsutaan eli koodissa on funktion nimi ja funktiolle annettavat parametrit suluissa. Esimerkiksi seuraava koodi kutsuu print
-funktiota parametrilla "tämä on parametri"
:
print("tämä on parametri")
Myös käyttäjältä syötteitä lukeva input
on funktio. Parametrina funktio saa käyttäjälle näytettävän viestin:
nimi = input("Kerro nimesi: ")
Tässä tapauksessa funktio palauttaa arvon, mikä tarkoittaa, että funktion kutsukohtaan ilmestyy arvo funktion suorituksen jälkeen. Funktion input
palauttama arvo on käyttäjän syöttämä teksti merkkijonona. Funktion palauttama arvo sijoitetaan usein muuttujan arvoksi, jotta arvoa voidaan hyödyntää ohjelmassa.
Tyyppi
Tyyppi (engl. type) tarkoittaa, millainen jokin koodissa esiintyvä arvo on. Esimerkiksi seuraavassa koodissa muuttujan nimi
tyyppi on merkkijono ja muuttujan tulos
tyyppi on kokonaisluku:
nimi = "Anna"
tulos = 100
Funktio type
kertoo annetun lausekkeen tyypin. Esimerkiksi:
print(type("Anna"))
print(type(100))
<class 'str'> <class 'int'>
Syntaksi
Syntaksi (engl. syntax) määrittää, miten ohjelman koodi tulee kirjoittaa. Jokaisella ohjelmointikielellä on omanlainen syntaksinsa.
Esimerkiksi Python-kielen syntaksiin kuuluu, että if
-lauseen aloitusrivin lopussa on kaksoispiste ja ehtoon kuuluva koodi on sisennetty:
if nimi == "Anna":
print("Moi!")
Jos ohjelmointikielen syntaksia ei noudateta, seurauksena on virheilmoitus:
if nimi == "Anna"
print("Moi!")
File "testi.py", line 1 if nimi == "Anna" ^ SyntaxError: invalid syntax
Debuggaaminen
Kun ohjelman syntaksi on kunnossa mutta ohjelma ei toimi halutulla tavalla, ohjelmassa on bugi.
Bugit ilmenevät eri tavoin. Jotkin bugit aiheuttavat suoritusaikaisen virheen. Esim. ohjelma
x = 10
y = 0
tulos = x / y
print(f"{x} jaettuna {y} on {tulos}")
aiheuttaa seuraavan virheen:
ZeroDivisionError: integer division or modulo by zero on line 3
Ongelma on siis siinä, että nollalla jakaminen ei ole sallittua ja se keskeyttää ohjelman suorituksen.
Suoritusaikaiseen virheeseen johtavat bugit ovat usein helpohkoja korjata, sillä bugin aiheuttama rivi selviää virheilmoituksesta. Toki bugin varsinainen syy on usein muualla kuin virheilmoituksen aiheuttaneessa rivissä.
Joskus bugi taas ilmenee siten, että koodin tuottama tulos on virheellinen. Tälläisten bugien havaitseminen ja niiden syyn paikallistaminen voi olla haastavaa. Kurssin tehtävissä testit paljastavat usein juuri tämän kategorian bugeja. Ennen kuin ongelma päästään korjaamaan, on bugi paikallistettava.
Koodarijargonissa bugien syiden selvittämistä kutsutaan debuggaamiseksi. Debuggaaminen on äärimmäisen keskeinen taito, ja ammatikseen ohjelmoivat käyttävät usein enemmän aikaa debuggaamiseen kuin varsinaiseen ohjelmointiin.
Yksinkertainen mutta tehokas debuggauskeino on lisätä ohjelmaan debug-tulostuksia eli print
-komentoja, joiden avulla varmistetaan, että koodissa tapahtuu ohjelmoijan olettamia asioita.
Seuraavassa on ratkaisuyritys yhteen edellisen osan tehtävään:
tuntipalkka = float(input("Tuntipalkka: "))
tunnit = int(input("Työtunnit: "))
paiva = input("Viikonpäivä: ")
palkka = tuntipalkka * tunnit
if paiva == "sunnnuntai":
palkka * 2
print(f"Palkka {palkka} euroa")
Ohjelma ei näytä toimivan oikein ja testien suoritus kertoo seuraavaa:
FAIL: PalkkaTest: test_sunnuntai_1 Syötteellä 23.0, 12, sunnuntai oikeaa palkkaa 552.0 ei löydy tulosteestasi Palkka 276.0 euroa
Ensimmäinen askel debuggaamisessa on useimmiten kokeilla ohjelmaa ongelmallisella syötteellä. Kokeilu varmistaa, että tulos ei ole haluttu:
Palkka 276.0 euroa
Debugattaessa ohjelman toimintaa kokeillaan usein. Voikin olla hyödyllisä "kovakoodata" ongelman aiheuttavat syötteet suoraan koodiin sen sijaan, että ne kysyttäisiin joka kerta käyttäjältä. Tämä onnistuu esimerkiksi muuttamalla koodia tilapäisesti seuraavalla tavalla:
# tuntipalkka = float(input("Tuntipalkka: "))
# tunnit = int(input("Työtunnit: "))
# paiva = input("Viikonpäivä: ")
tuntipalkka = 23.0
tunnit = 12
paiva = "sunnuntai"
palkka = tuntipalkka * tunnit
if paiva == "sunnnuntai":
palkka * 2
print(f"Palkka {palkka} euroa")
Seuraava askel on lisäillä koodiin debug-tulostuksia. Koska nimenomaan sunnuntain palkka lasketaan väärin, laitetaan sen hoitavaan osaan tulostukset korotusta ennen ja sen jälkeen:
# ...
palkka = tuntipalkka * tunnit
if paiva == "sunnnuntai":
print("palkka alussa:", palkka)
palkka * 2
print("palkka kasvatuksen jälkeen:", palkka)
print(f"Palkka {palkka} euroa")
Kun ohjelma nyt suoritetaan, ei debug-tulostuksia jostain syystä näy ollenkaan. Vaikuttaa siltä, että ohjelman suoritus ei edes mene if-haaraan. Komennon ehdossa täytyy siis olla jokin ongelma. Ehdon arvokin voidaan tulostaa koodista:
# ...
palkka = tuntipalkka * tunnit
print("ehto:", paiva=="sunnnuntai")
if paiva == "sunnnuntai":
print("palkka alussa:", palkka)
palkka * 2
print("palkka kasvatuksen jälkeen:", palkka)
print(f"Palkka {palkka} euroa")
Ja tosiaan kun koodi suoritetaan, ehdon arvo on False
eli koodi hyppää if-lohkon ohi:
ehto: False Palkka 276.0 euroa
Vian täytyy siis olla if-komennon ehdossa, ja kun sitä katsotaan tarkemmin, huomataan, että sunnuntai on vahingossa kirjoitettu väärin. Korjataan typo:
# ...
palkka = tuntipalkka * tunnit
print("ehto:", paiva=="sunnuntai")
if paiva == "sunnuntai":
print("palkka alussa:", palkka)
palkka * 2
print("palkka kasvatuksen jälkeen:", palkka)
print(f"Palkka {palkka} euroa")
Koodin suoritus aiheuttaa nyt seuraavan tulostuksen:
ehto: True palkka alussa: 276.0 palkka kasvatuksen jälkeen: 276.0 Palkka 276.0 euroa
Koska tuntipalkka = 23.0
ja tunnit = 12
, vaikuttaa muuttujassa palkka
olevan oikea arvo aluksi, mutta kasvatuskomento ei kuitenkaan kasvata muuttujan arvoa. Komento on siis mitä ilmeisemmin virheellinen. Ja toden totta, komento
palkka * 2
ainoastaan laskee tuplapalkan mutta ei tee tulokselle mitään. Korjataan komento muotoon, joka tallentaa korotetun palkan muuttujaan palkka
:
palkka *= 2
Kun ohjelma suoritetaan nyt, huomataan että lopputuloskin on oikea:
ehto: True palkka alussa: 276.0 palkka kasvatuksen jälkeen: 552.0 Palkka 552.0 euroa
Kun ohjelma on kunnossa, tulee debuggaustulosteet ja muu debuggauksen takia kirjoitettu ylimääräinen koodi poistaa.
Esimerkki oli yksinkertainen ja näin lyhyessä ohjelmassa oleva bugi selviäisi varmasti myös koodia lukemalla. Monesti kuitenkin debug-tulostuksilla pääsee huomattavasti nopeammin jyvälle siitä, missä vika piilee. Tulostamalla voidaan usein varmistua siitä, mitkä osat ohjelmasta toimivat "varmuudella" oikein, ja bugien jäljitys voidaan nopeasti saada kohdistettua niihin koodiriveihin, joissa ongelma todennäköisesti piileskelee.
Debuggaukseen on olemassa muitakin keinoja kuin debug-tulostusten tekeminen. Palaamme asiaan myöhemmin kurssilla. Sinun kannattaa tästä lähtien kurssilla käyttää debug-tulostamista virheiden etsimiseen. Ohjelmoinnin ammattilaiset eivät selviä työstään ilman debug-tulostuksia, joten on vaikea kuvitella, että aloittelijoidenkin ei kannattaisi laajentaa työkalupakkiaan tältä osin.
Log in to view the quiz