1. Johdanto

2. Algoritmi

2.1 Sisällys

  • Algoritmin määritelmä.

  • Aiheen pariin johdatteleva esimerkki.

  • Algoritmista ohjelmaksi.

2.2 Algoritmin määritelmä

  • Algoritmi (algorithm) on vaiheittainen kuvaus jonkin tehtävän suorittamista varten äärellisessä ajassa.

  • Algoritmilla on siis alku-, väli- ja loppuvaiheet.

  • Algoritmilla voidaan kuvailla jokapäiväisiä tehtäviä.

  • Tietokoneohjelma (program) on ohjelmointikielellä toteutettu algoritmi.

    • Algoritmi ovat luonteva tapa tutustua ohjelmoinnin vaatimaan abstraktiin ajatteluun.

2.3 Algoritmi oven avaukseen

  • Tehtävänä kertoa erityisen älykkäälle robotille kuinka avata ovi.

  • Tehdään aluksi joitakin algoritmin muotoilemista helpottavia oletuksia: ovi on kunnossa, paikallaan, kiinni, lukitsematon, ovessa on kahva eikä kahvaa tarvitse painaa.

  • Usein ensimmäisessä hahmotelmassa havaitaan virhe (niin sanottu bugi): Edellä ei huomioitu sitä, että jotkut ovet voi avata myös työntämällä.

3. Vuokaaviot

3.1 Sisällys

  • Kaavioiden rakenne.

  • Kaavioiden piirto symboleita yhdistelemällä.

  • Kaavion osan valitseminen päätöksellä ja toistaminen silmukalla.

  • Esimerkkejä:

    • algoritmi oven avaamiseen vuokaaviona,

    • yksikkömuunnos,

    • peli luvun arvaukseen ja

    • keskiarvon laskeminen.

3.2 Vuokaaviot

  • Graafinen kieli algoritmien kuvaamiseen.

    • Ymmärrettäviä ja intuitiivisia.

    • Soveltuvat monimutkaistenkin algoritmien esittämiseen.

  • Muodostetaan yhdistelemällä symboleja nuolilla.

  • Symbolissa algoritmin vaihe.

  • Kaavio suoritetaan (ajetaan) seuraamalla nuolia alkusymbolista alkaen ja loppusymboliin päätyen.

  • Esimerkki:

  • Etenevät yleensä ylhäältä alas ja vasemmalta oikealle:

    • Suunta länsimaisesta kirjoituksesta.

    • Tilan loppuessa voi piirtää muutenkin.

  • Aina yksi alku- ja yksi loppusymboli.

  • Symboleista lähtevien nuolien lukumäärä on yksikäsitteinen.

  • Symboleihin tulevien nuolien lukumäärässä tulkinnan varaa.

3.3 Lähtevät nuolet

  • Alkusymbolista lähtee aina vain yksi nuoli.

  • Loppusymbolista ei lähde nuolia.

  • Toimintosymbolista lähtee aina vain yksi nuoli.

  • Päätössymbolista lähtee aina kaksi nuolta, jotka vastaavat kyllä- ja ei-päätöksiä.

3.4 Tulevat nuolet

  • Alkusymboliin ei tule nuolia.

  • Muihin symboleihin tulee aina joko yksi tai useampi nuoli.

  • Jos symboliin tulee useampi nuoli, voidaan

    • nuolet piirtää suoraan kiinni symboliin tai

    • symboliin piirtää yksi nuoli, johon muut nuolet liittyvät.

  • Kalvoilla ja mallivastauksissa pyritään käyttämään selvyyden vuoksi jälkimmäistä piirtotapaa, jolloin tulevia nuolia on aina yksi.

3.5 Algoritmi oven avaamiseen

  • Algoritmia voidaan tarkentaa päätöksen avulla: päätös mahdollistaa toisensa poissulkevat algoritmin suorituslinjat.

  • Algoritmia tarkennetaan edelleen kuvaamalla toistuvat toiminnot päätöstä hyödyntäen.

3.6 Silmukka

  • Vuokaavion osa voidaan suorittaa silmukan (loop) avulla.

  • Koostuu päätöksestä, joka liitetään nuolella toistettavaan vuokaavion osaan, josta palataan päätökseen joko suoraan tai epäsuorasti.

  • Päätös sijoitetaan usein siten, että se on silmukan ensimmäiseksi suoritettava osa (esiehto).

  • Toisinaan on helpompaa sijoittaa päätös silmukan loppuun (jälkiehto).

    • Jälkiehtoa voi käyttää vuokaavioissa huoletta, vaikka Python-kielessä onkin vain esiehto.
  • Silmukointi jatkuu niin kauan kuin päätös on silmukkaan johtavaan nuolen suuntainen.

  • Algoritmi saattaa joutua ikuiseen silmukkaan, jos päätös on muotoiltu virheellisesti.

3.7 Algoritmi oven avaamiseen

  • Algoritmin uuteen versioon on lisätty silmukat, joissa ovea joko vedetään tai työnnetään kahvasta kunnes ovi on auki.

3.8 Yksikkömuunnos

  • Esimerkiksi pituutta ja painoa mitataan maailmanlaajuisesti eri yksiköillä.

    • Yksikkömuunnin on tyypillinen älylaitteista löytyvä sovellus.
  • Ohessa on esitetty yksinkertainen algoritmi jaardeina mitatun pituuden muuttamiseksi metreiksi.

  • Algoritmi on vuorovaikutteinen: jaardit luetaan käyttäjältä (user) ja metrit tulostetaan näytölle (screen).

3.9 Peli luvun arvaukseen

  • Peli arpoo kokonaisluvun käyttäjän tuntemalta väliltä. Käyttäjän tehtävänä on arvata lukua kunnes hän osuu oikeaan. Väärin menneen arvauksen osalta käyttäjälle kerrotaan oliko luku liian pieni tai suuri suhteessa arvattavaan lukuun. Oikeasta arvauksesta onnitellaan.

  • Algoritmiin tarvitaan silmukka, koska on hyvin epätodennäköistä, että käyttäjä arvaa luvun heti.

    • Silmukassa luetaan arvaus ja tehdään päätöksiä, joiden avulla kerrotaan oliko arvaus mahdollisesti "ali" tai "yli".

    • Oikeasta vastauksesta voidaan onnitella silmukan jälkeen, koska silmukka pysähtyy vasta, kun käyttäjä arvaa oikein.

    • Arpominen on tehtävä on ennen silmukkaa, jotta luvun arvaaminen on mahdollista vihjeiden avulla.

3.10 Peli luvun arvaukseen esiehdolla

Esiehtoa käytettäessä täytyy varmistaa ennen silmukkaa, että arvaus on eri suuri kuin arvattava, jotta silmukka saadaan käynnistettyä. Ohessa sopiva valinta tehdään käyttäjän puolesta. Luentorungossa käytetään myöhemmin vain esiehtoa, vaikka jälkiehto (katso seuraava sivu) olisi luontevampi, koska Pythonissa on vain esiehtoinen silmukka. Älä huolehdi tästä, vaan valitse harjoitusten vuokaavio-tehtävissä aina ratkaisuun parhaiten sopiva silmukka.

3.11 Peli luvun arvaukseen jälkiehdolla

Oheisessa ja edellisessä vuokaaviossa liian pienestä ja suuresta arvauksesta ilmoittavista toiminnoista voitaisiin palata suoraan arvauksen lukemiseen ja liian suuren arvauksen päätöksestä voitaisiin hypätä suoraan onnittelun tulostavaan toimintoon arvauksen ollessa oikein. Näin toimien lukujen erisuuruutta tutkivaa päätöstä ei tarvittaisi. Päätös on mukana, koska algoritmeissa pyritään yleensä selkeyden vuoksi palaamaan silmukan alkuun vain yhtä reittiä ja samoin lopettamaan silmukka sen ehdon kautta. Älä huolestu tästäkään! Voit käyttää harjoitustehtävissä lyhyempää tapaa. Pitemmän ja lyhyemmän vuokaavion ero selviää, kun silmukoihin palataan ohjelmien merkeissä.

3.12 Keskiarvon laskeminen

  • Keskiarvon laskennan tapaiset tiedon analysointiin liittyvät tehtävät on usein luontevaa toteuttaa tietokoneella.

  • Ohjelma kommunikoi käyttäjänsä kanssa: luvut luetaan yksi kerrallaan ennen keskiarvon laskemista ja keskiarvo tulostetaan käyttäjälle.

  • Ohjelmaan tarvitaan silmukka, jonka kullakin kierroksella luetaan luku ja lisätään luku summaan.

    • Silmukka on esiehtoinen, koska voidaan olettaa, että lukujen lukumäärä tiedetään ennen silmukkaa.

    • Ohjelmassa on varauduttava tilanteeseen, jossa lukumäärä on virheellinen (≤ 0).

3.13 Pohdintaa

  • Vuokaavioiden ongelmia:

    • Graafinen esitys poikkeaa paljon useimmista ohjelmointikielistä.

    • Algoritmin tarkentaminen kasvattaa kaaviota nopeasti.

    • Kaavioiden piirtäminen on työlästä.

  • Vapaamuotoisesti tekstillä kuvaillut algoritmin vaiheet ovat liian monikäsitteisiä tietokoneelle.

  • Kuinka algoritmin esitys voidaan tarkentaa tietokoneen ymmärtämälle tasolle?

4. Lausekielinen ohjelmointi

4.1 Konekieli ja symbolinen konekieli

  • Aluksi ohjelmoitiin tietokoneiden sellaisenaan ymmärtämällä konekielellä (machine language), jossa suorittimen komennot ja niiden parametrit esitetään binäärilukuina.

    • 10101001 00000001
  • Symbolisessa konekielessä (assembly language) komennot on nimetty lyhyesti.

    • LDA #$01
  • Koneläheisillä kielillä ohjelmointi on hidasta ja virhealtista, mutta toisaalta täysin ihmisläheinen ohjelmointi luonnollisella kielellä on mahdotonta kieliopin monimutkaisuuden sekä lauseiden ja sanojen moniselitteisyyden vuoksi.

4.2 Lausekieli

  • Lausekieli (high level language) on luonnollisen kielen kaltainen kieli, jota voidaan käyttää ohjelmointiin rajatun kieliopin ja sanaston sekä kääntäjä- ja tulkkiohjelmien ansiosta.

  • Kielimuurien yli lausekielellä.
  • Sanasto koostuu pääosin tunnuksiksi (identifier) kutsutuista nimistä ja kielelle varatuista sanoista (reserved word), joita ei voi käyttää tunnuksina.

  • Algoritmin vaiheet esitetään lauseina (statement).

    • Lause vastaa vuokaavion toimintoa.

    • Päätöstä tai silmukkaa kuvaavat lauseet aloitetaan tietyillä varatuilla sanoilla (esimerkiksi if ja while).

    • Päätökseen tai silmukkaan liittyvät lauseet kootaan yhteen kieliopillisella merkinnällä.

  • Lausekielistä ohjelmaa kutsutaan usein lähdekoodiksi (source code) erotuksena kääntäjän tai tulkin tuottamasta konekielisestä versiosta.

    • Sekä lähdekielistä että konekielistä ohjelmaa kutsutaan lyhyesti ohjelmaksi.
  • Ohjelman kirjoittaminen eli prosessi, jossa algoritmi toteutetaan, tunnetaan koodaamisena.

  • Lähdekoodi, samoin kuin sen sisältävä tiedosto, koostuu luonnollisista kielistä tutuista merkeistä (muun muassa kirjaimet, numerot, välilyönti ja välimerkit) ja joistakin näkymättömistä ohjausmerkeistä (muun muassa rivinvaihto).

4.3 Hyvä ohjelmointitapa

  • Lähdekoodin tulisi olla helposti ymmärrettävää, koska ohjelmia ei yleensä tehdä yksin ja vain omaan käyttöön.

  • Lausekielisen ohjelman toimintaa voi selkeyttää pienellä vaivalla käyttämällä kuvaavia nimiä, kommentoimalla ja ohjelman ulkoasua muotoilemalla sisentämällä ohjelman rivejä.

    • Kommentit ovat luonnollisella kielellä kirjoitettuja, ohjelman toimintaa selventäviä lauseita, joita ei suoriteta.

    • Sisennys on rivinvaihtojen lisäksi ainoa keino muotoilla ohjelmaa. Lähdekoodia ei voi esimerkiksi kursivoida tai lihavoida eikä siihen voi lisätä kuvia.

    • Sisennys voi osoittautua yllättävän hankalaksi omaksua.

4.4 Lausekielestä konekieleksi

  • Lausekielisen ohjelma kirjoitetaan tekstieditorilla ja tallennetaan tekstitiedostoksi, josta kääntäjä- tai tulkkiohjelma lukee lähdekoodin sitä muuntaessaan.

  • Kurssilla suositellaan käytettäväksi erityisesti ohjelmointiin tarkoitettua editoria.

    • Editorin tulee osata utf-8-merkistö. Kurssin kotisivuilla annetaan ohjeita Notepad++:n (Windows) ja BBEdit:in (Mac) käyttöön.
  • Ohjelmointiympäristöihin (IDE) on syytä tutustua vasta myöhemmin, koska niistä saattaa olla enemmän haittaa kuin hyötyä aloittelevalle ohjelmoijalle.

    • Pythonille esimerkiksi IDLE ja PyCharm.
  • Microsoft Word -ohjelma ei ole tarkoitettu ohjelmointiin.

  • Kääntäjäohjelma muuttaa lähdekoodin suoraan konekieliseksi tiedostoksi eli suoritettavaksi ohjelmaksi.

  • Tulkittavan kielen lähdekoodi suoritetaan erillisellä ohjelmalla eli tulkilla, joka muuttaa ajonaikaisesti lähdekoodin konekieleksi.
  • Ohjelman on oltava kieliopillisesti täysin oikein, jotta tulkkaus tai kääntäminen onnistuisi.

  • Käännetty koodi on yleensä tulkattua koodia nopeampaa.

  • Tulkilla voi tehdä nopeasti pieniä testejä, jos tulkkia voi käyttää myös siten, että ohjelmaa ei lueta tiedostosta, vaan se voidaan syöttää lause kerrallaan näppäimistöltä.

  • Käännettäviä lausekieliä ovat esimerkiksi Pascal, C ja C++ ja tulkattavia kieliä muun muassa Basicin jotkut versiot sekä Python.

    • Java-kielinen lähdekoodi käännetään tulkittavaksi tavukoodiksi.

5. Muuttujat ja funktiot

5.1 Sisällys

  • Imperatiivinen laskenta.

  • Muuttujat.

    • Nimi ja arvo.

    • Muuttujan nimeäminen.

    • Muuttujan tyyppi.

  • Operaattorit.

    • Operandit.

    • Arvon sijoitus muuttujaan.

    • Aritmeettiset operaattorit.

  • Funktiot.

    • Nimeäminen.

    • Parametrit.

    • Paluuarvo.

  • Esimerkkejä.

    • Yksikkömuunnos.

5.2 Muuttujat ja funktiot

  • Lausekielinen ohjelma on algoritmin toteutus, jossa käytetään tarkasti säännellyn kielen lisäksi ja jotain laskentamallia (ohjelmointi-paradigma).

  • Eräs tapa laskea ohjelmallisesti on

    • esitellä äärellinen joukko muuttujia ja

    • käyttää funktioita (aliohjelma, operaatio, metodi jne.) muuttujien arvojen lukemiseen ja muuttamiseen.

  • Muuttujien arvot algoritmin tietyssä vaiheessa määrittävät algoritmin tilan (state).

    • Algoritmi siirtyy tilasta toiseen suorituksensa aikana.
  • Tällainen laskenta on luonteeltaan imperatiivista ja on käytössä useimmissa ohjelmointikielissä Python mukaan lukien.

5.3 Muuttujat

  • Muuttuja (variable) koostuu nimestä (tunnuksesta) ja nimeen liittyvästä arvosta.

  • Muuttuja ei ole matematiikasta tuttu käsite, jolla symboloidaan usein tuntematonta arvoa.

    • Esimerkiksi yhtälössä x + 1 = 0 tuntematon arvo on x.
  • Algoritmissa muuttujan arvo on yleensä tunnettu ja erityisesti erona on se, että arvo voi muuttua algoritmin edetessä vaiheesta toiseen.

    • Muuttuja muistuttaa hieman suuretta eli mitattavaa ominaisuutta. Esimerkiksi lämpötila (T) on suure, jolla mitataan aineen lämpöenergian määrää.
  • Muuttuja nimetään sen tarkoitusta kuvaavasti.

    • Algoritmien ymmärrettävyyden kannalta on tärkeää, että nimistä nähdään minkä tiedon säilyttämiseen muuttujia käytetään.
  • Yleensä hyvä nimi on riittävän pitkä nimi.

    • Esimerkiksi pituuden sisältävälle muuttujalle on parempi antaa nimeksi pituus kuin p, joka voi sekaantua vaikkapa painoon.
  • Pythonissa muuttujien nimissä voidaan käyttää kirjaimia, numeroita ja alaviivaa.

    • Nimessä käytetään pieniä kirjaimia.

    • Nimen sanat erotetaan toisistaan alaviivalla.

    • Nimissä voi käyttää skandinaavisia kirjaimia å, ö ja ä.

    • Nimi ei voi alkaa numerolla.

  • Toisin kuin useissa ohjelmointikielissä, Pythonissa ei ole erillistä muuttujan esittelyä, jossa määritellään erikseen muuttujan tietotyyppi.

    • Python päättelee muuttujan tyypin sen alkuarvon avulla.

5.4 Operaattorit

  • Operaattoreiksi (operator) kutsutuille funktioille on annettu omat symbolit.

  • Operaattorin käsittelemät arvot ovat operandeja.

    • Operandit voivat olla arvoja sellaisenaan (literaaleja), muuttujien arvoja tai muiden funktioiden palauttamia arvoja.

    • Useimmat operaattorit ovat kaksioperandisia.

  • Operaattoreista tärkein on sijoitus, joka korvaa muuttujan nykyisen arvon uudella.

    • Myös Pythonissa symbolina yhtäsuuruusmerkki =.

      • Arvojen yhtäsuuruuden vertailuun käytetään kahta yhtäsuuruusmerkkiä (==).
    • Ensimmäinen (vasen) operandi on muuttujan nimi.

    • Toinen (oikea) operandi on uusi arvo.

  • Sijoituslause: muuttujan\_nimi = uusi\_arvo on algoritmin vaihe.

  • Ensimmäistä sijoitusta kutsutaan muuttujan alustukseksi (initialisation).

    • Muuttujan tyyppi päätellään sen alkuarvon avulla.

    • Muuttuja täytyy alustaa ennen sen myöhempää käyttöä ohjelmassa.

  • ikä = 42

    • Ikä-nimisen muuttujan arvoksi asetetaan 42.

    • Muuttujan tyypiksi kiinnitetään kokonaisluku (int).

  • ikä = 50

    • Uusi arvo 42 korvaa muuttujan vanhan arvon 50. Sijoitusta ei voi peruuttaa.
  • paino = 80.5

    • Reaaliluvuissa (float) käytetään desimaali-erottimena pistettä.
  • paino = 80,5

    • Pilkkua on helppo käyttää vahingossa desimaali-erottimena, koska pilkuilla toisistaan erotetuista arvoista muodostuu automaattisesti tupla, joka on eräs Pythonin sekvensseistä.
  • Reaalityyppiä kutsutaan esitystapansa vuoksi myös liukuluvuksi.

  • Liukuluvut ovat likiarvoja.

    • Esimekiksi \(\frac{1}{11}\) = 0.09090909090909091
  • sukunimi = "Simpson" sukunimi = 'Simpson'

    • Merkkijonotyyppiset (string) literaalit suljetaan joko yksin- tai kaksinkertaisiin lainausmerkkeihin.

      • Luentomateriaalissa käytetään pääosin kaksinkertaista lainausta.

      • Valitse oma tyylisi lainausmerkkien suhteen ja seuraa tyyliäsi johdonmukaisesti.

  • Merkkijono voidaan alustaa tyhjällä, nollan merkin mittaisella arvolla "" tai ''.

  • Huomaa, että "sukunimi" on literaali eikä muuttujan nimi.

    • Merkkijonoksi katsotaan myös pelkistä numeroista koostuvat jonot. Esimerkiksi "42" on merkkijono.
  • Python sallii muuttujan tyypin vaihdon sijoituksen kautta.

  • pituus = 150 pituus = 150.7

    • Muuttujan tyyppi vaihtuu kokonaisluvusta liukuluvuksi sijoituksen seurauksena.
  • On oltava tarkkana ettei tyyppi vaihdu vahingossa vääräksi.

  • pituus = "165"

    • Tämäkin sijoitus on sallittu, vaikka loogista olisi käyttää edelleen vain lukuarvoja.
  • Pythonissa on saatavilla lukujen ja merkkijonojen lisäksi useita muita tyyppejä.

    • Kurssin edetessä tutustutaan uusiin tyyppeihin.
  • Koulusta tutut aritmeettiset operaattorit lukujen yhteen-, vähennys-, kerto- ja jakolaskuun ovat käytettävissä Pythonissa.

    • Symbolit +, - ja / ovat tuttuja, kertomerkkinä käytetään tähteä * eli asteriskia.

    • Laskujärjestys on tuttu.

      • Sulkuja voidaan käyttää järjestyksen muuttamiseen.
  • Aritmeettiset operaattorit ovat sijoitusoperaattoria vahvempia.

    • Aritmeettisia lausekkeita (lauseen osia) lasketaan, kunnes saadaan yksittäinen arvo sijoitettavaksi.

    • Sijoitus muodostaa algoritmin vaiheen myös, kun lauseessa on muuta laskentaa ennen sijoitusta.

  • tulosumma = 1 * 2 + 2 * 2

    • Ensin lasketaan tulolausekkeet 1 * 2 ja 2 * 2, toiseksi summalause 2 + 4 ja lopuksi sijoitetaan summa (6) muuttujan arvoksi.

    • Sijoitus muodostaa algoritmin vaiheen, vaikka ennen sijoittamista lasketaan aritmeettisilla operaattoreilla.

  • Laskennan aikana voi tapahtua automaattisia (implisiittisiä) tyyppi-muunnoksia.

    • Kokonaislukua kokonaisluvulla jaettaessa tulos on reaaliluku.

      • Esimerkiksi lausekkeen 4 / 2 arvo on 2.0, vaikka jako menee tasan.
    • Aritmeettinen operaattori palauttaa reaalilukutyyppisen arvon, jos jompikumpi operandi on reaaliluku.

  • Muuttujan nykyistä arvoa täytyy toisinaan kasvattaa tai vähentää, jolloin saman muuttujan tunnus esiintyy molemmin puolin sijoitusoperaattoria.

    • Erityisesti silmukoihin liittyvät kokonaisluku-arvoiset laskurit (counter) muuttuvat usein yhden arvon verran.
  • Tällaisissa tapauksissa muuttujan nykyistä arvoa käytetään vasemmalla puolella ja muuttuja saa uuden arvon vasta, kun oikea puoli on laskettu.

  • ikä = ikä + 1

    • Ensin haetaan iän vanha arvo (50 + 1) ja sitten lasketaan summa (51) ja lopuksi sijoitetaan summa muuttujan uudeksi arvoksi.

5.5 Funktiot

  • Operaattoreita moni-mutkaisemmat funktiot ovat algoritmeja, joita kutsutaan (call) nimen avulla.

    • Tunnuksen lopussa on aina kaarisulkeiden pari.
  • Funktion tarvitsemat tiedot eli parametriarvot (parameter values) välitetään funktiolle kirjoittamalla arvot kaarisulkeiden sisään.

    • Arvot erotetaan pilkuilla.
  • Parametriarvo on operandin tapaan literaali, muuttujan arvo jne.

  • Funktion parametrien lukumäärä, tyypit ja järjestys selviää funktion dokumentaatiosta.

  • help()

    • Lause käynnistää Python-tulkin ohjetoiminnon, kun tulkkia käytetään interaktiivisessa tilassa.
  • Funktiolla voi olla paluuarvo (return value).

    • Operaattori palauttaa aina arvon, mutta funktio voi palauttaa arvon tai jättää sen palauttamatta.

    • Paluuarvo voidaan jättää huomiotta tai hyödyntää.

      • Paluuarvo sijoitetaan usein muuttujaan, jotta sitä voidaan käyttää myöhemmin ohjelmassa. Paluuarvon säilövä muuttuja voi myös selkeyttää ohjelmaa.
  • luku1 = 42 luku2 = 13 suurempi = max(luku1, luku2)

    • Python hakee luku1- ja luku2-muuttujien arvot ja käyttää niitä funktion kutsussa.

    • Funktio päättelee kumpi parametriarvoista 42 ja 13 on suurempi ja palauttaa suuremman arvon (42), joka sijoitetaan muuttujaan.

    • Kutsu ja sijoitus muodostavat yhdessä lauseen.

  • neliö = pow(3, 2)

    • Funktio korottaa luvun kolme toiseen potenssiin.

    • Pow-funktion kutsussa on tärkeää huomioida arvojen järjestys: ensimmäinen parametriarvo on kantaluvulle, toinen eksponentille.

  • Funktioiden avulla voidaan kommunikoida käyttäjän kanssa.

  • Print-funktio tulostaa parametriarvonsa näytölle.

    • Parametriarvoja voi olla yksi tai useampia. Funktio tulostaa parametriarvojen väliin yhden välilyönnin.

    • Tulosteen jälkeen vaihdetaan riviä.

  • print(″Hello, World!″)

    • Näytölle tulostetaan merkkijonolla ″Hello, World!″ alkava rivi ja vaihdetaan riviä.
  • print(″Tulos on″, suurempi)

    • Python välittää funktiolle suurempi-muuttujan arvon.

    • Funktio muodostaa merkkijonon ″Tulos on 42″, joka koostuu välilyönnillä erotetuista parametriarvoista ja tulostaa merkkijonon näytölle.

  • Input-funktio palauttaa käyttäjän näppäimistöltä antaman arvon merkkijonona.

    • Funktiota voidaan kutsua ilman parametria tai antamalla parametriksi käyttäjää avustava kehote.

    • Kurssilla input-funktiota kutsutaan aina ilman parametria, jotta automaattinen tarkistus olisi helpompaa.

  • etunimi = input()

    • Ohjelman pysähtyy odottamaan syötettä funktiossa.

    • Ohjelman suoritus jatkuu vasta, kun käyttäjä kirjoittaa syötteen ja painaa Enter-näppäintä.

    • Funktion palauttama merkkijono sijoitetaan muuttujaan.

  • Numeerinen merkkijono voidaan muuttaa ohjelmoijan toimesta (eksplisiittisesti) halutuksi lukutyypiksi int- tai float-funktiolla.

  • Ohjelma pysähtyy ajonaikaiseen virheeseen (runtime error), jos muunnos ei onnistu.

    • Kurssilla oletetaan, että käyttäjä osaa antaa aina oikean tyyppisen syötteen.

    • Syöte voi kuitenkin olla virheellinen esimerkiksi siten, että annettu luku ei kuulu syötteeksi hyväksyttyjen lukujen välille.

  • ikä = int(input())

    • Luetaan merkkijono, muunnetaan se kokonaisluvuksi ja sijoitetaan muunnoksen tuloksena saatava merkkijono muuttujan arvoksi.

    • Sisäkkäisissä funktiokutsuissa sisempää funktiota kutsutaan ensin, jolloin ulompi funktio saa sisemmän kutsun paluuarvo parametriarvokseen.

  • Int-funktion kanssa on oltava tarkkana, koska sillä voi myös poistaa reaaliluvun desimaalit.

  • pituus = float(input()) pituus = int(pituus)

    • Poistetaan reaalilukuna saadun syötteen desimaalit. Ohjelma kadottaa tietoja, jos jälkimmäinen muunnos on vahinko.

5.6 Yksikkömuunnos

  • Mahdollisimman suora muunnos luonnollisella kielellä esitetystä muodosta Python-kielelle

  • Algoritmissa on selkeyden vuoksi omat muuttujat pituudelle lähtö- ja tulos-yksiköissä.

    • Yhdeksi toiminnoksi (lauseeksi) tiivistettynä ohjelma on hankala ymmärtää.

  • Ohjelmasta saadaan käyttäjäystävällisempi lisätulosteilla.

    • Ilman toisena toimintona tulostettavaa kehotetta käyttäjän täytyy arvata, että ohjelma odottaa syötettä.

    • Muunnoksen tulos esitetään paremmin ymmärrettävässä muodossa antamalla print-funktiolle kaksi parametria.

6. Ensimmäinen Python-ohjelma

6.1 Hello world -ohjelma

  • Uutteen ohjelmointi-kieleen tutustuttaessa tehdään perinteisesti ensimmäisenä ohjelmana Hello world -ohjelma, joka tulostaa näytölle tervehdyksen maailmalle.

  • Hello world on Pythonissa äärinmmäisen yksinkertainen.

    • Ohjelma koostuu vain ohjelmaa ja sen tekijää valottavista kommenteista ja tulostus-lauseesta.

    • Ristikkomerkillä (risuaita) alkavat rivit ovat koodin lukijalle kohdistettua metatietoa, jonka tulkki ohittaa.

# Ensimmäinen Python-ohjelma.
# Jorma Laurikkala (jorma.laurikkala\@tuni.fi)
print("Hello, World!")
  • Ohjelma voidaan suorittaa Python-tulkin interaktiivisessa tilassa kirjoittamalla se rivi riviltä tulkissa.

    • Tulkki käynnistyy komentoikkunassa komennolla python.

    • Tulkki tulostaa käynnistyessään versiotietoja ja kertoo joistakin tulkin ymmärtämistä komennoista.

    • Lauseet, lausekkeet ja tulkin komennot kirjoitetaan kehotteen >>> jälkeen ja lähetetään tulkille Enter-näppäintä painamalla.

    • Kurssin kotisivuilla kerrotaan komentoikkunasta ja Pythonin asennuksesta.

  • Tulkki lukee kommentteja kunnes painetaan Enter-näppäintä tai syötetään jotain muuta kuin kommentti.

    • Kolme pistettä kertoo, että tulkki odottaa jatkosyötettä.
  • Tulostuslause tulkitaan heti ja sen tulostama rivi seuraa riviä, jolla lause annettiin ohjelmalle.

  • Interaktiiviseen tilaan käynnistetty tulkki suljetaan exit-komennolla.

  • Python-ohjelmat suoritetaan tällä kurssilla pääosin tavanomaiseen tapaan syöttämällä tulkille kaikki ohjelman lauseet kerralla lähdekooditiedostosta.

  • Kirjoitetaan ohjelma tekstieditorilla ja tallennetaan se hello_world.py-nimiseen tiedostoon.

  • Tiedoston on oltava Python-tulkin olettamassa utf-8-koodatussa Unicode-merkistössä, jotta se voidaan tulkita.

    • Merkistö on ongelma vain Windowsissa, joka käyttää omaa merkistöään.

    • Utf-8 on oletusmerkistö Mac- ja Linux-järjestelmissä.

  • Ohjelma annetaan tulkin suoritettavaksi komentoikkunassa komennolla python hello_world.py.

  • Ennen tulkin kutsumista on komentoikkunassa siirryttävä cd-komennolla oikeaan hakemistoon, jotta tulkki löytää lähdekooditiedoston.

    • Alla olevassa kuvassa tiedosto on test-hakemistossa.
  • Exit-komentoa ei tule kirjoittaa ohjelmaan; tulkki pysähtyy automaattisesti ohjelman viimeisen lauseen luettuaan.

6.2 Muuntaja-ohjelma

  • Jaardit metreiksi muuntavan ohjelman muuttaminen Python-ohjelmaksi on helppoa; siinä on vain peräkkäisiä toimintoja.

    • Vuokaavion muuttujilla ja funktioilla kuvatut toiminnot voidaan kopioida lähdekoodiin samaan järjestykseen lauseiksi.

    • Kutakin toimintoa vastaava lause kirjoitetaan omalle rivilleen, koska Pythonissa lause on pääsääntöisesti yhdellä rivillä.

    • Löydät muuntaja.py-tiedoston kurssin kotisivuilta.

    • Komentoikkunassa siirrytään ennen ohjelman suoritusta ohjelmatiedoston sisältävään kansioon.

6.3 Lähdekoodin organisointi

  • Kurssin harjoitusten ratkaisut kannattaa organisoida samalla tavoin kuin kurssin esimerkit on organisoitu.

  • Tee kurssikansio, jossa on harjoituksille oma alikansio ja tässä kansiossa kullekin harjoitukselle oma alikansio, jonne tallennat kyseisen harjoituksen ohjelmat.

    • Löydät näin nopeasti tietyn tehtävän koodin eivätkä saman ohjelman eri versiot mene helposti sekaisin.
  • Älä tallenna koskaan ohjelmiasi Python-tulkin omaan hakemistoon, koska suorituskelpoiset ohjelmat täytyy pitää erillään niiden käsittelemästä datasta.

  • Muista tehdä säännöllisesti varmuuskopioita ohjelmistasi.

6.4 Kielioppivirheet

  • Ohjelman tulkitseminen pysähtyy, jos siinä havaitaan kielioppivirhe.

  • Oheisesta Hello World-ohjelmasta puuttuu sulkeva lainausmerkki.

  • Tulkin virheilmoituksesta käy ilmi virheen rivi ja virheen laatu.

  • Korjattu ohjelma pitää muistaa tallentaa, koska tulkki lukee lähdekoodin tiedostosta, ei editorista.

7. Tulostuksen hienosäätöä

  • Funktiolle voidaan välittää niin sanottuja avainsana-parametreja (keyword argument), joilla voidaan säädellä muun muassa mitä parametriarvojen väliin tulostetaan (sep-parametri) ja kuinka rivi päätetään (end-parametri).

    • Tavanomaisista parametreista poiketen avainsanaparametrien arvot annetaan parametrin nimen avulla. Tavanomaista parametria käytettäessä riittää antaa vain parametrin arvo.

    • Avainsanaparametrien arvot annetaan muiden parametriarvojen jälkeen.

  • print("Grill", "O", "Mat", sep = "-")

    • Tulostaa merkkijonon "Grill-O-Mat", koska sep-parametrille annetaan arvoksi merkkijono "-".
  • Funktio päättää normaalisti tulosteensa rivinvaihtoon.

    • Tulkki päättää rivin käyttöjärjestelmän määräämällä tavalla.

      • Windows: CR- ja LF-merkit. Mac ja Linux: LF-merkki.
  • Ohjelmoija voi korvata rivinvaihdon end-parametrilla.

    • Hyödyllistä, jos halutaan tulostaa samalle riville usealla lauseella.
  • print("Always Look on the ", end = "") print("Bright Side of Life")

    • Tulostaa merkkijonon "Always Look on the Bright Side of Life" yhdelle riville. Ensimmäinen lause ei vaihda riviä, koska end-parametri on määritelty. Tyhjä merkkijono "" käskee funktiota olemaan tulostamatta mitään merkkijonon "Always Look on the " jälkeen. Jälkimmäinen lause vaihtaa riviä koko tulosteen lopuksi.

7.2 Erikoismerkit

  • Erikoismerkkejä (escape characters) käytetään merkkeinä ja merkkijonon osina.

  • Koostuvat kahdesta merkistä, mutta ovat kieliopillisesti yksi merkki.

    • Aloitetaan kenoviivalla \, jota seuraa toinen merkki.
  • \n rivinvaihto

  • \t tabulaattori

  • \' yksinkertainen lainausmerkki

  • \" lainausmerkki

  • \\ kenoviiva

  • \a äänimerkki

7.3 Tulostus erikoismerkeillä

  • Erikoismerkeillä saadaan aikaan helposti vaikeasti luettavaa koodia.

    # Älä tee näin.
    print("\t\t**********\n\t\t* Python *\n\t\t**********")
    # Tee näin.
    print("\t\t**********")
    print("\t\t* Python *")
    print("\t\t**********")
  • Yksinkertaisissa lainausmerkissä oleva lainausmerkki voidaan esittää sellaisenaan ja päinvastoin.

    # Yksinkertaista lainausmerkkiä ei tarvitse esittää
    # alla erikoismerkkinä, koska se on lainattu.
    print("It's just a flesh wound...")

8. Lisää operaattoreista ja tyypeistä

8.1 Sisältö

  • Lisää aritmeettisia operaattoreita.

    • Potenssilasku, osamäärä ja jakojäännös.
  • Merkkijonojen yhdistäminen eri tavoin.

  • Totuusarvo bool.

  • Vertailuoperaattorit.

8.2 Aritmetiikkaa

  • Potenssilaskulle on pow-funktion lisäksi oma operaattori **.

  • pow(2, 3) ja 2 ** 3 ovat molemmat \(2^3\).

  • Osamäärän kokonaisosa saadaan selville //-operaattorilla ja jakojäännös %-operaattorilla.

    • Jakojäännösoperaattori on hyödyllinen, kun pitää päätellä onko vasen operandi jaollinen oikealla operandilla.
  • 7 // 3 on 2, koska kolme menee kaksi kertaa lukuun seitsemän.

  • 7 % 3 on 1, koska 7 - 2 ∙ 3 = 1.

  • 3 on pariton luku, koska 3 % 2 on 1.

  • 4 on parillinen luku, koska 4 % 2 on 0.

8.3 Merkkijonojen yhdistäminen

  • Python yhdistää automaattisesti peräkkäiset merkkijonoliteraalit.

  • yhdiste1 = "Spam!" " " "Spam!"

    • Muuttuja saa arvokseen merkkijonon "Spam! Spam!"
  • Yhteenlaskuoperaattori on käytettävissä myös merkkijonojen yhdistämiseen.

    • Molempien operandien on oltava merkkijonoja. Tulos merkkijono.

    • Tarvitaan, kun merkkijonoliteraaliin yhdistetään muuttujan arvo, koska automaattinen yhdistäminen on sallittua vain literaaleille.

    etunimi = "Brian"
    # Tulostetaan "The life of Brian."
    print("The life of " + etunimi + ".")
  • Merkkijonoon saadaan yhdistettyä luku muutamalla se merkkijonoksi str-funktiolla ennen yhdistämistä.

  • print(str(metrit) + " metriä.")

    • Reaalilukuarvoinen metrit-muuttuja muunnetaan ensin merkkijonoksi. Sitten muunnokseen yhdistetään merkkijonoliteraali. Lopuksi tulostetaan yhdistetty merkkijono.

    • Oletetaan muuttujan arvoksi 1.8288. Tällöin: str(metrit) + " metriä." → "1.8288" + " metriä." → "1.8288 metriä."

  • tulos = str(metrit) + " metriä." print(tulos)

    • Apumuuttuja voi selkeyttää ohjelmaa.
  • Yhdistäminen on mahdollista lausua lyhemmin kerto-operaattorilla.

    • Operandeina kokonaisluku ja merkkijono tai päinvastoin. Tulos merkkijono.
  • 5 * "Spam! "

    • "Spam! Spam! Spam! Spam! Spam! "

    • Huomaa tuloksen loppuun jäävä "ylimääräinen" välilyönti.

  • 4 * "Spam! " + "Spam!"

    • "Spam! Spam! Spam! Spam! Spam!"

8.4 Totuusarvotyyppi bool

  • Totuusarvotyyppinen (Boolean-tyyppi) muuttuja voi olla vain joko tosi (True) tai epätosi (False).

  • vaihtoehtoinen_totuus = False

  • Pythonissa totuusarvoilla on numeeriset vastineet.

    • Luku 0 vastaa arvoa False.

      • Myös tyhjä merkkijono '' tai "" tulkitaan tarvittaessa epätodeksi.
    • Luku 1 vastaa arvoa True.

8.5 Vertailuoperaattorit

  • Operandeja kaksi tai useampia.

    • Kokonaislukuja voidaan verrata reaalilukuihin ja päinvastoin.

    • Merkkijonoja voidaan vertailla keskenään.

  • Palauttavat totuusarvon.

  • Vertailuoperaattorit ovat keskenään samanarvoisia.

  • Aritmeettiset operaattorit ovat vertailuja vahvempia.

    • 2 + 1 == 1 * 32 + 1 == 3 → 3 == 3True
  • Ketjutetut vertailut, joissa on vähintään kolme operandia, arvioidaan vasemmalta oikealle.

  • a < b < ca < b ja b < c.

  • Älä tee pitkiä ketjuja; niitä on vaikea seurata.

  • Totuusarvoja voidaan verrata suunnallisesti, koska niillä on kokonaislukuvastineet.

    • True > False1 > 0True.

    • True < False1 < 0False.

  • Käytä True- ja False-arvoja kokonaislukujen sijasta aina, kun mahdollista, koska totuusarvotyyppi on tehty vertailuun.

  • Merkkijonoja vertaillaan pohjautuen merkkien sijaintiin Unicode-merkistössä.

    • Isot ja pienet kirjaimet ovat eri merkkejä.

    • "a" < "A"False, koska suuren a-kirjaimen paikka on ennen pientä kirjainta.

    • Ord-funktio palauttaa merkin paikan (Unicode-koodin) merkistössä.

      • ord("a")97, ord("A")65
    • Chr-funktio toimii päinvastoin: se palauttaa koodia vastaavan merkin.

      • chr(97)"a", chr(65)"A"

9. Pythonin valintarakenteet

9.1 If-lause

  • Merkitään varatulla sanalla if.

  • Kuvaa yksisuuntaisen päätöksen: rakenteen lauseet suoritetaan vain ehdon ollessa totta (True).

  • Lauseet kiinnitetään rakenteeseen sisentämällä niitä neljällä välilyönnillä.

    • Pythonissa sisennyksellä on kieliopillinen merkitys.

    • Sisennys auttaa ohjelman lukijaa hahmottamaan mitkä lauseet ovat osa rakennetta.

9.2 If-lause (vertailija1.py)

...
# Luetaan luvut.

print("Anna ensimmäinen luku:")
x = int(input())
print("Anna toinen luku:")
y = int(input())

# Vertaillaan lukuja.
if x == y:
# Tulostetaan, jos x ja y ovat yhtä suuret.
print(x, "==", y)
...

9.3 If-else-lause

  • Merkitään varatuilla sanoilla if ja else.

  • Kuvaa kaksisuuntaisen päätöksen: if-osaan liittyvät lauseet suoritetaan ehdon ollessa totta, muuten suoritetaan else-osaan liittyvät lauseet.

    • Else-osalla ei ole omaa ehtoa.
  • Lause kiinnitetään rakenteen osaan neljällä välilyönnillä.

9.4 If-else-lause (vertailija2.py)

...
# Vertaillaan lukuja.
if x == y:
    # Tulostetaan, jos x ja y ovat yhtä suuret.
    print(x, "==", y)
else:
    # Tulostetaan, jos x ja y ovat erisuuret.
    print(x, "!=", y)
...

9.5 Sisäkkäiset valinnat

  • Monisuuntaisten valintojen ilmaisuun.

  • Sisennysten merkitys kieliopillisena merkintänä ja ohjelman rakenteen selkeyttäjänä korostuu rakenteiden.

  • Sisennysten määrää voidaan vähentää korvaamalla sisäkkäiset rakenteet if-elif-else-lauseella, jossa kukin elif-avainsanalla merkitty osa yhdistää ulomman if-else-lauseen else-osan ja sisemmän if-else-lauseen if-osan.

  • Sisäkkäiset valintarakenteet voidaan korvata peräkkäisillä valinnoilla, jos rakenteiden ehdot sulkevat toisensa pois.

    • Sisäkkäisiä valintoja suositellaan, koska ne ovat vähemmän virhealttiita ja tehokkaampia.

9.6 Sisäkkäiset valinnat (vertailija3.py)

...
# Ulompi lause.
if x < y:
    print(x, "<", y)
else:
    # Sisempi lause.
    if x == y:
        print(x, "==", y)
    else:
        # Pakko olla totta, jos ehtolausekkeet
        # ovat epätosia.
        print(x, ">", y)
...

9.7 Sisäkkäiset valinnat (vertailija4.py)

...
if x < y:
    # Tulostetaan, jos x on pienempi kuin y.
    print(x, "<", y)
# "else:" ja "if x == y:" yhdessä.
elif x == y:
    # Tulostetaan, jos x ja y ovat yhtä suuret.
    print(x, "==", y)
else:
    # Pakko olla totta, jos ehtolausekkeet ovat 
    # epätosia.
    print(x, ">", y)
...

9.8 Peräkkäiset valinnat

...
# (x == y) == False ja (x > y) == False
if x < y:
    print(x, "<", y)

# (x < y) == False ja (x > y) == False
if x == y:
    print(x, "==", y)

# (x < y) == False ja (x == y) == False
if x > y:
    print(x, ">", y)
...

10. Toisto while-lauseella

10.1 Yleistä

  • Algoritmin osien toistuva suoritus ilmaistaan lausekielissä toistorakenteilla, joita kutsutaan lyhyemmin silmukoiksi (loop).

  • Pythonissa on käytettävissä while- ja for-rakenteet.

    • For esitellään myöhemmin, koska se on tehty sekvenssien silmukointiin.
  • While-rakennetta ohjataan joko lipulla tai laskurilla riippuen siitä onko silmukan kierrosten lukumäärä tuntematon vai tunnettu ennen silmukkaa.

    • While on esiehtoinen silmukka.

    • Silmukka käyttäytyy jälkiehtoisen silmukan tapaan, kun ehdossa käytettävien muuttujien alkuarvot valitaan sitten, että silmukan ensimmäinen kierros suoritetaan aina.

10.2 While-lause

  • Aloitetaan varatulla sanalla while.

  • Ilmaisee esiehtoisen toistorakenteen, jossa rakenteen lauseita suoritetaan niin kauan kuin ehtolauseke on totta (True).

  • Toistettavat lauseet kiinnitetään toistorakenteeseen niitä sisentämällä.

    • Sisennykseen käytetään Pythonin tyyliohjeiden mukaisesti neljää välilyöntiä.

    • Muista edelleen sisennyksen perimmäisin merkitys: koodin muotoilu välilyönnein kertoo lukijalle mitkä lauseet kuuluvat rakenteeseen.

10.3 Liput

  • Lippu (flag tai sentinel) on joko muuttuja tai arvo, jota käytetään silmukan ohjaamiseen, kun toistojen määrää ei ole kiinnitetty.

    • Esimerkiksi tekstipohjaisten ohjelmien käyttöliittymässä on yleensä "pääsilmukka", josta poistutaan vasta, kun käyttäjä antaa ohjelmalle lopetuskomennon jotain näppäintä painamalla.
  • Lippua tarkastellaan silmukan ehtolausekkeessa.

  • Lippumuuttujan arvo vaihdetaan silmukan sisällä, kun silmukka päätetään lopettaa.

    • Yleensä bool-tyyppiä, jolloin True-arvo vaihtuu False-arvoksi tai päinvastoin.

    • Ohjelma joutuu ikuiseen silmukkaan, jos lippumuuttujan arvon vaihto unohtuu.

  • Lippu voi olla myös silmukassa käytettävän muuttujan arvo, jonka esiintyessä silmukka päätetään.

    • Silmukan pysäyttävä arvo voi olla esimerkiksi käyttäjän syötteenä antama negatiivinen luku, kun positiiviset luvut katsotaan normaaleiksi syötteiksi.

    • Lippumuuttuja on suositeltavampi, kun silmukka on pitkä tai ehtolauseke alkaa monimutkaistua.

  • Lippua käytetään usein, kun silmukka jatko riippuu käyttäjän antamasta syötteestä.

    • While-silmukka pyritään muotoilemaan jälkiehtoisen silmukan kaltaiseksi, jotta ohjelmaan ei tarvitse kirjoittaa ylimääräisiä lauseita ensimmäisen syötteen lukemiseen ennen esiehtoista silmukkaa.

10.4 Liput (lukija1.py)

  • Silmukkaa ohjaa lippuarvo "Spam!"

  • Syötteen säilövä muuttuja alustetaan siten, että esiehtoinen silmukka suoritetaan aina vähintään kerran.

...
merkit = ""
while merkit != "Spam!":
    print("Sano jotain:")
    merkit = input()
...

10.5 Liput (lukija2.py)

  • Silmukkaa ohjaa lippumuuttuja, jonka arvo vaihdetaan silmukan sisällä if-lausetta käyttäen.

  • Lippumuuttuja alustetaan siten, että esiehtoinen silmukka suoritetaan aina vähintään kerran.

  • Lippumuuttujaa ei tarvitse vertailla erikseen True-arvoon, koska muuttujan arvo on jo sellaisenaan bool-tyyppinen.

...
jatketaan = True
while jatketaan:
    print("Sano jotain:")
    merkit = input()
    if merkit == "Spam!":
        jatketaan = False
...

10.6 Liput (lukija3.py)

  • Lippumuuttuja alustetaan käyttäjän syötteen avulla, jolloin joudutaan toistamaan lauseita. Toisto ei ole toivottavaa, koska toistamalla ohjelma monimutkaistuu tarpeettomasti ja sen ylläpito vaikeutuu.
...
# Turhaa toistoa.
print("Sano jotain:")
merkit = input()
jatketaan = True
if merkit == "Spam!":
    jatketaan = False

while jatketaan:
    print("Sano jotain:")
    merkit = input()
    if merkit == "Spam!":
        jatketaan = False
...

10.7 Break-, continue ja ikuinen silmukka

  • Break-lause keskeyttää silmukan suorituksen.

    • Käytetään aina ehdollisena, koska silmukka pysähtyy välittömästi.
  • Continue-lause pysäyttää silmukan nykyisen kierroksen, mutta silmukan suoritusta jatketaan, mikäli kierroksia on jäljellä.

  • Break- ja continue-lauseita lauseita käytetään yleensä yhdessä tarkoituksella tehdyn ikuisen while-silmukan kanssa. Tällä kurssilla näin ei saa tehdä, jotta while-lausetta opittaisiin aluksi käyttämään aidosti ehtolausekkeen kautta.

10.8 Liput (lukija4.py)

  • Silmukan ehtolauseke on ilmaistu pelkällä True-literaalilla.

  • Silmukka pysäytetään lippuarvon syötön jälkeen break-lauseella.

  • Ei näin tällä kurssilla; eräs kurssin oppimistavoite on osata käyttää while-lausetta oikein.

while True:
    print("Sano jotain:")
    merkit = input()
    if merkit == "Spam!":
        break

10.9 While ja else

  • While-silmukan yhteydessä voidaan käyttää else-avainsanaa.

  • Tätä tekniikkaa ei esitellä tarkemmin muun muassa, koska sitä käytetään harvemmin, hyödyt ovat pieniä ja siitä saattaa olla enemmän haittaa kuin hyötyä ohjelman ymmärrettävyyden kannalta.

  • "I would not have the feature at all if I had to do it over."
    - Guido van Rossum, Python-kielen kehittäjä.

Korjasin virheen “enemmän hyötyä kuin haittaa”

12 Nov 19

10.10 Peli luvun arvaukseen

  • Pelin vapaamuotoinen versio voidaan muuttaa melko suoraviivaisesti muuttujia ja Pythonin funktioita käyttäväksi tarkemmaksi algoritmiksi.

    • Otetaan käyttöön muuttujat arvattavalle ja arvatulle luvulle.

    • Arvattava luku alustetaan satunnaisen kokonaisluvun tuottavalla funktiolla väliltä [1, 10].

      • Laajemmalla välillä saataisiin luonnollisesti vaikeampi peli.
    • Arvaus luetaan käyttäjältä input-operaatiolla.

    • Vinkit ja onnittelut viestitään print-operaatiolla.

      • Algoritmin käytettävyyttä parannetaan muutamalla lisätulosteella.
    • Päätökset ilmaistaan vertailuoperaattoreiden avulla.

10.11 Peli luvun arvaukseen esiehdolla

10.12 Laskurit

  • While-silmukkaa ohjataan laskurimuuttujan (counter) avulla, kun silmukan toistojen lukumäärä tunnetaan.

  • Voidaan nimetä lyhyesti ellei ilmeistä parempaa nimeä löydy.

    • Yleisimmät lyhyet nimet ovat matematiikasta tutut indeksien symbolit i, j, k ja l.
  • Laskurina käytetään yleisesti kokonaislukutyyppistä muuttujaa, jonka arvoon lisätään luku yksi jokaisella silmukan kierroksella.

    • Tällainen laskuri pitää kirjaa siitä montako kertaa toistaiseksi on silmukoitu.

    • Yhdellä kasvatettavan laskurin arvoa rajoitetaan kierrosten lukumäärällä, joka voidaan ilmaista literaalina tai muuttujana.

  • Tavanomaisen, yhdellä kasvavan laskurin arvon tulkinta riippuu alkuarvosta.

    • Alkuarvo 0: laskuri kertoo montako kertaa silmukka on suoritettu ehto-lauseketta arvioitaessa.

      i = 0
      # Ehdossa pienempi kuin operaattori,
      # koska laskurin alkuarvo on nolla.
      while i < 3:
          # Tulostaa arvot 0, 1 ja 2.
          print(i)
          i = i + 1
    • Alkuarvo 1: laskuri ilmaisee monesko silmukan kierros on alkamassa, kun ehtolausetta arvioidaan.

      i = 1
      # Ehdossa pienempi tai yhtä suuri
      # kuin operaattori, koska laskurin 
      # alkuarvo on yksi.
      while i <= 3:
          # Tulostaa arvot 1, 2 ja 3.
          print(i)
          i = i + 1
    • Ehtolausekkeen on oltava linjassa alkuarvon kanssa.

10.13 Laskurit (n_x_hello_world.py)

# Tervehditään käyttäjää ja kerrotaan 
# ohjelmasta.
print("Moi! Sanon monta kertaa moi.")
# Luetaan syöte käyttäjältä.
print("Anna moikkien lukumäärä:")
lkm = int(input())
# Alustetaan silmukan laskuri.
i = 0
# Tulostetaan haluttu määrä 
# tervehdyksiä silmukan avulla.
while i < lkm:
    # Tulostetaan tervehdys.
    print("Hello, World!")
    # Kasvatetaan laskuria.
    i = i + 1

10.14 Keskiarvon laskeminen

  • Päätellään tarvittavat muuttujat kaavasta \(\bar{x} = \displaystyle\sum^n_{i=1}\frac{x_i}{n},\quad n \gt 0\)

    • luku on luettu luku (termi \(x_i\)),

    • i on laskuri, josta selviää monesko silmukan kierros on alkamassa (termin indeksi \(i\)),

    • summa vastaa lukujen summaa (\(\sum x_i\)),

    • lkm vastaa lukujen lukumäärää (indeksin yläraja \(n\)) ja

    • ka on lukujen keskiarvo (\(\bar{x}\)).

    • Arvaus luetaan käyttäjältä input-operaatiolla.

  • Käyttäjän kanssa kommunikoidaan print- ja input-operaatioilla.

    • Algoritmin käytettävyyttä parannetaan lisätulosteilla.

10.15 Tyypillisiä ohjelmointivirheitä

  • Yleisin silmukoihin liittyvä ohjelmointivirhe on se, että toistojen todellinen lukumäärä poikkeaa tavalla tai toisella ajatellusta lukumäärästä.

  • Tällaisen looginen virhe voi olla

    • hankalasti löydettävä, jos silmukan viimeinen kierros jää suorittamatta tai silmukkaa suoritetaan yhden ylimääräisen kerran tai

    • helposti löydettävä, mutta ohjelman suorituksen kannalta katastrofaalinen, koska ohjelma joutuu ikuiseen silmukkaan.

  • Ikuiseen silmukkaan joutunut ohjelma voidaan pysäyttää CTRL-C-näppäinyhdistelmällä tai sulkemalla komentoikkuna.

  • Tyypillinen ikuiseen silmukkaan johtava virhe on silmukan laskurin päivityksen unohtuminen.
i = 0
# Tavoitteena kolme kierrosta.
# Tuloksena ikuinen silmukka.
while i < 3:
    # Tulostaa arvot 0, 0, 0...
    print(i)
  • Jos silmukka jää kierroksen "lyhyeksi" tai menee kierroksen "pitkäksi", on todennäköistä, että laskurin alkuarvo ja silmukan lopetusehto eivät ole keskenään linjassa.
i = 0
# Tavoitteena kolme kierrosta.
# Tuloksena yksi kierros liikaa.
while i <= 3:
    # Tulostaa arvot 0, 1, 2 ja 3.
    print(i)
    i = i + 1
i = 1
# Tavoitteena kolme kierrosta.
# Yksi kierros jää puuttumaan.
while i < 3:
    # Tulostaa arvot 0 ja 1.
    print(i)
    i = i + 1

10.16 Sisäkkäiset silmukat

  • Silmukoita voi olla sisäkkäin kaksi tai useampia.

    • Tällä kurssilla rajoitutaan pääasiassa yksinkertaisimpaan sisäkkäiseen toistorakenteeseen, jossa on yksi ulompi ja yksi sisempi silmukka.

    • Jos ulommassa silmukassa on m kierrosta ja sisemmässä silmukassa n kierrosta, niin sisempään silmukkaan liittyvät lauseet suoritetaan mn kertaa.

  • Pääsilmukka on kaikkein ulommaisin silmukka, jonka avulla ohjelmaan kaikkia toimintoja voidaan toistaa kunnes käyttäjä päättää lopettaa ohjelman.

  • Joissakin tehtävissä -- kuten arvojen lajittelussa ja taulukoiden käsittelyssä -- toistorakenteita yhdistetään tyypillisesti siten, että toistorakenne sisältyy toiseen toistorakenteeseen.

11. Loogiset operaattorit

11.1 Yleistä

Operaatio Merkintä
Konjunktio (looginen ja) and
Disjunktio (looginen tai) or
Negaatio (looginen ei) not
  • Operandit ovat totuusarvoiksi käyviä literaaleja tai lausekkeiden tuloksia.

  • Tulos on totuusarvoksi käyvä literaali.

  • Kurssilla käytetään selvyyden vuoksi loogisten operaattoreiden operandeina vain bool-tyypin arvoja True ja False, vaikka esimerkiksi 1 ja 0 ovat sallittuja.

  • Operaattorit määritellään totuustauluina, joissa annetaan operandien arvojen kaikki yhdistelmät ja arvoyhdistelmien tulokset.

11.2 Laskentajärjestys

  1. Aritmetiikka.

  2. Vertailut.

  3. Looginen not.

  4. Looginen and.

  5. Looginen or.

  6. Lopuksi mahdollinen sijoitus.

  • Vasemmalta oikealle.

  • Laskentajärjestystä voi muuttaa suluilla.

  • Sulkuja on joskus syytä käyttää selvyyden vuoksi.

  • And- ja Or- operaattorit operaatiot ovat ehdollisia: jos tulos saadaan selville jo ensimmäisestä operandista, niin toista operandia ei arvioida.

11.3 And

  • Olkoon A ja B totuusarvoja tai totuusarvoisia lausekkeita.

  • Lauseke A and B on tosi vain, kun A ja B ovat tosia.

A B A and B
False False False
True False False
False True False
True True True
# Syödään karkkia vain, jos on oikea
# päivä ja hampaat käyttökelpoisia.
if karkkipäivä and reikien_lkm < 2:
    karkkien_lkm = karkkien_lkm - 1
karkkipäivä = True
reikien_lkm = 1
karkkipäivä and reikien_lkm < 2 -->
True and 1 < 2 --> True and True -->
True
karkkipäivä = False
reikien_lkm = 1
karkkipäivä and reikien_lkm < 2 -->
False and 1 < 2 --> False

11.4 Or

  • Lauseke A or B on tosi, kun jompikumpi lauseke tai molemmat lausekkeet ovat tosia.
A B A or B
False False False
True False True
False True True
True True True
# Syödään karkkia, kun on karkki-
# päivä tai kun hampaat ovat käyttö-
# kunnossa tai karkkipäivänä, kun on
# hampaat ovat kunnossa.
if karkkipäivä or reikien_lkm < 2:
    karkkien_lkm = karkkien_lkm - 1
karkkipäivä = True
reikien_lkm = 1
karkkipäivä or reikien_lkm < 2 -->
True or 1 < 2 --> True
karkkipäivä = False
reikien_lkm = 1
karkkipäivä or reikien_lkm < 2 -->
False or 1 < 2 --> False or True --> True

11.5 Not

  • Lauseke not A on tosi silloin, kun lauseke A on epätosi.

  • Lauseke not A on epätosi silloin, kun lauseke A on tosi.

A not A
True False
False True
# Syödään karkkia vain, jos ei ole
# oikea päivä ja hampaat ovat
# käyttökelpoisia.
if not karkkipäivä and reikien_lkm < 2:
    karkkien_lkm = karkkien_lkm - 1
karkkipäivä = True
reikien_lkm = 1
not karkkipäivä and reikien_lkm < 2 -->
not True and 1 < 2 --> not True and True --> False and True --> False
karkkipäivä = False
reikien_lkm = 1
not karkkipäivä and reikien_lkm < 2 -->
not False and 1 < 2 --> not False and True --> True and True --> True

11.6 Ohjausrakenteissa

  • Loogisilla operaatioilla yhdistetyistä lausekkeista voidaan muodostaa monimutkaisempia ehtoja, joiden avulla voidaan puolestaan vähentää sisäkkäisiä rakenteita.
...
# Sisäkkäiset rakenteet.
if viikonpäivä == "ma":
    if monesko == 13:
        print("2 x huono päivä.")

# Ehdot yhdistämällä voidaan poistaa
# sisempi if-lause.
if viikonpäivä == "ma" and monesko == 13:
    print("2 x huono päivä.")
...
...
# Sisäkkäiset rakenteet.
if viikonpäivä == "ma":
    print("Huono päivä.")
elif monesko == 13:
    print("Huono päivä.")

# Ehdot yhdistämällä voidaan poistaa
# elif-osa.
if viikonpäivä == "ma" or monesko == 13:
    print("Huono päivä.")
...
  • Monimutkaisia lausekkeita tulee välttää ja kommentoida.

  • Lausekkeita voidaan selventää ylimääräisiä sulkeilla käyttämällä.

...
if (viikonpäivä == "ma") and (monesko == 13):
    print("2 x huono päivä.")
if (viikonpäivä == "ma") or (monesko == 13):
    print("Huono päivä.")
...
  • Älä käytä ylimääräisiä sulkeita tarpeettomasi muissa yhteyksissä. Ne vain hämäävät ohjelmasi lukijaa.
# Tästä suljeparista on lähinnä haittaa lukijalle. Älä tee näin!
i = (i + 1)

11.7 Bittioperaatorit

  • Myös Python-kielessä on and-, or ja not-operaattorit (&, | ja ~) kokonaislukujen käsittelyyn bittien tasolla.

  • Näitä operaattoreita on syytä välttää, vaikka ne toimivat True- ja False-arvojen kokonaislukuvastineiden 1 ja 0 ansiosta loogisesti oikein, koska Pythonissa on omat operaattorit logiikalle.

  • Biteittäinen xor (^) on poikkeus, koska poissulkevan disjunktion looginen versio puuttuu Pythonista.

    • Xor voidaan esittää myös erisuuruusoperaattorin avulla.
A B A ^ B
False False False
True False True
False True True
True True False

12. Hyvä ohjelmointitapa

12.1 Yleistä

  • Ohjelman elinkaari ei tyypillisesti pääty sen toteuttamiseen; ohjelmaa voidaan käyttää ja ylläpitää jopa vuosikymmeniä.

  • Jotta koodin muuttaminen on mahdollista, sen on oltava muidenkin kuin tekijänsä ymmärrettävissä.

  • Hyvää ohjelmointitapaa noudattamalla saadaan aikaiseksi ymmärrettäviä ja hallittavia ohjelmia.

  • Perusasioita:

    • Nimeä muuttujat järkevästi.

    • Kommentoi riittävästi ja oikeissa paikoissa.

    • Sisennä koodia.

    • Rivitys: vältä liian pitkiä rivejä, käytä välirivejä ja käytä välilyöntejä riveillä.

    • Käytä vakioita.

12.2 Nimeä järkevästi

  • Tunnusten (nimien) tulee olla järkeviä.

  • Nimestä tulisi voida päätellä muuttujan tehtävä ohjelmassa ja muuttujan sisältämän tiedon luonne.

  • Usein hyvä nimi on yhtä kuin riittävän pitkä nimi.

    • Laskurit saa nimetä lyhyemmin, kun ilmeistä hyvää nimeä on vaikea keksiä.
  • Noudata kielen nimeämiskäytäntöjä johdonmukaisesti.

    • Pythonissa muuttujien nimet annetaan pienin kirjaimin.

    • Sanat erotetaan toisistaan alaviivoilla.

      • Yhdyssanat kirjoitetaan yhteen samoin kuin luonnollisessa kielessä.
  • Käytä tunnuksissa mahdollisimman pitkälti vain yhtä kieltä.

    • Tunnusten on oltava käytännössä joko suomeksi tai englanniksi.

    • Luentomateriaalissa pyritään yhtenäisesti suomenkielisiin tunnuksiin. Annetut ohjelmatiedostot nimetään siten ettei niissä ole å-, ä- tai ö-kirjaimia, jotta niitä on helppo siirtää järjestelmien välillä.

  • Ohjelman muuttujat ja kommentit saa kirjoittaa suomenkielellä, vaikka ohjelman käyttöliittymässä käytettäisiin englannin kieltä.

  • Kirjoita ohjelmasi täysin englanniksi (tunnukset, kommentit ja käyttöliittymä), jos on mahdollista, että siihen on kansainvälistä kiinnostusta.

12.3 Kommentoi

  • Kommentit kannattaa kohdistaa erityisesti koodin keskeisiin osiin ja vaikeasti ymmärrettäviin osiin.

    • Varsinkin pitemmässä ohjelmassa ei tarvitse kommentoida kaikkea.

    • Harva laajempi ohjelma on niin selkeä, että kommentteja ei tarvita. Kommentit puuttuvat yleensä siksi ettei niille ole aikaa tai kirjoittaja on vain laiska.

    • Kurssin materiaalissa on kommentteja, jotka ovat käytännössä turhia, jotta koodi olisi aloittelijalle selkeämpää.

  • Ohjelman alkuun kannattaa kirjoittaa kommentti, josta käy ilmi mitä ohjelma tekee, kuka ohjelman teki ja yhteystietoja esimerkiksi sähköpostin muodossa.

  • Muista päivittää kommentit ohjelman muuttuessa.

12.4 Sisennä

  • Sisennys auttaa hahmottamaan kokonaisuuksia.

  • Sisennyksellä osoitetaan Pythonissa lauseiden looginen ja kieliopillinen yhteenkuuluvuus.

  • Sisennä Python-lähdekoodi välilyönnein.

    • Kukin sisennyksen taso on neljä välilyöntiä.

    • Sekä tavalliset lauseet että kommentit samalle tasolle.

  • Ole tarkkana valintarakenteissa: muista sisentää esimerkiksi if- ja else-osiin kuuluvat kommentit ja lauseet samalle tasolle.

12.5 Rivitys

  • Osoita lauseiden "teemoittainenˮ yhteen-kuuluvuus kootun lauseen sisällä välirivejä käyttämällä.

    • Erota teemat yhdellä välirivillä.

    • Pythonin tyyliopas ohjeistaa käyttämään välirivejä säästeliääsi.

      • Esimerkeissä ja mallivastauksissa on selkeyden vuoksi normaalia enemmän erottavia rivejä.
...
# Tulostussilmukan laskuri.
rivi = 0
    
# Tulostetaan rivit.
while rivi < korkeus:
   # Muodostetaan rivi ja
   # tulostetaan se.
   print(leveys * piirtomerkki)
   # Kasvatetaan laskuria.
   rivi = rivi + 1

# Jatkokyselyä ohjaava lippu.
väärä_vastaus = True
...
  • Pitkät rivit ovat vaikeaselkoisia ja näkyvät vain osin pienessä ikkunassa.

  • Jaa liian pitkä rivi kahdeksi lauseeksi kahdelle riville tai katkaise rivi, jolloin yksi lause on kahdella rivillä.

    • Pythonin tyylioppaassa rivin maksimimitaksi annetaan 79 merkkiä.

      • Kurssilla yli 100 merkin mittaiset ovat liian pitkiä.
    • Rivien katkonnasta kerrotaan myöhemmin.

  • Paranna yksittäisten rivien luettavuutta käyttämällä välilyöntejä.

    • Yksi välilyönti ennen ja jälkeen operaattorin.

    • Ei välilyöntiä avaavan sulkeen jälkeen eikä ennen sulkevaa suljetta.

# Ei näin...
i=1
i=(j+1)*2
    
# ...vaan näin...
i = 1
i = (j + 1) * 2

# ...tai näin, jos halutaan
# ilmaista, että yhteenlasku
# on kertolaskua heikompi
# operaattori.
i = 1
i = (j+1) * 2

12.6 Vakiot

  • Vakiot (constant) ovat muuttujia, joiden arvo ei ole tarkoitettu muutettavaksi alustamisen jälkeen.

  • Vakioiden tunnus kirjoitetaan ISOIN KIRJAIMIN, jotta ne erottuvat paremmin muuttujista.

    • Pythonissa ei ole valitettavasti määrettä, jolla arvon myöhempi muutos voitaisiin estää. Isoin kirjaimin annetut muuttujat on siksi vain muistettava jättää rauhaan.
  • PII = 3.14

  • LOPETUSKOMENTO = "e"

  • LYHYET_JÄÄHYVÄISET = "Ohjelma lopetettu."

  • Vakiot määritellään yleensä ohjelman alussa ennen muita lauseita.

  • Helpottavat ohjelmien ylläpitoa.

    • Luonnollisesti hyödyllisiä, kun muuttujan arvo on suojattava muutoksilta ylipäätänsä.

    • Vakiolla voi korvata monessa kohtaa lähdekoodissa esiintyvän literaalin, jolloin literaalin voi muuttaa kätevästi vain muuttamalla vakion alustuslausetta.

    • Ohjelman käyttöliittymän muokkaaminen on helpompaa, kun ohjelman tuntemat komennot samoin kuin sen tulostamat viestit vakioidaan .

  • Osa hyvää ohjelmointitapaa.

  • Oheisessa esimerkissä vakion ansiosta piin likiarvon muuttuessa tarvitsee muuttaa vain yhtä lausetta.

...
// 3.14 literaalina.
ympyrän_ala = 3.14 * r ** 2
ympyrän_keha = 2 * 3.14 * r
pallon_ala = 4 * 3.14 * r ** 2
...
...
// Arvo 3.14 vakiona.
PII = 3.14
ala = PII * r * r
keha = 2 * PII * r
pallon_ala = 4 * PII * r ** 2
...

13. Pythonin tietorakenteita

13.1 Yleistä

  • Tietorakenne (data structure) on järjestelmällinen esitysmuoto, joka mahdollistaa tietoalkioiden tehokkaan saannin ja muuttamisen.

  • Eräs Pythonin vahvuus ovat useat kieleen sisäänrakennetut tietorakenteet.

    • Esimerkiksi Javassa voi käyttää suoraan vain taulukoita.
  • Tällä kurssilla käsitellään joitakin sekvenssejä (sequence) ja sanakirjaa (dictionary).

  • Sekvenssi on peräkkäisten alkioiden kokoelma.

    • Sekvenssit luokitellaan muuttumattomiksi (immutable) ja muuttuviksi (mutable) sen mukaan voidaanko sekvenssin alkioita muuttaa sekvenssin luomisen jälkeen.

13.2 Lukujono

  • Lukujono (range) on kokonaislukujen äärellinen aritmeettinen jono.

    • Peräkkäisen alkioiden väli \(d = a_{i+1} - a_i\) on vakio, missä \(i = 0, 1, ...\)

    • Alkio \(a_i = a_0 + d \cdot (i - 1)\)

    • Esimerkiksi jonossa 2, 4, 6, 8 väli \(d\) on 2.

  • Muodostetaan kolmeparametrisella range-funktiolla.

    • start: jonon ensimmäinen alkio \(a_0\). Nolla, jos määrittelemättä.

    • stop: jonon viimeisen alkion päättelyyn käytettävä arvo.

    • step: väli \(d\). Jos \(d > 0\), \(a_i = a_0 + d \cdot i\), missä \(i \geq 0\) ja \(a_i <\) stop. Jos \(d < 0\), \(a_i = a_0 + d \cdot i\), missä \(i \geq 0\) ja \(a_i >\) stop. Lukujono on tyhjä, jos \(a_0\) ei täytä epäyhtälöä. Väli on yksi, jos se jätetään määrittelemättä. Väli ei voi olla nolla.

  • Funktion peruskäyttötapaus on yksiparametrinen kutsu, jolloin jono alkaa nollasta ja koostuu peräkkäistä kokonaisluvuista parametriarvo pois lukien.

    • Esimerkiksi range(5) tuottaa jonon 0, 1, 2, 3, 4.
# 1, 2, 3, 4, 5
range(1, 6)
# 2, 4, 6, 8
range(2, 9, 2)
# 7, 20, 33, 46, 59, 72, 85, 98
range(7, 100, 13)
# -3, -2, -1, 0, 1
range(-3, 2)
# 5, 4, 3, 2, 1
range(5, 0, -1)
# 1, -1, -3
range(1, -5, -2)
# Tyhjä väli.
range(1, 1)
range(7, 3)
# Ajonaikainen virhe ValueError
range(1, 10, 0)
  • Vaikka lukujono itsessään on muuttumaton sekvenssi, sitä käytetään harvoin pääasiallisena tietorakenteena.

  • Lukujonoja käytetään tyypillisimmin silmukoinnin yhteydessä.

    • Jonon alkiot voivat toimia laskurin tapaan ja jonolla voidaan tuottaa tietorakenteen alkioita yksilöiviä indeksiarvoja.

      • Lukujono on näissä tehtävissä tehokas, koska se ei säilö alkioitaan, vaan laskee aina tarvittaessa uuden alkion arvon yhtälön avulla.

13.3 For-silmukka

  • Pythonin for-silmukka on tehty sekvenssien tapaisten iteroitavien rakenteiden käsittelyyn.

  • Silmukan otsikkorivillä esitellään muuttuja, johon sijoitetaan automaattisesti otsikkorivillä määritellyn sekvenssin seuraava alkio.

    • Lippua tai laskuria ei voi käyttää ehdossa; ehto-lauseke annetaan aina avainsanaa in käyttäen.
for muuttuja in sekvenssi:
   ...
  • Toistettavat lauseet sidotaan rakenteeseen sisentämällä.

    • Muista neljä välilyöntiä.
  • Pysäytettävä break-lauseella, jos kaikkia alkioita ei haluta käsitellä.

    • Continue aloittaa välittömästi seuraavan alkion käsittelevän kierroksen

13.4 Karkausvuodet1.py ja karkausvuodet2.py

...
vuosi = eka_vuosi
...
while vuosi <= vika_vuosi:
    if ...:
        print(vuosi)
    vuosi = vuosi + 1
...
for vuosi in range(eka_vuosi, vika_vuosi + 1):
    if ...:
        print(vuosi)

13.5 Merkkijonot

  • Muuttumattomia sekvenssejä, joista opitaan seuraavaksi erityisesti kuinka merkkijonosta voidaan valita yksittäisiä merkkejä ja osia []-notaatiolla ja indeksiarvoilla.

    • Valinta ja merkkijonojen yhteydessä esitellyt funktiot toimivat myös lukujonojen ja myöhemmin esiteltävien sekvenssien yhteydessä.
  • Python indeksoi sekvenssit nollasta alkavin kokonaisluvuin.

    • Viimeinen indeksiarvo on \(n- 1\), kun sekvenssissä on \(n\) kappaletta tietoalkioita.

    • Sekvenssin pituus voidaan selvittää len-funktiolla.

    • Myös negatiiviset (\(< 0\)) indeksiarvot ovat mahdollisia; Python lisää niihin automaattisesti sekvenssin pituuden.

  • Indeksointia havainnollistaa oheinen taulukko, jossa on annettu merkkijonon "Python" merkkien indeksit.

    • Ensimmäisen merkin indeksi on 0 tai -6, koska len(\"Python\") + (-6) = 6 - 6 = 0.

    • Viimeisen merkin indeksi on 5 tai -1, koska len(\"Python\") + (-1) = 6 - 1 = 5.

P y t h o n
0 1 2 3 4 5
-6 -5 -4 -3 -2 -1
  • Sekvenssistä poimitaan yksittäinen alkio lisäämällä sekvenssin nimen perään hakasuljepari ja yksilöimällä alkio indeksiarvolla, joka sijoitetaan parametrin tapaan sulkeiden väliin.

    • Virheellinen indeksiarvo aiheuttaa ajonaikaisen virheen IndexError.
  • Merkkijonon merkkejä ei voi muuttaa.

merkit = "Python"
# "P"
print(merkit[0])
# vika = "n"
vika = merkit[-1]
print(vika)
# Indeksiarvo 6: IndexError
merkit[len(merkit)]
# Indeksiarvo -7: IndexError
merkit[-len(merkit) - 1]
# TypeError: 'str' object does
# not support item assignment
merkit[0] = "T"
  • Sekvenssistä poimitaan useampia tietoalkioita kerrallaan [::]-notaatiolla "viipaloimalla" (slicing).

    • Valitut alkiot ovat peräkkäin tai seuraavat toisiaan tasavälein.
  • Indeksiarvoja käytetään viipaloinnissa pitkälti range-funktion parametrien tapaan: [start:stop:step].

    1. start: ensimmäisen viipaleeseen kuuluvan tietoalkion indeksi. Nolla, jos ei määritellä. Sekvenssin pituus \(n\), jos suurempi kuin \(n\) ja nolla, jos pienempi kuin \(-n - 1\).

    2. stop: ensimmäisen viipaleesta pois jäävän tietoalkion indeksi. \(n\), jos ei määritellä tai suurempi kuin \(n\). Nolla, jos pienempi kuin \(-n - 1\).

    3. step: viipaleeseen valittavien tietoalkioiden väli. Yksi, jos määrittelemättä.

  • Koko sekvenssin viipalointi kopioi sekvenssin.

merkit = "Python"
# "tho"
merkit[2:5]
merkit[-4:-1]
# "P"
merkit[0:1]
merkit[-6:-5]
# Valitaan 3. merkkiin saakka:
# "Pyt"
merkit[:3]
merkit[:-3]
# Valitaan 4. merkistä alkaen:
# "hon"
merkit[3:]
merkit[-3:]
# Valitaan kaikki: "Python"
merkit[:]
# Valitaan parillisissa 
# paikoissa olevat merkit: "Pto"
merkit[::2]
# Kääntäen: "nohtyP"
merkit[::-1]
# 2.-4. merkit kääntäen: "oht"
merkit[4:1:-1]
# Tyhjä merkkijono "".
merkit[4:1]
# Liian pieni tai suuri indeksi 
# ei kaada ohjelmaa, vaan niille
# annetaan arvot 0 ja 6: "Python"
merkit[-100:100]
  • In-avainsanalla saadaan myös selville onko tietty alkio sekvenssissä.

    • Toisin kuin useimmissa sekvensseissä, merkkijonoista voidaan hakea osia (osamerkki-jonoja).
  • Min- ja max-funktiot palauttavat vastaavasti sekvenssin pienimmän ja suurimman alkion.

merkit = "Python"
# True ja False
"P" in "Python"
"P" not in "Python"
# Pieni ja suuri kirjain ovat 
# eri merkit: False
"p" in "Python"
# True
"on" in "Python"
# Merkkijonon min-funktio 
# palauttaa pienimmän Unicode-
# koodin omaavan merkin: "P"
min("Python")
# Merkkijonon max-funktio 
# palauttaa suurimman Unicode-
# koodin omaavan merkin: "y"
max("Python")

13.6 Listat

  • Lista (list) on muuttuva sekvenssi, joka luomiseen on eri tapoja:

    • Tyhjä hakasulkeiden pari [] luo tyhjän listan.

    • Hakasulkeissa oleva yksittäinen tietoalkio tai hakasulkeissa olevat pilkulla toisistaan erotetut tiedot ovat lista.

    • Parametriton list-funktion kutsu luo ja palauttaa tyhjän listan.

    • List-funktio luo parametrinaan saamastaan iteroitavasta rakenteesta listan ja palauttaa sen.

lista1 = []
lista2 = list()
alkuaineita = ["kupari", "hopea", "kulta", "rauta"]
# [1, 3, 5, 7, 9]
parittomia_luonnollisia_lukuja = list(range(1, 10, 2))
# ["P", "y", "t", "h", "o", "n"]
merkit_listana = list("Python")
  • Lista on tarkoitettu homogeenisten alkioiden säilömiseen.

    • Pythonin dokumentaatiossa homogeenisuutta ei määritellä, vaan todetaan, että tehtävä määrää samankaltaisuuden asteen.

    • Kurssilla alkioiden samankaltaisuutta mitataan tyypillä.

    • Listoja saatetaan hyödyntää toisin kuin alunperin on ajateltu, koska erityyppisten alkioiden käyttöön ei ole teknisiä esteitä.

  • Kaikki merkkijonojen yhteydessä esitellyt sekvenssien yhteiset operaattorit, avainsanat ja notaatiot ovat saatavilla.

    • Valinnat []- ja [::]-notaatioilla palauttavat vastaavasti listan alkio arvon ja listan.
  • Ilmeisin ero aiemmin esiteltyihin sekvensseihin on se, että listaa voidaan muuttaa korvaamalla valitut alkiot sijoitusoperaattorilla.

  • []-notaatiolla korvattavaksi valitun alkion paikalle sijoitetaan yksittäinen arvo.

  • Viipaloimalla määriteltyjä alkioita korvattaessa sijoitus-operaattorin toiseksi operandiksi annetaan korvaavat alkiot sisältävä iteroitava rakenne, joka on tällä kurssilla aina lista.

    • Korvaavalla listalla voi olla eri määrä alkioita kuin viipaleessa, paitsi kun viipale on määritelty väliä käyttäen.
# Korvataan viimeinen alkio.
# ["kupari", "hopea", "kulta", "platina"]
alkuaineita[-1] = "platina"
# Korvataan 2. ja 3. alkio eli "hopea" ja "kulta" tiedolla 
# "vety". ["kupari", "vety", "platina"]
alkuaineita[1:3] = ["vety"]
  • Tiedot lisätään sijoitettaessa listalle alkioita korvaamatta, kun viipale määritellään siten, että ensimmäisellä ja toisella parametrilla on sama arvo.

    • Tieto lisätään loppuun, kun arvot ovat n, missä n on listan pituus.
# Lisätään kaksi uutta alkiota listan alkuun.
# ["kalsium", "argon", "kupari", "vety", "platina"]
alkuaineita[0:0] = ["kalsium", "argon"]
# Lisätään uusi alkio listan keskimmäiseen paikkaan. Lisäys-
# paikassa oleva alkio ja sitä seuraavat alkiot seuraavat
# lisättyä alkiota. ["kalsium", "argon", "sinkki",
# "kupari", "vety", "platina"]
alkuaineita[2:2] = ["sinkki"]
# Lisätään uusi tieto listan viimeiseksi alkioksi.
alkuaineita[len(alkuaineita):len(alkuaineita)] = ["alumiini"]
  • Yhteenlasku- ja kerto-operaattorit + ja * toimivat samoin kuin merkkijonojen yhteydessä.

    • Operaattorin paluuarvo on sijoitettava muuttujaan, jos uutta listaa tarvitaan myöhemmin.

      • Lisäykset voidaan tehdä vaihtoehtoisesti lisäysoperaattorilla, joka on kätevämpi listan alkuun ja loppuun lisättäessä.

      • Myöhemmin olioita tarkasteltaessa nähdään tarkemmin mitä tapahtuu, kun sijoitusoperaattoria käytetään sekvenssien ja muiden kurssilla esiteltyjen tyyppien kanssa.

  • Del-lause poistaa []- ja [::]-notaatiolla määritellyt alkiot.

    • Viipaloimalla valitut alkiot voidaan poistaa myös sijoittamalla viipaleeseen tyhjä lista [].
# Alustetaan listatyyppinen muuttuja. Lista on homogeeninen;
# se sisältää samantyyppisiä alkioita Huomaa, että tarkasti ottaen 
# kyseessä on sekvenssien sekvenssi.
kieliä = ["Java", "Python", "C"]
# Korvataan viimeinen alkio: ["Java", "Python", "C++"]
kieliä[-1] = "C++"
# Korvataan 1. ja 2. alkio: ["Python", "Java", "C++"]
kieliä[:2] = ["Python", "Java"]
# Lisätään listan loppuun uusi alkio. Uusi lista sijoitetaan
# samaan muuttujaan, jonon vanha lista liittyi, jotta esimerkissä
# voidaan jatkaa saman muuttujan käyttöä. Uuden listan alkiot:
# ["Python", "Java", "C++", "Haskell"]
kieliä = kieliä + ["Haskell"]
# Alkion lisääminen epäonnistuu, koska 2. operandi ei ole lista.
# Sekvenssien yhteenlaskussa operandien on oltava samantyyppistä. 
kieliä = kieliä + "JavaScript"
# 2. ja 4. alkion korvaaminen epäonnistuu, koska väliä käytettäessä 
# korvattavia ja korvaavia alkioita on oltava sama määrä.
kieliä[1:4:2] = ["Scala"]
# Lisätään listan toiseksi viimeiseksi alkioksi "Perl".
# ["Python", "Java", "C++", "Perl", "Haskell"]
kieliä[len(kieliä) - 1:len(kieliä) - 1] = ["Perl"]
# Korvataan kaikki alkiot yhdellä uudella alkiolla: ["JavaScript"]
kieliä[:] = ["JavaScript"]
# Samanarvoinen alkio voi esiintyä listalla useasti:
# ["JavaScript", "JavaScript", "JavaScript"]
kieliä = 3 * kieliä
# Poistetaan listan ensimmäinen alkio: ["JavaScript", "JavaScript"]
del kieliä[0]
# Tyhjennetään lista. Myös: del kieliä[:].
kieliä[:] = []

13.7 Tuplat

  • Tupla (tuple) on listan muuttumaton versio.

    • Tuplan alkioita ei voida korvata eikä alkioita lisätä tai poistaa.

    • Valitsemalla ja yhteenlaskuoperaattorilla laskettaessa muodostuu uusi tupla toisin kuin listojen yhteydessä.

    • Tuplan alkiot voivat olla muuttuvia.

  • Tupla on ajateltu heterogeenisten alkioiden tallentamiseen.

    • Tuplaa käytetään myös, kun tarvitaan muuttumaton heterogeenisten alkioiden sekvenssi.

    • Teknisesti lista ja tupla eroavat toisistaan muuttuvuuden osalta.

  • Tupla luodaan samoin periaattein kuin lista.

    • Tyhjä kaarisulkeiden pari () luo tyhjän listan.

    • Pilkulla toisistaan erotetut tiedot ovat lista.

      • Kaarisulkeita voidaan käyttää selvyyden vuoksi.

      • Yhden alkion mittainen tupla luodaan antamalla tieto ja pilkku.

    • Parametriton tuple-funktion kutsu luo ja palauttaa tyhjän listan.

    • Tuple-funktio luo parametrinaan saamastaan iteroitavasta rakenteesta tuplan ja palauttaa sen.

# ("one",)
ainokainen = "one",
# (13, 42, 3.14). Myös: lukuja = (13, 42, 3.14)
lukuja = 13, 42, 3.14
# ("ni", -1, 1.23, ["yes", "way"])
sekalaista = "ni", -1, 1.23, ["yes", "way"]
alkuaineita = ("kupari", "hopea", "kulta", "rauta")
# Valitaan toinen alkio eli "hopea".
toinen = alkuaineita[1]
# Muodostetaan uusi tupla, jolta puuttuu "hopea"-alkio ja liitetään 
# uusi tupla muuttujaan. []-notaatiolla valittu yksittäinen alkio
# on muutettava tuplaksi, jotta +-operaattorin operandit ovat
# samaa tyyppiä. Tuple-funktio ei sovellu tähän tehtävään, koska se
# muuntaisi ensimmäisen alkion merkit alkioiksi.
alkuaineita = (alkuaineita[0],) + alkuaineita[2:]
# False. Alkio "hopea" ei ole uudella listalla.
"hopea" in alkuaineita
# Tuplan alkioita ei voi korvata: TypeError: 'tuple' object does 
# not support item assignment
alkuaineita[1] = "alumiini"
  • Sekvenssin purkaminen (unpacking) tarkoittaa sijoitusta, jossa operaattorin vasemmalla puolella on yhtä monta pilkulla erotettua muuttujaa kuin oikealla puolella olevassa sekvenssissä on alkioita.

    • Pythonin monisijoitus on tuplan purkamista muuttujiin.
# Tuplan (1, 2) ensimmäinen alkio 1 sijoitetaan muuttujaan eka
# ja toinen alkio 2 muuttujaan toka.
eka, toka = 1, 2
print(eka)  # 1
print(toka) # 2
# Monisijoituksella on helppo vaihtaa kahden muuttujan arvot
# päikseen.
toka, eka = eka, toka
print(eka)  # 2
print(toka) # 1

13.8 Sanakirjat

  • Sanakirjan (dict) alkiot ovat avain--arvo-pareja.

    • Sanakirja ei ole peräkkäisten alkioiden sekvenssi, vaan assosiatiivinen rakenne, jossa järjestyksellä ei ole juuri merkitystä.
  • Sana kirja on muuttuva rakenne.

  • Avainten on oltava muuttumattomia.

    • Tuplaa voidaan käyttää avaimena, mutta sen alkioiden on oltava muuttumattomia.

    • Avainten ei tarvitse olla samaa tyyppiä. Myös arvot voivat olla keskenään erityyppisiä.

  • Avain--arvo-pari on muotoa avain:arvo.

  • Sanakirjojen luomiseen on useita menetelmiä. Esimerkiksi:

    • Aaltosulkeiden pari {} tai parametriton dict-funktion kutsu luo tyhjän sanakirjan.

    • Aaltosulkeissa oleva yksittäinen avain--arvo-pari tai aaltosulkeissa olevat pilkulla toisistaan erotetut parit muodostavat sanakirjan.

    • Dict-funktiolle voidaan antaa parit määrittelevien tuplien sekvenssi.

lukusanat = { 1: "yksi", 2: "kaksi", 3: "kolme" }
# Luodaan sanakirja tuplista ("hei", "hi") ja ("moi", "hi").
# {"hei": "hi", "moi": "hi"}
suomi_englanti_sanakirja = dict((("hei", "hi"), ("moi", "hi")))
  • Pari lisätään sanakirjaan []-notaatiolla ja sijoittamalla: hakasulkeiden sisään annetaan avain ja sitten sijoitetaan arvo.

    • Samalla menetelmällä päivitetään arvo.
  • Sanakirjasta haetaan tietoa []-notaatiolla sijoittamalla hakasulkeiden sisään annetaan avain.

    • Avaimen puuttuessa sanakirjasta tapahtuu ajonaikainen virhe KeyError, joka voidaan välttää avainsanalla in.
  • Pari poistetaan del-lauseella ja []-notaatiolla.

    • KeyError, jos avain puuttuu.
# Lisätään kaksi luku - sana -paria.
lukusanat[4] = "nelja"
lukusanat[5] = "viisi"
# Lasketaan parien määrä.
len(lukusanat) # 5
# {1: "yksi", 2: "kaksi", 3: "kolme", 4: "neljä", 5: "viisi"}
print(lukusanat)
# Korjataan typo.
lukusanat[4] = "neljä"
# Haetaan päivitetty arvo.
print(lukusanat[4]) # neljä
# Poistetaan pari.
del lukusanat[5]
# Avain ja sen arvo on poistunut.
5 in lukusanat # False
len(lukusanat) # 4

14. Oliot

14.1 Yleistä

  • Python esittää kaikki tiedot olioina (object).

  • Oliot ovat luokkansa (class) ilmentymiä.

    • Luokka mallintaa käsitettä, jollaisia ovat muun muassa kissa, koira tai sekvenssi.

    • Luokka on ohjelmointikielen tietotyyppi, joka määrittelee mitä tietoja (muuttujia) ja toimintoja (funktioita) luokan olioilla on.

    • Esimerkiksi kissaa mallintavalla luokalla voisi olla muuttuja painolle ja funktio, joka tulostaa näytölle naukumista, jolloin kaikilla kissaolioilla on oma paino ja kaikki kissaoliot osaavat naukua.

  • Python-luokkien muuttujia kutsutaan attribuuteiksi ja funktioita metodeiksi (method).

  • Kurssin jatkon kannalta olio-ohjelmoinnista on tarpeen tietää lähinnä vain,

    • että erityisesti muuttujien arvot ovat olioita,

    • muuttujien nimet (tunnukset) ovat olioihin sijoituksella liitettäviä viitteitä (reference), joita voi liittyä yhteen olioon useampia ja

    • kuinka olion metodeja voidaan kutsua pistenotaatiolla olion kautta.

# Liitetään olioon 42 viite 
# sijoittamalla. Olio on 
# int-tyyppinen.
luku = 42
# Olion tyyppi voidaan
# selvittää type-funktiolla.
# <class 'int'>
type(luku)

14.2 Sijoitus

  • Muuttujaa toiseen sijoitettaessa sijoitus-operaattorin ensimmäinen operandi liitetään olioon, johon toinen operandi liittyy.

    • Sijoitettaessa olion sisältöä ei kopioida toiseen olioon; vain viiteen kohde vaihtuu.

    • Sijoitus irrottaa ensimmäisen operandin oliostaan, jos operandiin liittyy sellainen.

# Olioon 42 liitetään toinen
# viite.
kaiken_tarkoitus = luku

42 on vastaus elämään, universumiin ja kaikkeen, ei tarkoitus. Yleensä se sekoitetaan jostain syystä elämän tarkoitukseen (ehkä Adamsin “The Meaning of LifF”, tai Pythonin “The Meaning of Life”); “kaiken tarkoitus” on uusi minulle. Piti huomauttaa, kun aikaisemmat referenssit on olleet täsmällisiä

12 Nov 19 (edited 12 Nov 19)

14.3 Identiteetti

  • Oliolla on identiteetti (identity), joka erottaa oliot toisistaan.

    • CPythonissa identiteetti on olion keskusmuistipaikka.
  • Id-funktio palauttaa parametrinaan saamaansa viitteeseen liittyvä olion identiteetin.

  • Avainsanalla is saadaan selville liittyvätkö viitteet samaan olioon.

    • Toisin sanoen tutkitaan onko viitteisiin liittyvien olioiden identiteetti sama.
# Viitteet liittyvät samaan olioon. 
id(luku) # 1633773376
id(kaiken_tarkoitus) # 1633773376
luku is kaiken_tarkoitus # True
luku is not kaiken_tarkoitus # False
  • Olioiden identiteetti ja sisältö ovat eri asia.

    • Esimerkiksi samanpainoiset kissat ovat eri olioita.
  • Vertailuoperaattorit, erityisesti ==, vertailevat olioiden sisältöä.

    • Python optimoi ohjelman muistinkäyttöä siten, että se liittää viitteitä samoihin pieniarvoisiin muuttumattomiin olioihin automaattisesti ilman sijoitusta.
luku1 = 42
luku2 = 42
# Viitteet liittyvät samaan 
# olioon, vaikka muuttujia
# ei ole sijoitettu 
# keskenään.
luku1 is luku2 # True
luku1 == luku2 # True
luku1 = 2147483647
luku2 = 2147483647 
# Viitteet liittyvät eri 
# olioihin, joiden sisältö 
# on sama.
luku1 is luku2 # False
luku1 == luku2 # True

14.4 Muuttuvat oliot ja sijoitus

  • Kokonaislukujen kaltaisia muuttumattomia olioita voidaan käsitellä huoletta minkä tahansa siihen liittyvän viitteen kautta.

  • Muuttuvien olioiden kanssa toimittaessa on muistettava, että olion sisältö muuttuu myös muidenkin kuin olioon alun perin liittyneen viitteen kautta.

# Liitetään viite muuttuvaan
# olioon.
eka = [1, 3, 4]
# Liitetään toka-viite eka-
# viitteen olioon.
toka = eka
# Viitteiden jakamaa muuttuvaa 
# oliota voidaan muuttaa minkä
# tahansa siihen liittyvää 
# viitettä käyttäen.
toka[-1] = 5
# [1, 3, 5]
print(eka)
# [1, 3, 5]
print(toka)

14.5 Yhteenlaskuoperaattori

  • Yhteenlasku luo uuden olion ja palauttaa siihen liittyvän viitteen.

  • Tulosolioon on sijoitettava viite, jos oliota halutaan käyttää myöhemmin.

# a)
luku1 = 1000
# b)
luku2 = 1
# c) luku1 + luku2
# d) summa = c)
summa = luku1 + luku2

14.6 Metodit

  • Luokassa määriteltyjä metodeja voidaan kutsua viitteellä yksilöidyn olion kautta pistenotaatiolla.

    • Metodin kutsu on muodoltaan erilainen kuin funktion kutsu, mutta molemmissa kutsutavoissa toiminto kohdistuu olioon.

    • Metodia kutsuttaessa on muistettava edelleen antaa oikea määrä oikeantyyppisiä parametriarvoja järjestys huomioiden.

    • Funktion tapaan metodi voi palauttaa arvon.

# Kutsutaan metodia, joka luo kohteestaan uuden merkkijono-
# olion, joka kirjainmerkit on muutettu isoiksi kirjaimiksi.
# Uuteen olioon liitetään uusi viite.
kirjaimia = "abc"
isoja_kirjaimia = kirjaimia.upper()
print(isoja_kirjaimia) # "ABC"
  • Kaikilla sekvensseillä on index- ja count-metodit.

kieli = "Java"
haettava = "a"
# Vältetään ohjelman pysäyttävä virhe tehottomalla tavalla.
if kieli.count(haettava) > 0:
    eka_ind = kieli.index(haettava)
    print(eka_ind) # 1
# Haetaan merkkijono-oliosta "abbababba" ensimmäinen parametrin 
# esiintymän indeksiarvo. Hakualuetta voidaan tarketaan 
# lisäparametreilla.
"abbababba".find("ba") # 2
"abbababba".find("B") # -1
# Korvataan ensimmäisenä parametrina annetun osajonon 
# esiintymät toisena parametrina annetulla merkkijonolla.
# Korvausten määrää voidaan rajoittaa.
"abbababba".replace("ba", "-") # "ab--b-"
# Pilkotaan merkkijono osiin käyttäen erottimena annettua
# merkkijonoa. Osien määrälle voidaan antaa yläraja.
osat = "1; Brian; Cohen".split("; ") # ["1", "Brian", "Cohen"]
alkuaineita = ["kupari", "hopea", "kulta", "rauta"]
# Poistetaan listan toinen alkio ja sijoitetaan se muuttujaan.
toinen_alkio = alkuaineita.pop(1)
print(alkuaineita) # ["kupari", "kulta", "rauta"]
print(toinen_alkio) # "hopea"
# IndexError: pop index out of range
alkuaineita.pop(3)
tunnuksia = {"cu": "kupari", "au": "kulta", "fe": "rauta"}
# Luetaan avaimet ja arvot ja tehdään metodien palauttamista
# näkymäolioista tuttuja sekvenssejä.
avaimet = list(tunnuksia.keys()) # ["cu", "au", "fe"]
arvot = list(tunnuksia.values()) # ["kupari", "kulta", "rauta"]
parit = list(tunnuksia.items())
# Tulostetaan avaimet ja arvot:
# ("cu", "kupari")
# ("au", "kulta")
# ("fe", "rauta")
for pari in parit:
    print(pari)

15. Omat funktiot

15.1 Yleistä

  • Funktio on nimetty ohjelman osa (aliohjelma), jolle on voidaan välittää tietoja ja joka voi myös palauttaa tietoja.

    • Toistaiseksi on hyödynnetty valmiita funktioita. Nyt opitaan kirjoittamaan omia funktioita.
  • Funktioilla pitkä ohjelma voidaan jakaa "palasiin", jolloin koodia on helpompi hallita, ymmärtää ja ylläpitää.

    • Monimutkaisuuden hallinta modulaarisella ohjelmoinnilla.
  • Ohjelman eri kohdissa samana toistuva osa voidaan sijoittaa fuktioon, jolloin koodia ei tarvitse "monistaa" eri kohtiin, vaan pelkkä operaation kutsu riittää ohjelman osan suoritukseen.

    • Ohjelman ylläpito helpottuu.
  • Funktiot nimetään samoin periaattein kuin muuttujat.

    • Nimet ovat yleensä muuttujien nimiä pitempiä.

    • Funktiot kuvaavat toimintoja -- nimessä on usein käskymuotoinen verbi.

  • Hyvä ohjelmointitapa koskee myös funktioita.

    • Funktion yleinen kommentti kertoo vähintään funktion tarkoituksen.
  • Python-lähdekooditiedosto on niin sanottu skripti (script), jos se on tarkoitettu suoritettavaksi.

  • Moduuli (module) on funktioita ja muita määrittelyjä sisältävä Python-lähdekooditiedosto, jonka sisältö voidaan ottaa käyttöön muissa lähdekooditiedostoissa import-lauseella.

    • Moduuli on eräänlainen kirjasto.
  • Ohjelmat kirjoitetaan kurssilla jatkossa Pythonille tyypilliseen tapaan siten, että niitä voi käyttää sekä skripteinä että moduleina, vaikka ohjelma onkin edelleen yhdessä tiedostossa.

  • Ohjelmiin tehdään myös niin sanottu pääohjelma, joka on main-niminen funktio, joka on ensimmäinen kutsuttava funktio, kun ohjelma suoritetaan skriptinä.

    • Pääohjelma ei ole pakollinen, mutta se kirjoitetaan, koska jatkossa opiskellaan kieliä, joissa suorituskelpoisessa ohjelmassa täytyy olla pääohjelma.

    • Pääohjelmasta kutsutaan muita omia funktioita, jotka puolestaan voivat kutsua muita omia funktioita.

15.2 Pääohjelma

  • Pääohjelma määritellään muiden funktioiden tapaan avainsanalla def, joka aloittaa metodin otsikon (header). Otsikkorivillä annetaan funktion nimi ja parametrilista ennen rivin päättävää kaksoispistettä.

  • Parametrilista voi olla tyhjä () tai se voi sisältää yhden tai useamman parametrin tunnuksen kaarisulkeiden sisällä.

    • Parametrit nimetään kuten muuttujat ja erotetaan toisistaan pilkulla. Pääohjelman parametrilista on tällä kurssilla tyhjä.
  • Funktioon liittyviä lauseita kutsutaan funktion rungoksi (body).

    • Lauseet sisennetään neljällä välilyönnillä.
def funktion_nimi(parametrilista):
    ...
def main():
    ...
  • Jaetaan merkkijonon kehystettynä tulostava ohjelma (kehystaja1.py) pääohjelmaksi main ja kehystäväksi funktioksi.

  • Aloitetaan pääohjelmasta, jonne sijoitetaan tervehdyksen tulostaminen ja syötteiden luku.

  • Pääohjelma on perinteisesti tiedoston viimeinen funktio, jotta sitä kutsuttaessa on varmaa, että kaikki muut funktiot on määritelty.

def main():
    # Tervehditään käyttäjää ja kerrotaan ohjelmasta.
    print("Moi! Tulostan merkkijonon merkillä kehystettynä.")
    # Luetaan syötteet.
    print("Anna merkkijono:")
    merkit = input()
    print("Anna merkki:")
    merkki = input()
  • Pääohjelman käynnistämiseen tarvitaan funktiokutsu, joka tulee suorittaa vain kun ohjelmaa käytetään skriptinä eli ajetaan.

    • Kutsu voidaan muotoilla ehdollisena Python-tulkin sisäisen muuttujan __name__ avulla, jolla on arvo "__main__", kun ohjelma on käynnistetty tulkkiin. Muuttujan arvo on tiedoston nimi, kun ohjelmaa ei ajeta, vaan käytetään moduulina.

    • Pääohjelma on määriteltävä ennen kutsua.

def main():
    # Tervehditään käyttäjää ja kerrotaan ohjelmasta.
    print("Moi! Tulostan merkkijonon merkillä kehystettynä.")
    ...
# Kutsutaan pääohjelmaa, jos ohjelmaa ajetaan.
if __name__ == "__main__":
    main()

15.3 Kehystävä funktio

  • Kirjoitetaan seuraavaksi kehystävä funktio, jolle välitetään parametreina käyttäjän antamat tiedot.

  • Parametrien tunnuksiksi valitaan selvyyden vuoksi tunnukset, joita käytetään pääohjelmassa syötteiden säilömiseen.

    • Parametrien tunnuksina voidaan käyttää mitä tahansa hyviä tunnuksia, koska yhdessä funktiossa käytetyt tunnukset eivät näy yhdestä funktiosta toiseen funktioon.

    • Parametrit toimivat funktion sisällä samoin kuin muuttujat.

  • Ohjelmissa ei käytetä jatkossa funktioiden ulkopuolella määriteltyjä niin sanottuja globaaleja muuttujia ellei kyseessä ole useassa funktiossa tarvittava vakio, koska tällaiset muuttujat rikkovat modulaarisuusperiaatetta.

  • Funktioon kirjoitetaan yleinen kommentti, jossa kerrotaan mitä funktiossa tehdään ja mitä tietoja parametreilla välitetään funktioon.

    • Kommenteissa kerrotaan myös paluuarvosta, jos funktiolla on sellainen.

    • Monesta kielestä poiketen funktion yleinen kommentti kirjoitetaan funktion sisään.

      • Kommentti on heti otsikon jälkeen. Kommentin jälkeen ei tule väliriviä.
  • Yleiset kommentit esitetään kolminkertaisiin lainausmerkkeihin """ suljettuna merkkijonona (docstring), joka on varattu funktioiden ja sitä laajempien kokonaisuuksien dokumentointiin.

  • Funktioiden rungoissa kommentoidaan normaaliin tapaan.

def tulosta_kehystettynä(merkit, merkki):
    """Tulostetaan viesti kehystettynä.
    merkit -- kehystettävä viesti, jossa on oltava vähintään yksi merkki.
    merkki -- yhden merkin mittainen merkkijono, jolla viesti kehystetään.
    """
    # Koodia selventävä ja kutsuja vähentävä apumuuttuja.
    merkkien_lkm = len(merkit)
    
    # Tulostetaan vain, jos parametrit ovat järkeviä
    if merkkien_lkm > 0 and len(merkki) == 1:
        # Luodaan ylä- ja alarivi.
        reunarivi = (merkkien_lkm + 4) * merkki

        # Tulostetaan.
        print(reunarivi)
        print(merkki + " " + merkit + " " + merkki)
        print(reunarivi)
  • Myös pääohjelmassa on tehtävä funktiokutsu, jotta kehystävä funktio saadaan suoritettua.

    • Funktio sijoitetaan ennen pääohjelmaa, jotta se on määritelty ennen kutsua.

    • Uusi ohjelma on kokonaisuudessaan kehystaja2.py-tiedostossa.

def main():
    # Tervehditään käyttäjää ja kerrotaan ohjelmasta.
    print("Moi! Tulostan merkkijonon merkillä kehystettynä.")
    ...
    # Kutsutaan tulostavaa funktiota antamalla syötteet
    # sisältävien muuttujien arvot parametrien arvoiksi
    # oikea järjestys huomioiden.
    tulosta_kehystettynä(merkit, merkki)
# Kutsutaan pääohjelmaa, jos ohjelmaa ajetaan.
if __name__ == "__main__":
    main()

15.4 Funktioiden kutsut

  • Funktiot on järjestettävä lähdekoodi-tiedostossa siten, että kutsuttavan funktion määrittely on ennen funktioiden ulkopuolella tehtävää kutsua.

    • Pääohjelman on oltava muiden funktioiden jälkeen, jotta on varmaa, että kaikki tarpeellinen on määritelty ennen pääohjelman käynnistystä.

    • Python sallii funktioille järjestykset, joiden virheellisyys ilmenee vasta ohjelmaa ajettaessa.

    • Metodien määrittelyjärjestyksellä ei ole kieliopillista merkitystä.

  • Yhteenkuuluvia funktioita kannattaa sijoitella lähelle toisiaan ellei kutsujärjestys ole esteenä.

  • Parametrien tunnukset ovat muuttujien tapaan viitteitä. Ne saavat arvonsa (olionsa) funktiota kutsuttaessa.

    • Funktiokutsussa parametrin tunnus liitetään parametrin arvoksi annettuun viitteeseen liittyvään olioon.

    • Voidaan ajatella, että parametriarvoksi saadaan nimetön viite, kun funktion kutsussa käytetään literaalia.

  • Kutsu siirtää ohjelman suorituksen funktioon, josta palataan automaattisesti kutsupaikkaan ellei funktiossa käytetä ohjelmoijan toimesta return-lausetta.

  • Ennen kehystävän funktion kutsua ja funktion kutsun jälkeen olioihin liittyvät vain pääohjelman muuttujat.
# main
merkit = input() # "Python"
merkki = input() # "+"

  • Kehystävässä funktiossa olioihin liittyvät pääohjelman muuttujien lisäksi kehystävän funktion parametrit.
def tulosta_kehystettynä(merkit, merkki):

15.5 Parametrien oletusarvot

  • Parametrille voidaan määritellä funktion otsikossa oletusarvo, joka otetaan käyttöön, kun parametrille ei anneta arvoa kutsussa.

    • Oletusarvoisten parametrit määritellään muiden parametrien jälkeen.

    • Oletusarvoisia parametreja voidaan käyttää kutsussa avainsanaparametreina.

    • Kutsussa ei voi käyttää avainsanaparametreja ennen normaaleja parametreja.

  • Määritellään kehystävän funktion reunamerkin parametrille oletusarvo. Lisätään samalla oletusarvoinen parametri reunan kulmamerkille.

15.6 Kehystävä funktio (kehystaja3.py)

def tulosta_kehystettynä(merkit, reuna = "-", kulma = "+"):
    """Tulostetaan viesti kehystettynä.
    merkit -- kehystettävä viesti, jossa on oltava vähintään yksi merkki.
    reuna -- yhden merkin mittainen merkkijono, jolla viesti kehystetään.
    Oletusarvo on miinusmerkki.
    kulma -- yhden merkin mittainen merkkijono, joka tulostetaan kehyksen
    kulmiin. Oletusarvo on plusmerkki.
    """
    # Koodia selventävä ja kutsuja vähentävä apumuuttuja.
    merkkien_lkm = len(merkit)
    # Tulostetaan vain, jos parametrit ovat järkeviä
    if merkkien_lkm > 0 and len(reuna) == 1 and len(kulma) == 1:
        # Luodaan ylä- ja alarivi.
        reunarivi = kulma + (merkkien_lkm + 2) * reuna + kulma
        # Tulostetaan.
        print(reunarivi)
        print(reuna + " " + merkit + " " + reuna)
        print(reunarivi)
        
        
merkkijono = input() # "Python"
reunamerkki = input() # "."
kulmamerkki = input() # "#"
# Kutsutaan tulostavaa funktiota antamalla syötteet sisältävien
# muuttujien arvot parametrien arvoiksi oikea järjestys huomioiden.
tulosta_kehystettynä(merkkijono, reunamerkki, kulmamerkki)
#........#
. Python .
#........#
# Kutsutaan tulostavaa funktiota välittämällä sille vain 
# kehystettävä merkkijono. Muut parametrit saavat oletusarvonsa.
tulosta_kehystettynä(merkkijono)
+--------+
- Python -
+--------+


# Kutsutaan tulostavaa funktiota välittämällä sille kehystettävä
# merkkijono ja reunamerkki. Kulmamerkki saa oletusarvonsa.
tulosta_kehystettynä(merkkijono, reunamerkki)
+........+
. Python .
+........+
# Kutsutaan tulostavaa funktiota välittämällä sille reunamerkki
# avainsanaparametrina. Reunamerkki saa oletusarvonsa.
tulosta_kehystettynä(merkkijono, kulma = kulmamerkki)
#--------#
- Python -
#--------#
# Kutsu ei ole mahdollinen, koska avainsanaparametrin arvo on
# annettu ennen normaalia parametriarvoa.
# SyntaxError: positional argument follows keyword argument
tulosta_kehystettynä(reuna = kulmamerkki, merkkijono)

15.7 Paluuarvo

  • Funktiosta palautetaan arvo return-lauseella.

    • Parametriton return-lause keskeyttää funktion suorituksen siten, että paluuarvo on automaattisesti None-vakio.
  • Python ei vaadi return-lauseen yhtenäistä käyttöä: funktiosta on mahdollista palata sekä ohjelmoijan toimesta että automaattisesti.

  • Paluuarvoja voi olla useita, jos ne palautetaan esimerkiksi listana tai tuplana.

  • Paluuarvo voidaan sijoittaa kutsupaikassa muuttujaan.

  • Paluuarvo muodostaa yhdessä parametrilistan kanssa funktion liittymän, jonka kautta funktio kommunikoi muiden funktioiden kanssa.

15.8 Kehystävä funktio (kehystaja4.py)

def tulosta_kehystettynä(merkit, reuna = "-", kulma = "+"):
    """Tulostetaan viesti kehystettynä.
    ...
    Paluuarvo on True, jos parametriarvot ovat sellaiset, että voitiin
    viesti voitiin tulostaa kehystettynä. Muuten palautetaan False.
    """
    ...    
    # Tulostetaan vain, jos parametrit ovat järkeviä
    if merkkien_lkm > 0 and len(reuna) == 1 and len(kulma) == 1:
        ...        
        # Viestitään, että tulostus onnistui.
        return True
    # Yksi tai useampi parametriarvo on virheellinen.
    else:
        # Viestitään, että tulostus epäonnistui.
        return False

def main():
    ...

    # Kutsutaan tulostavaa funktiota antamalla syötteet sisältävien
    # muuttujien arvot parametrien arvoiksi oikea järjestys huomioiden.
    # Paluuarvo sijoitetaan muuttujaan.
    syötteet_ok = tulosta_kehystettynä(merkkijono, reunamerkki, kulmamerkki)
    
    # Kerrotaan virheestä käyttäjälle.
    if not syötteet_ok:
        print("Virheellisiä syötteitä!")

15.9 Näkyvyys

  • Tunnuksen näkyvyysalue (scope, visibility) on joko globaali tai paikallinen.

    • Näkyvyyttä pohditaan kurssilla hyvin rajatusti funktioiden näkökulmasta: globaalit tunnukset ovat funktioiden ulkopuolella määriteltyjä muuttujia tai vakioita, kun taas funktion parametrit, muuttujat ja vakiot ovat paikallisia.
  • Globaalit tunnukset näkyvät kaikkiin funktioihin.

    • Globaaleja tunnuksia on vältettävä, koska niillä ohitetaan funktioiden liittymät. Seurauksena on funktioiden välisiä riippuvuuksia, jotka vaikeuttavat ohjelman ymmärtämistä ja ylläpitoa.

    • Funktioihin jaettavissa harjoitustehtävissä ei saa käyttää globaaleja muuttujia ellei kyseessä ole vakio.

  • Globaalit vakiot esitellään ohjelman alussa ennen funktioita.

  • Paikalliset tunnukset näkyvät vain funktionsa sisällä ja ovat siten käytettävissä vain, kun funktiota suoritetaan.

    • Tähän poikkeuksena funktiossa global-lauseella globaaleiksi esitellyt tunnukset, joita ei pitäisi käyttää ylipäätänsä, koska ne vaikeuttavat ohjelman ymmärtämistä huomattavasti.

    • Älä käytä global-lausetta tällä kurssilla.

    • Funktioiden kesken ei voi välittää tietoa käyttämällä saman-nimisiä tunnuksia.

      • Funktiossa voidaan esitellä huoletta samannimisiä tunnuksia, koska ne eivät näy muihin funktioihin.
  • Tiedonvälitykseen tarvitaan paluuarvo tai muuttuvia parametreja.

15.10 Sähköpostiosoitteen muutos (posti.py)

def vaihda_etu_ja_suku_ver1(sähköpostiosoite):
    """Vaihtaa nimimuotoisen sähköpostiosoitteen etu- ja sukunimen
    keskenään. Muutos ei heijastu kutsupaikkaan, koska muutos
    on paikallisen eli voimassa vain funktiossa.
    sähköpostiosoite - osoite, jonka alun oletetaan olevan
    "etunimi.sukunimi@" tai "sukunimi.etunimi@".
    """
    # Katkaistaan osoite @-merkin kohdalta.
    alku, loppu = sähköpostiosoite.split("@")
    # Katkaistaan alku pisteen kohdalta.
    eka_nimi, toka_nimi = alku.split(".")
    # Kootaan uusi osoite.
    sähköpostiosoite = toka_nimi + "." + eka_nimi + "@" + loppu
    
    
def vaihda_etu_ja_suku_ver2(sähköpostiosoite):
    """Vaihtaa nimimuotoisen sähköpostiosoitteen etu- ja sukunimen
    keskenään. Muutos heijastuu kutsupaikkaan, koska paikallinen
    muutos välitetään takaisin paluuarvoa käyttäen.
    sähköpostiosoite - osoite, jonka alun oletetaan olevan
    "etunimi.sukunimi@" tai "sukunimi.etunimi@".
    """
    # Katkaistaan osoite @-merkin kohdalta.
    alku, loppu = sähköpostiosoite.split("@")
    # Katkaistaan alku pisteen kohdalta.
    eka_nimi, toka_nimi = alku.split(".")
    # Kootaan uusi osoite.
    sähköpostiosoite = toka_nimi + "." + eka_nimi + "@" + loppu
    # Palautetaan muutettu osoite.
    return sähköpostiosoite


def main():
    # Tervehditään käyttäjää ja kerrotaan ohjelmasta.
    print("Moi! Muutan sähköpostisoitteita.")
    # Luetaan syöte.
    print("Anna osoite:")
    sähköpostiosoite = input()
    # Kutsutaan muunnosfunktion ensimmäistä versiota
    # ja tarkistetaan, että muuttujan arvo ei ole muuttunut.
    vaihda_etu_ja_suku_ver1(sähköpostiosoite)
    print(sähköpostiosoite)
    # Kutsutaan muunnosfunktion toista versiota
    # ja tarkistetaan, että muuttuja on saanut uuden arvon.
    sähköpostiosoite = vaihda_etu_ja_suku_ver2(sähköpostiosoite)
    print(sähköpostiosoite)

15.10.1 Sähköpostiosoitteen muutos (versio 1)

  1. Pääohjelma ennen funktion kutsua.

  1. Funktion parametrin tunnus (viite) liitetään samaan olioon, johon pääohjelman muuttujan tunnus (viite) liittyy.

  1. Funktiossa tehtyä muutosta ei saada pääohjelman käyttöön kahdesta syystä:

    1. Alkuperäinen olio on muuttumaton. Funktiossa on luotava siksi uusi olio.

    2. Alkuperäinen viite ei ole saatavilla. Sijoitus liittää uuteen olioon paikallisen viitteen

  1. Paikallisesti tehty muutos katoaa funktiosta palattaessa, koska paikallisia muuttujia ei säilötä.

15.10.2 Sähköpostiosoitteen muutos (versio 2)

  1. Funktiossa luotu uusi olio saadaan käyttöön pää-ohjelmassa palauttamalla olioon liitetty paikallinen viite return-lauseella.

  1. Uusi olio säilyy, vaikka paikallinen viite katoaa funktiosta palattaessa, koska pääohjelmaan palautuu nimetön viite.

  1. Pääohjelmassa tehty sijoitus liittää alkuperäisen viitteen uuteen olioon. Alkuperäinen olio tuhotaan, koska siihen ei liity enää viitettä.

15.11 Merkkijonojen listan kääntäjä (kaantaja.py)

def käännä_merkkijonot(merkkijonot):
    """Käännetään listan merkkijonot. Paluuarvoa ei tarvita,
    koska parametrin tunnukseen liittyvää oliota voidaan muuttaa.
    merkkijonot -- lista, jonka merkkijonot on käännetty.
    """
    # Enumerate-funktio tuottaa laskurin ja lista-alkion tuplia.
    # Laskurin alkuarvo on nolla. For-silmukassa esitellään omat
    # muuttujat tuplan alkioille.
    for ind, merkit in enumerate(merkkijonot):
        # Alkio käännetään viipaloimalla koko merkkijono siten,
        # että siirrytään merkki kerrallaan kohti merkkijonon
        # alkua. Alkion kääntö tuottaa uuden merkkijono-olion,
        # joka sijoitetaan alkuperäisen merkkijonon paikalle.
        merkkijonot[ind] = merkit[::-1]

def main():
    # Tervehditään käyttäjää ja kerrotaan ohjelmasta.
    print("Moi! Käännän listan merkkijonot.")
    # Esitellään lista, käännetään ja tulostetaan se.
    lista = ["this", "is", "a", "test"]
    käännä_merkkijonot(lista)
    print(lista) # ['siht', 'si', 'a', 'tset']

  1. Pääohjelma ennen funktion kutsua.

  1. Funktion parametrin tunnus (viite) liitetään samaan olioon, johon pääohjelman muuttujan tunnus (viite) liittyy.

  1. Funktiossa tehty muutos saadaan käyttöön pääohjelmassa ilman paluuarvoa, koska listaolion tietoja voidaan muuttaa.

  1. Funktion parametrin tunnus tuhotaan funktiosta palattaessa.

15.12 Moduulit

  • Moduulit ovat funktioita ja muita kokonaisuuksia sisältäviä kirjastoja, joita kutsutaan skripteistä ja muista moduuleista.

    • Moduuleja ajetaan vain, kun niihin on kirjoitettu sisäänrakennettuja testejä.
  • Python-kieleen kuuluu useita valmiita moduuleja, joista kurssilla on käytetty vain random-moduulia.

    • Moduuleihin kannattaa tutustua ennen suurempia tehtäviä; Pythonissa saattaa olla valmiina projektisi keskeisimmät osat.
  • Laajemmat omat ohjelmat jaetaan moduuleihin ja pakkauksiin, jotka ovat moduulien kokoelmia.

    • Tällä kurssilla ei perehdytä omien moduulien tekoon.
  • Moduuli tai sen osa otetaan käyttöön import-lauseella.

    • Nämä ovat ohjelman alussa ennen muita lauseita.

    • Ohjelman kommentit edeltävät import-lauseita.

    • Lauseella on useita muotoja.

# Otetaan käyttöön random-moduuli.
import random
...
# Kutsutaan moduulin funktiota.
arvattava = random.randint(ALARAJA, YLÄRAJA)
# Otetaan käyttöön tietty random-moduulin funktio.
from random import randint
...
# Funktiota voidaan nyt kutsua helpommin. 
arvattava = randint(ALARAJA, YLÄRAJA)
# Otetaan käyttöön random-moduulin kaikki funktiot.
from random import *
...
# Funktiota voidaan nyt kutsua helpommin. 
arvattava = randint(ALARAJA, YLÄRAJA)

15.13 Pitkien rivien katkaisemien

  • Monimutkaisen funktion otsikko voi olla tarpeen jatkaa usealle riville mittansa vuoksi.

  • Pythonin tyyliopas https://www.python.org/dev/peps/pep-0008/ antaa vaihtoehtoisia tapoja katkaista pitkiä funktion otsikoita, funktiokutsuja ja if-elif-else-lauseen aloittavia rivejä.

  • Kurssin materiaalissa funktioiden otsikot katkaistaan alla annetun esimerkin mukaisesti.

# Ensimmäinen otsikkorivi päättyy avaavaan sulkeeseen.
# Muut otsikkorivit sisennetään kahdeksalla välilyönnillä.
def pitkän_pitkän_pitkä_funktion_nimi(
        ensimmäinen_parametri, toinen_parametri,
        kolmas_parametri, neljäs_parametri):
    # Rungon rivit sisennetään neljällä välilyönnillä.

16. Testiosio

# for_ex1

These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.