Lista
Tarkastellaan seuraavaa ongelmaa: Meteorologi haluaa ohjelman, johon hän voi syöttää kuukauden jokaisen päivän maksimilämpötilan. Kun lämpötilat on syötetty, ohjelman pitää tulostaa lämpötilojen keskiarvo ja syötetyt lämpötilat alkuperäisessä järjestyksessä. Oletetaan, että jokaisessa kuukaudessa on 30 päivää.
Jos ohjelman pitäisi tulostaa vain lämpötilojen keskiarvo, olisi helppo kerätä lämpötilojen summa yhteen muuttujaan ja laskea lopuksi keskiarvo tämän muuttujan avulla. Nyt kuitenkin halutaan tulostaa myös syötetyt lämpötilat. Tämän vuoksi jokaisen päivän lämpötila pitää tallentaa erikseen. Tähän asti opituilla välineillä tarvitsisimme kolmekymmentä eri muuttujaa lämpötilojen tallentamista varten. Meidän pitäisi myös kirjoittaa kolmekymmentä käskyä lämpötilojen lukemiseen ja toiset kolmekymmentä käskyä lämpötilojen tulostamiseen. Näin ohjelmasta tulisi tarpeettoman pitkä. Lisäksi sen muuttaminen toimimaan jollain muulla lämpötilamäärällä olisi hankalaa. Jos esimerkiksi haluaisimme ohjelman käsittelevän samalla tavalla koko vuoden lämpötilat, pitäisi ohjelmaan lisätä 335 uutta muuttujaa sekä saman verran uusia luku- ja tulostuskäskyjä.
Tällainen ongelma voidaan ratkaista selvästi helpommin käyttämällä
listaa. Lista on tietorakenne, johon voi tallentaa useita arvoja.
Näihin arvoihin pääsee käsiksi indeksin avulla. Jos on esimerkiksi lista
lampotilat
, niin listan viidenteen alkioon pääsee käsiksi
kirjoittamalla lampotilat[4]
. (Hakasuluissa oleva indeksi on 4 eikä
5 siksi, että listan ensimmäisen alkion indeksi on aina 0.)
Uusi lista luodaan esimerkiksi kirjoittamalla
lampotilat = []
Sijoituskäskyn oikealla puolella luodaan tyhjä lista ja sijoituskäsky
yhdistää muuttujan lampotilat
luotuun listaan.
Tämän jälkeen listan loppuun voi lisätä uuden arvon kirjoittamalla
lampotilat.append(arvo)
Uusi arvo lisätään aina vanhan listan loppuun niin, että listan pituus kasvaa yhdellä. Esimerkiksi ohjelma
def main():
lampotilat = []
lampotilat.append(15.0)
lampotilat.append(22.5)
lampotilat.append(-12.9)
print(lampotilat)
main()
tulostaa
[15.0, 22.5, -12.9]
Tässä siis listaan lisättiin lukuja metodin append
avulla. Kuten
funktio, metodi on ohjelmakoodin osa, jolle on annettu oma nimi. Metodia
kuitenkin kutsutaan eri tavalla kuin funktiota: lista, johon lisäys
tehdään, ei olekaan metodin parametrina vaan se on kirjoitettu kutsuun
ennen metodin nimeä ja erotettu metodin nimestä pisteellä. Metodien
määrittely ja niiden kutsutapa liittyy olio-ohjelmoinnin piirteisiin,
joihin tutustutaan tarkemmin kierroksella 9. Listojen ja myöhemmin
merkkijonojen yhteydessä kuitenkin käytämme joitakin Pythonin valmiita
metodeita.
Jos listassa on vähintään i+1
alkiota, pystyy indeksille i
sijoittamaan uuden arvon kirjoittamalla
listamuuttuja[i] = arvo
tällöin indeksillä i
oleva vanha arvo häviää ja ja se korvautuu
sijoituskäskyn oikealla puolella olevalla arvolla.
Jos esimerkiksi edellisen ohjelman main
-funktion loppuun lisätään
rivi
lampotilat[1] = 32.0
on lista lampotilat
rivin suorittamisen jälkeen
[15.0, 32.0, -12.9]
Seuraava animaatio vielä näyttää, mitä edellä annettujen käskyjen suorituksessa tapahtuu:
Tällaisella sijoituskäskyllä ei voi kuitenkaan kasvattaa listan kokoa
eli hakasuluissa olevan indeksi saa olla
korkeintaan listan alkioiden määrä vähennettynä yhdellä (esimerkiksi 2 silloin, kun listassa on 3 alkioita).
Jos edellä esitetyn ohjelman
main
-funktion loppuun lisää käskyn
lampotilat[3] = 18.0
johtaa se ohjelman kaatumiseen ja seuraavaan virheilmoitukseen:
Traceback (most recent call last):
File "lampotilat1.py", line 10, in <module>
main()
File "lampotilat1.py", line 7, in main
lampotilat[3] = 18.0
IndexError: list assignment index out of range
Tässä viimeinen rivi “list assignment index out of range” kertookin aika suoraan, millaisesta virheestä on ollut kysymys eli että sijoituksessa listaan käytetty indeksi on ollut sallittujen rajojen ulkopuolella.
Listan alkioiden arvoja voidaan käyttää indeksoinnin avulla samalla tavalla kuin minkä tahansa muiden muuttujien arvoja, esimerkiksi
summa = lampotilat[1] + lampotilat[2]
laskee lampotilat
-listassa indekseillä 1 ja 2 olevien alkioiden
arvot yhteen ja sijoittaa näin saadun tuloksen muuttujan summa
arvoksi. Tässäkin käytettyjen indeksien pitää olla listan indeksialueen
sisällä.
Palataan sitten alkuperäiseen esimerkkiin: ohjelmaan, joka kirjaa kuukauden eri päivien lämpötilat ja sen jälkeen tulostaa nämä lämpötilat uudelleen sekä niiden keskiarvon.
Ohjelmassa pitää siis ensinnäkin luoda lista lämpötiloja varten. Tämä voidaan tehdä käskyllä
lampotilat = []
Sitten tarvitaan toistokäsky, joka pyytää käyttäjältä eri päivien
lämpötilat ja lisää ne lampotilat
-listaan. Toistokäsky tarvitsee
tiedon siitä, montako lämpötilaa käyttäjältä luetaan, jotta se osaisi
lopettaa lämpötilojen pyytämisen oikean määrän luettuaan.
(Vaihtoehtoisesti ohjelma voidaan rakentaa niin, että käyttäjä ilmoittaa
jollain sovitulla arvolla, että kaikki halutut lämpötilat on luettu).
Otetaan tätä lämpötilojen määrää varten käyttöön vakio LKM
, jolle
siis annetaan arvo 30. Lisäksi while
-käskyssä tarvitaan laskuri,
joka pitää kirjaa siitä, kuinka monta lämpötilaa on jo luettu.
Seuraavassa ohjelmassa käytetään siihen tarkoitukseen muuttujaa i
.
Lämpötilat lukeva ja listaan lisäävä while
-käsky voi siis olla sitä
edeltävine alustuksineen seuraava:
LKM = 30
i = 0
while i < LKM:
lampo = float(input("Seuraava lampotila: "))
lampotilat.append(lampo)
i += 1
Tarkastellaan sitten sitä osaa ohjelmasta, joka laskee lämpötilojen
keskiarvon. Kirjoitetaan toistokäsky, joka laskee listassa
lampotilat
olevien lämpötilojen summan. (Summan laskemisen voi
käytännössä yhdistää samaan toistokäskyyn joko lämpötilojen lukemisen
tai tulostamisen kanssa, mutta tässä se on esitetty aluksi selvyyden
vuoksi erikseen.) Tarvitsemme muuttujan, johon summaa kerätään. Tämä
muuttuja alustetaan aluksi nollaksi. Sen jälkeen käydään toistokäskyn
avulla koko lista lampotilat
läpi ja jokaisella kierroksella
lisätään yksi alkio aikaisemmin laskettuun summaan:
summa = 0.0
i = 0
while i < LKM:
summa += lampotilat[i]
i += 1
Tässä siis i
:n arvo vaihtelee eri kierroksilla niin, että
ensimmäisellä kierroksella muuttujaan summa
lisätään alkio
lampotilat[0]
, toisella kierroksella lampotilat[1]
ja niin
edelleen, kunnes viimeisellä kierroksella summaan lisätään
lampotilat[LKM - 1]
. Listan viimeisen alkion indeksi on LKM - 1
,
vaikka listassa on LKM
alkiota, koska ensimmäisen alkion indeksi on
0.
Katsotaan vielä alla olevan animaation avulla, mitä edellä annettujen
käskyjen suorituksessa tapahtuu. Vakion LKM
arvoksi on vaihdettu 3,
jotta toistokäskyjen kierrosten määrä pysyisi kohtuullisena. Animaation
koodin loppuun on myös lisätty summan tulostus.
Listan läpikäynti voidaan kirjoittaa myös for
-käskyn avulla. Tämä
yleensä onkin kätevämpi tapa silloin, kun listaan ei tarvitse enää
lisätä uusia alkioita tai muuttaa listassa olevia arvoja, sillä for
-käskyssä ohjelmoijan ei tarvitse
ilmaista listan kokoa eikä pitää huolta indeksimuuttujan
kasvattamisesta. Käskyn yleinen muoto on tällöin
for elementti in lista:
tee jotain listan alkiolle elementti
Tässä muuttuja elementti
saa vuorotellen arvokseen kunkin listan
alkion niin, että ensimmäisellä kierroksella muuttujan elementti
arvo on listan ensimmäinen alkio, toisella kierroksella toinen alkio
jne. Listan lampotilat
alkioiden summa voidaan laskea seuraavalla
for
-käskyllä:
summa = 0.0
for arvo in lampotilat:
summa += arvo
Seuraava animaatio näyttää, miten for
-käskyn suoritus etenee. Animaation
lyhentämiseksi listan sisältö annetaan ohjelman alussa valmiina.
Ohjelmaan on myös lisätty summan tulostus.
Seuraavaksi koko ohjelma, joka pyytää lämpötilat, tulostaa ne ja laskee ja tulostaa niiden keskiarvon. Lämpötilojen tulostus ja niiden summan laskeminen on yhdistetty samaan toistokäskyyn.
def main():
LKM = 30
lampotilat = []
i = 0
print("Anna", LKM, "lampotilaa.")
while i < LKM:
lampo = float(input("Seuraava lampotila: "))
lampotilat.append(lampo)
i += 1
summa = 0.0
print("Annetut lampotilat")
for arvo in lampotilat:
print(arvo)
summa += arvo
keskiarvo = summa / LKM
print(f"Lampotilojen keskiarvo on {keskiarvo:.2f}.")
main()
Seuraavaksi esimerkki ohjelman toiminnasta. Vakion LKM
arvoksi on
vaihdettu 5, jotta esimerkkiajosta ei tulisi turhan pitkä.
Anna 5 lampotilaa.
Seuraava lampotila: 25.0
Seuraava lampotila: 12.0
Seuraava lampotila: -5.0
Seuraava lampotila: -10.0
Seuraava lampotila: 2.0
Annetut lampotilat
25.0
12.0
-5.0
-10.0
2.0
Lampotilojen keskiarvo on 4.80.
Jos käyttäjälle ei haluta tulostaa mitään tekstiä joka kerta, kun
luetaan uusi lämpötila, jätetään input
-käskyyn liittyvät sulut
tyhjiksi. Näin on tehty alla olevassa ohjelman toisessa versiossa.
Monessa harjoitustehtävässä käytetään tätä tapaa.
def main():
LKM = 30
lampotilat = []
i = 0
print("Anna", LKM, "lampotilaa.")
while i < LKM:
lampo = float(input())
lampotilat.append(lampo)
i += 1
summa = 0.0
print("Annetut lampotilat")
for arvo in lampotilat:
print(arvo)
summa += arvo
keskiarvo = summa / LKM
print(f"Lampotilojen keskiarvo on {keskiarvo:.2f}.")
main()
Alla esimerkki tämän ohjelmaversion toiminnasta. Vakion LKM
arvoksi on
jälleen vaihdettu 5.
Anna 5 lampotilaa.
25.0
12.0
-5.0
-10.0
2.0
Lampotilojen keskiarvo on 4.80.
Kun uusi lista luodaan, sen ei tarvitse välttämättä olla tyhjä, vaan listaa luodessa voidaan samalla antaa listaan aluksi kuuluvat alkiot, esimerkiksi
numerolista = [2, 4, 6, 8]
luo listan, jossa on neljä kokonaislukua (2, 4, 6 ja 8 tässä
järjestyksessä) ja liittää muuttujan numerolista
luotuun listaan.
Listan alkioihin päästään käsiksi indeksoinnin avulla, ja listaan
voidaan myös lisätä uusia alkioita append
-metodilla ihan samalla
tavalla kuin tyhjänä luotuun listaan.
Aikaisemmassa lämpötilaesimerkissä kasvatettiin listan kokoa aina uutta lämpötilaa lisättäessä. Toinen vaihtoehto on luoda heti aluksi 30-alkioinen lista ja lisätä sijoituskäskyllä alkiot siihen jo olemassaoleville paikoille. Ei ole tosin mahdollista luoda listaa, jossa olisi 30 tyhjää paikkaa, mutta sen sijaan on mahdollista luoda 30-paikkainen lista, jonka jokaisessa alkiossa on aluksi arvo 0.0 seuraavasti:
lampotilat = [0.0] * 30
tai vakiota LKM
käyttämällä
LKM = 30
lampotilat = [0.0] * LKM
Tällöin lämpötiloja luettaessa niitä ei enää lisätä listaan
append
-käskyllä, vaan lämpötilojen lisääminen voidaan tehdä
while
-käskyä käytettäessä indeksoimalla seuraavasti:
i = 0
while i < LKM:
lampo = float(input("Seuraava lampotila: "))
lampotilat[i] = lampo
i += 1
Jos lämpötilat lisätään näin luotuun listaan for
-käskyllä,
pitää käskyssä käydä läpi listan indeksejä eikä itse alkioita
esimerkiksi näin:
for i in range(LKM):
lampo = float(input("Seuraava lampotila: "))
lampotilat[i] = lampo