Lisää merkkijonoista ja listoista
Olemme käyttäneet aiemmin []
-syntaksia merkkijonon osajonon erottamiseen:
mjono = "esimerkki"
print(mjono[3:7])
merk
Sama syntaksi toimii myös listoissa, ja voimme erottaa sen avulla listan osan:
lista = [3,4,2,4,6,1,2,4,2]
print(lista[3:7])
[4, 6, 1, 2]
Lisää erottamisesta
Itse asiassa []
-syntaksi toimii hyvin samalla periaatteella kuin range
-funktio, eli voimme antaa sille myös askeleen:
mjono = "esimerkki"
print(mjono[0:7:2])
lista = [1,2,3,4,5,6,7,8]
print(lista[6:2:-1])
eiek [7, 6, 5, 4]
Jos emme anna jotain arvoa, oletuksena koko sisältö valitaan mukaan. Tämän avulla voimme tehdä seuraavan lyhyen ohjelman, joka kääntää merkkijonon:
mjono = input("Kirjoita merkkijono: ")
print(mjono[::-1])
Kirjoita merkkijono: esimerkki ikkremise
Varoitus: globaalin muuttujan käyttö funktion sisällä
Funktioiden sisällä on mahdollista määritellä muuttujia, mutta tämän lisäksi funktio näkee sen ulkopuolella pääohjelmassa määritellyt muuttujat. Tälläisia muuttujia sanotaan globaaleiksi muuttujiksi.
Globaalien muuttujien käyttämistä funktioista käsin ei useimmiten pidetä hyvänä asiana muun muassa siksi, että ne saattavat johtaa ikäviin bugeihin.
Seuraavassa on esimerkki funktiosta, joka käyttää "vahingossa" globaalia muuttujaa:
def tulosta_vaarinpain(nimet: list):
# käytetään vahingossa parametrin sijaan globaalia muuttujaa nimilista
i = len(nimilista) - 1
while i >= 0:
print(nimilista[i])
i -= 1
# globaali muuttuja
nimilista = ["Antti", "Emilia", "Erkki", "Margaret"]
tulosta_vaarinpain(nimilista)
print()
tulosta_vaarinpain(["Tupu", "Hupu", "Lupu"])
Margaret Erkki Emilia Antti
Margaret Erkki Emilia Antti
Vaikka funktiota kutsutaan oikein, se tulostaa aina globaalissa muuttujassa nimilista olevat nimet.
Kaikki funktioita testaava koodi on kirjoitettava erillisen lohkon sisälle, jotta TMC-testit hyväksyisivät koodin. Edellinen esimerkki siis tulisi toteuttaa seuraavasti:
def tulosta_vaarinpain(nimet: list):
# käytetään vahingossa parametrin sijaan globaalia muuttujaa nimilista
i = len(nimilista) - 1
while i>=0:
print(nimilista[i])
i -= 1
# kaikki funktiota testaava koodi tämän lohkon sisälle
if __name__ == "__main__":
# globaali muuttuja
nimilista = ["Antti", "Emilia", "Erkki", "Margaret"]
tulosta_vaarinpain(nimilista)
print()
tulosta_vaarinpain(["Tupu", "Hupu", "Lupu"])
Nyt myös globaalin muuttujan määrittely on siirtynyt if
-lohkoon.
TMC-testit suoritetaan aina siten, että mitään if
-lohkon sisällä olevaa koodia ei suoriteta. Tämän takia funktio ei voi edes teoriassa toimia, sillä se viittaa muuttujaan nimilista
, jota ei testejä suoritettaessa ole lainkaan olemassa.
Merkkijonoa ei voi muuttaa
Merkkijonoilla ja listoilla on paljon yhteistä, ja useimmat operaatiot toimivat samalla tavalla sekä merkkijonoille että listoille. Kuitenkin erona on, että merkkijonoa ei voi muuttaa. Esimerkiksi seuraava koodi ei toimi tarkoitetulla tavalla:
mjono = "esimerkki"
mjono[0] = "a"
Koska merkkijonoa ei voi muuttaa, ohjelman suoritus aiheuttaa virheen:
TypeError: 'str' object does not support item assignment
Samankaltainen virhe seuraa, jos yritetään esimerkiksi järjestää merkkijonoa järjestykseen sort
-metodilla.
Vaikka merkkijonoa ei voi muuttaa, voimme silti sijoittaa merkkijonon paikalle toisen merkkijonon.
Onkin tärkeää huomata ero seuraavien esimerkkien välillä:
lista = [1,2,3]
lista[0] = 10
mjono = "Moi"
mjono = mjono + "!"
Ensimmäisessä esimerkissä listan sisältö muuttuu. Toisessa esimerkissä alkuperäinen merkkijono korvataan toisella merkkijonolla. Alkuperäinen merkkijono jää muistiin, mutta siihen ei enää ole viittausta, joten sitä ei voi enää käyttää ohjelmassa.
Tähän palataan tarkemmin ensi viikolla, kun viittauksia listoihin käsitellään tarkemmin.
Lisää metodeita
Metodin count
avulla voidaan laskea osajonon esiintymien määrä. Metodi toimii samaan tapaan sekä merkkijonon että listan kanssa. Esimerkiksi näin:
mjono = "Vesihiisi sihisi hississä"
print(mjono.count("si"))
lista = [1,2,3,1,4,5,1,6]
print(lista.count(1))
5 3
Huomaa, että metodi count
ei laske päällekkäisiä esiintymiä. Esimerkiksi metodin mukaan merkkijonossa aaaa
esiintyy kaksi kertaa osajono aa
, vaikka oikeastaan esiintymiä olisi kolme, jos päällekkäiset esiintymät sallitaan.
Metodin replace
avulla voidaan muodostaa uusi merkkijono, jossa tietty merkkijono on korvattu toisella merkkijonolla. Esimerkiksi:
mjono = "Moi kaikki"
uusi = mjono.replace("Moi", "Hei")
print(uusi)
Hei kaikki
Metodi korvaa kaikki merkkijonon esiintymät:
lause = "hei heilan löysin minä heinikosta hei"
print(lause.replace("hei", "HEI"))
HEI HEIlan löysin minä HEInikosta HEI
Tyypillinen virhe replace
-metodia käytettäessä on unohtaa, että merkkijonot ovat muuttumattomia:
mjono = "Python on kivaa"
# Korvataan alijono, muttei tallenneta tulosta mihinkään...
mjono.replace("Python", "Java")
print(mjono)
Python on kivaa
Jos vanhaa jonoa ei tarvita, voidaan uusi jono sijoittaa samaan muuttujaan:
mjono = "Python on kivaa"
# Korvataan alijono, tallennetaan tulos samaan muuttujaan
mjono = mjono.replace("Python", "Java")
print(mjono)
Java on kivaa
Laajemman ohjelman tekeminen
Tämän osan huipentaa ensimmäinen hieman laajempi ohjelma, jota tehdessäsi pääset soveltamaan kaikkea tähän asti opeteltua.
Sääntö numero yksi isompaa tai oikeastaan mitä tahansa ohjelmaa tehdessä on se, että ei kannata yrittää ratkaista kaikkia ongelmia yhtä aikaa. Ohjelma kannattaa rakentaa pienistä paloista kuten sopivista apufunktioista, ja kunkin palan toimivuus kannattaa varmistaa ennen kun alkaa rakentaa seuraavaa palaa. Jos näin ei tee, on aika varmaa että edessä on suuri kaaos.
Isompaa ohjelmaa rakentaessa on järkevää testailla ohjelman funktioita aluksi erillään pääohjelmasta. Yksi helppo tapa on tehdä myös pääohjelmasta oma funktio, esimerkiksi nimeltään main
, jonka ohjelman funktioiden ulkopuoleinen osa käynnistää. Esimerkiksi seuraavaa tehtävää voitaisiin ruveta lähestymään näin:
def main():
pisteet = []
# ohjelman koodi tänne
main()
Näin ohjelman apufunktioita on mahdollista testata ilman pääohjelman suorittamista:
# apufunktio, joka laskee arvosanan pisteiden perusteella
def arvosana(pisteet):
# koodia
def main():
pisteet = []
# ohjelman koodi tänne
# kommentoidaan pääohjelma pois
#main()
# testataan apufunktiota
pistemaara = 35
tulos = arvosana(pistemaara)
print(tulos)
Tiedon välittäminen funktiosta toiseen
Jos ohjelma koostuu useista funktioista, nousee esiin kysymys, miten tietoa siirretään funktiosta toiseen.
Seuraavassa on esimerkki ohjelmasta, joka lukee käyttäjältä joukon kokonaislukuarvoja. Sen jälkeen ohjelma tulostaa arvot ja tekee niille vielä "analyysin". Ohjelma on jaettu kolmeen erilliseen funktioon:
def lue_kayttajalta(maara: int):
print(f"Syötä {maara} lukua:")
luvut = []
for i in range(maara):
luku = int(input("Anna luku: "))
luvut.append(luku)
return luvut
def tulosta(luvut: list):
print("Luvut ovat: ")
for luku in luvut:
print(luku)
def analysoi(luvut: list):
keskiarvo = sum(luvut) / len(luvut)
return f"Lukuja yhteensä {len(luvut)}, keskiarvo {keskiarvo}, pienin {min(luvut)} ja suurin {max(luvut)}"
# funktioita käyttävä "pääohjelma"
syotteet = lue_kayttajalta(5)
tulosta(syotteet)
analyysin_tulos = analysoi(syotteet)
print(analyysin_tulos)
Esimerkkisuoritus:
Syötä 5 lukua: Anna luku: 10 Anna luku: 34 Anna luku: -32 Anna luku: 99 Anna luku: -53 Luvut ovat: 10 34 -32 99 -53 Lukuja yhteensä 5, keskiarvo 11.6, pienin -53 ja suurin 99
Perusperiaatteena ohjelmassa on se, että pääohjelma "tallentaa" ohjelman käsittelemän tiedon eli tässä tapauksessa käyttäjän syöttämät luvut muuttujassa syotteet
.
Jos lukuja on tarve käsitellä jossain funktiossa, ne välitetään sinne parametrina. Näin tapahtuu funktioissa tulosta
ja analysoi
. Jos taas funktio tuottaa tietoa, jota muut ohjelman osat tarvitsevat, palauttaa funktio datan return
-komennolla. Näin tekevät käyttäjän syötteen lukeva funktio lue_kayttajalta
sekä analyysin tekevä funktio analysoi
.
Olisi periaatteessa mahdollista, että funktiot käyttäisivät suoraan "pääohjelman" globaalia muuttujaa syotteet
. Se ei kuitenkaan ole järkevää, sillä jos funktiot pystyvät muuttamaan globaalia muuttujaa, voi ohjelmassa alkaa tapahtua jotain hallitsematonta, varsinkin kun funktioiden määrä kasvaa.
Tiedon välitys funktioihin ja niistä ulos on siis järkevintä hoitaa parametrien ja paluuarvojen avulla.
Jos haluaisimme tehdä edellisen esimerkin ohjelman siten, että sen pääohjelma eriytettäisiin omaan funktioon main
, siirrettäisiin ohjelman käsittelemä data pääohjelmaa edustavan funktion sisäiseksi muuttujaksi:
# pääohjelmaa edustava funktio
def main():
syotteet = lue_kayttajalta(5)
tulosta(syotteet)
analyysin_tulos = analysoi(syotteet)
print(analyysin_tulos)
# ohjelman käynnistys
main()
Log in to view the quiz
Vastaa lopuksi osion loppukyselyyn:
Log in to view the quiz