Lista funktion parametrina ja funktion palauttamana arvona

Edellä esitetty ohjelma toimii täysin oikein, mutta sitä voi vielä selkeyttää jakamalla pääohjelmassa esiintyviä toimintoja sopiviin funktioihin.

Lämpötilalistan luominen ja arvojen lukeminen siihen on yksi kokonaisuus, joka sopii hyvin omaksi funktiokseen kysy_lampotilat. Tieto perustetusta listasta ja siihen tallennetuista lämpötiloista pitää kuitenkin saada jotenkin pääohjelman käyttöön. Tähän on kaksi keinoa. Toinen on luoda lista pääohjelmassa ja antaa lista sitten parametrina siihen lämpötilat tallentavalle funktiolle. Parametrina annettuun listaan tehdyt muutokset ja lisäykset näkyvät myös pääohjelmassa. Tästä kerrotaan tarkemmin kierroksen 6 luvussa Arvot ja viittaukset.

Toinen tapa on luoda lista funktiossa kysy_lampotilat ja palauttaa tämä lista funktion lopussa. Aivan samalla tavalla kuin funktion palauttama lukuarvo voidaan ottaa käyttöön pääohjelmassa, myös funktion palauttama lista voidaan ottaa käyttöön pääohjelmassa. Seuraavassa esimerkissä on käytetty tätä tapaa:

def kysy_lampotilat():
    LKM = 30
    lampotilat = [0.0] * LKM
    i = 0
    print("Anna", LKM, "lampotilaa")
    while i < LKM:
        lampo = float(input("Seuraava lampotila: "))
        lampotilat[i] = lampo
        i += 1
    return lampotilat

Pääohjelmassa funktiota voidaan kutsua esimerkiksi seuraavasti:

lampolista = kysy_lampotilat()

Tällöin muuttuja lampolista saa arvokseen funktion kysy_lampotilat palauttaman listan, joka sisältää käyttäjän antamat lämpötilat.

Vastaavasti voidaan kirjoittaa oma funktio listassa olevien lämpötilojen tulostamiseen:

def tulosta_lampotilat(lammot):
    print("Annetut lampotilat")
    for arvo in lammot:
        print(arvo)

Funktio saa parametrina listan, joka sisältää aikaisemmin luetut lämpötilat. Funktiossa oleva for-käsky käy tämän listan läpi ja tulostaa jokaisen siinä olevan lämpötilan. Funktiota voidaan kutsua pääohjelmasta esimerkiksi seuraavasti:

tulosta_lampotilat(lampolista)

Jos pääohjelman muuttujaan lampolista on aikaisemmin sijoitettu luetut lämpötilat sisältävä lista, niin funktiota tulosta_lampotilat suoritettaessa parametri lammot tarkoittaa tätä samaa listaa.

Kirjoitetaan myös funktio keskiarvon laskemista varten. Keskiarvon laskemista ei ole tässä sisälletetty lämpötilat tulostavaan funktioon kahdesta syystä: Ensiksi, keskiarvon laskeminen on selvästi lämpötilojen tulostamisesta erillinen toimenpide. Kun kirjoitamme funktion jonkin asian suorittamista varten, on selvempää, että funktio tekee juuri tämän asian eikä sen oheen ole liitetty funktion alkuperäiseen tarkoitukseen liittymättömiä asioita. Toiseksi, lämpötilojen tulostaminen sisältää selkeästi ohjelman käyttöliittymään liittyviä asioita, kun taas keskiarvon laskemiseen ei liity kommunikointia ohjelman käyttäjän kanssa. On järkevää erottaa toisistaan ohjelman sellaiset osat, jotka kommunikoivat käyttäjän kanssa ja sellaiset, jotka suorittavat puhdasta laskentaa. Tällöin ohjelman muuttaminen myöhemmin on helpompaa, jos halutaan muuttaa ohjelman käyttöliittymä toisenlaiseksi (esimerkiksi vaihtaa nyt ohjelmassa käytössä oleva tekstipohjainen käyttöliittymä graafiseen käyttöliittymään).

Keskiarvon laskemisessa tarvitaan listassa olevien lämpötilojen määrä. Funktiossa kysy_lampotilat on tätä varten määritelty vakio LKM, mutta toisessa funktiossa määritelty vakio ei näy toisen funktion sisällä, eikä vakiota voida näin ollen suoraan käyttää funktiossa laske_keskiarvo. Yksi vaihtoehto on siirtää vakiolle LKM arvon antava sijoituskäsky LKM = 30 funktioiden ulkopuolelle. Tällöin vakio on käytettävissä kaikissa samaan tiedostoon kirjoitetuissa funktioissa. Tässä tapauksessa siirto on kuitenkin tarpeeton, koska listassa olevien lämpötilojen määrän saa selville helposti myös ilman vakiota. Pythonissa on listoja varten valmis funktio len, joka palauttaa funktiolle parametrina annetun listan pituuden. Listan lampotilalista pituuden saa siis selville ilmauksella len(lampotilalista).

Aina jakolaskuja suoritettaessa on hyvä tarkistaa se, että jakaja ei ole nolla, koska nollalla jakaminen aiheuttaisi ohjelman kaatumisen. Funktiossa laske_keskiarvo on myös tämä mahdollisuus otettu huomioon. Jos lämpötilojen määrä on 0, funktio palauttaa arvon 0.0. Pääohjelmassa on kutsuttu keskiarvon laskevaa funktiota (lampolista on jälleen annetttu funktiolle parametrina) ja tulostettu funktion palauttama arvo. Seuraavaksi koko ohjelma:

def kysy_lampotilat():
    LKM = 30
    lampotilat = [0.0] * LKM
    i = 0
    print("Anna", LKM, "lampotilaa")
    while i < LKM:
        lampo = float(input("Seuraava lampotila: "))
        lampotilat[i] = lampo
        i += 1
    return lampotilat

def tulosta_lampotilat(lammot):
    print("Annetut lampotilat")
    for arvo in lammot:
        print(arvo)

def laske_keskiarvo(lampotilalista):
    summa = 0.0
    for lampotila in lampotilalista:
        summa += lampotila
    lukumaara = len(lampotilalista)
    if lukumaara > 0:
        keskiarvo = summa / lukumaara
    else:
        keskiarvo = 0.0
    return keskiarvo

def main():
    lampolista = kysy_lampotilat()
    tulosta_lampotilat(lampolista)
    keskiarvo = laske_keskiarvo(lampolista)
    print(f"Lampotilojen keskiarvo on {keskiarvo:.2f}.")

main()

Annetussa ohjelmassa on käytetty useita eri muuttujia ja parametreja, joiden arvona on lämpötilat sisältävä lista. Kunkin funktion sisällä listaan käydään käsiksi oman muuttujan tai parametrin kautta. Asian selventämiseksi esimerkissä on käytetty jokaisessa funktiossa tuolle muuttujalle tai parametrille eri nimeä. Yhdessä funktiossa määritelty nimi ei näy suoraan toisessa funktiossa, mutta tiedon käsiteltävästä listasta voi välittää funktiolta toiselle parametrien ja paluuarvojen välityksellä.

Seuraavassa animaatiossa on vielä esimerkki samantapaisen, mutta vähän lyhyemmän ohjelman suorituksesta. Tarkkaile erityisesti parametrien ja pääohjelman lampolista-muuttujan käyttöä ohjelmassa. Animaation lyhentämiseksi listan alkioiden määrän tarkistus on jätetty pois keskiarvoa laskettaessa.

Käytännön ohjelmoinnissa eri funktioissa käytetään samaa asiaa tarkoittavasta parametrista kuitenkin usein samaa muuttujan ja parametrin nimeä, vaikka Python-ohjelman kannalta kysymys on useasta eri muuttujasta tai parametrista. Käytännössä esimerkiksi lämpötilaohjelma kirjoitettaisiin usein niin, että käytössä olisi lämpötilat sisältävälle listalle vain yksi nimi, lampotilat, jota käytettäisiin sekä parametrina funktioissa kysy_lampotilat, tulosta_lampotilat ja laske_keskiarvo että muuttujana main-funktiossa. Kysymys ei olisi kuitenkaan yhdestä muuttujasta, vaan kolmesta eri parametrista ja yhdestä muuttujasta, joilla kaikilla vain on sama nimi. Tieto käsiteltävästä listasta välittyisi edelleen parametrien ja funktioiden paluuarvojen välityksellä. Tässä monisteessa on kuitenkin pyritty aluksi käyttämään eri parametreille eri nimiä, jotta aloittelijan olisi helpompi nähdä, milloin on kysymys samasta ja milloin eri muuttujasta.

Tarkastellaan seuraavaksi esimerkkiä hieman monimutkaisemmasta listan läpikäynnistä. Haluamme selvittää, kuinka moni listassa olevista lämpötiloista on vähintään yhtä suuri kuin käyttäjän antama raja. (Tällaista toimintoa voidaan käyttää esimerkiksi hellepäivien määrän selvittelyyn.) Kirjoitetaan oma funktio montako_yli_rajan, joka käy sille parametrina annetun listan läpi ja tarkistaa jokaisen listassa olevan lämpötilan kohdalla, onko kyseinen lämpötila suurempi tai yhtäsuuri kuin funktiolle toisena parametrina annettu raja. Tarkistus onnistuu siten, että kirjoitamme toistokäskyn sisään if-käskyn, joka tekee tarvittavan tarkastuksen jokaisella kierroksella. Lisäksi tarvitaan laskuri, joka pitää kirjaa siitä, kuinka monta tarpeeksi suurta lämpötilaa on jo kohdattu. Tämä laskuri alustetaan aluksi nollaksi ja sitä kasvatetaan aina, kun listasta löydetään annettua rajaa suurempi tai yhtäsuuri luku. Kun koko lista on käyty läpi, funktio palauttaa laskurin arvon.

def montako_yli_rajan(lampotilojen_lista, raja):
    ylittavien_maara = 0
    for asteet in lampotilojen_lista:
        if asteet >= raja:
            ylittavien_maara += 1
    return ylittavien_maara

Arvon palauttava return-käsky on kirjoitettava toistokäskyn ulkopuolelle. Jos käsky olisi for-käskyn sisällä, arvo palautettaisiin (ja funktion suoritus lopetettaisiin) jo siinä vaiheessa, kun listan ensimmäinen alkio on tutkittu ja listan loppuosaa ei ole vielä käyty lainkaan läpi.

Funktiota voidaan käyttää esimerkiksi lisäämällä pääohjelmaan seuraavat käskyt:

lamporaja = float(input("Anna raja, jonka ylittavat lampotilat lasketaan: "))
paiva_lkm = montako_yli_rajan(lampolista, lamporaja)
print("Raja ylittyi", paiva_lkm, "paivana.")

Toinen tyypillinen esimerkki listan läpikäynnistä on tilanne, jossa halutaan etsiä listasta parhaiten jonkin ehdon täyttävä arvo, esimerkiksi listan korkein lämpötila. Tällöin listaa läpikäydessä käytetään apumuuttujaa, johon tallennetaan korkein tähän asti löydetty lämpötila. Ennen läpikäyntiä apumuuttujan arvoksi sijoitetaan joko listan ensimmäinen alkio (jos tiedetään, että lista ei ole tyhjä) tai sitten niin pieni arvo, että se ei voi olla minkään päivän lämpötila. Sitten käydään lista läpi alkio kerrallaan. Jos tarkasteltavan alkion arvo on suurempi kuin apumuuttujan arvo, päivitetään apumuuttujan arvo. Kun lista on käyty kokonaan läpi, apumuuttuja sisältää koko listan korkeimman lämpötilan. Alla esimerkkinä funktio etsi_maksimi, joka käy sille parametrina annetun listan läpi ja etsii ja palauttaa korkeimman siitä löytyneen lämpötilan. Jos lista on tyhjä, funktio palauttaa erikoisarvon None.

def etsi_maksimi(lampotilalista):
    if len(lampotilalista) == 0:
        return None
    else:
        maksimi = lampotilalista[0]
        for lampotila in lampotilalista:
            if lampotila > maksimi:
                maksimi = lampotila
        return maksimi

Funktiota voidaan kutsua ja sen paluuarvo tulosta lisäämällä pääohjelmaan esimerkiksi seuraavat rivit:

maksimilampotila = etsi_maksimi(lampolista)
print("Korkein lampotila on ", maksimilampotila)

Huomautus: Pythonissa on myös valmis funktio max, joka etsii ja palauttaa suurimman arvon parametrina annetusta listasta. Yllä esitettyä periaatetta voi kuitenkin käyttää myös muissakin tilanteissa, joissa etsitään listasta jollain kriteerillä halutuinta alkiota, esimerkiksi alkiota, joka on lähimpänä parametrina annettua arvoa: Käytetään apumuuttujaa, jonka arvona on tähän asti löydetty paras alkio. Käydään läpi listaa alkio kerrallaan ja tutkitaan, onko vuorossa oleva alkio (käytetyllä kriteerillä) parempi kuin apumuuttujassa oleva arvo. Jos näin on, päivitetään apumuuttujan arvoa.