Kurssin viimeisimmän version löydät täältä: O1: 2024
Luku 5.4: Katsaus Scala-työkalujen sisään
Tästä sivusta:
Pääkysymyksiä: Kuinka Scala toimii? Mitä IntelliJ tekee huomaamatta puolestani? Miksi eräät laskutoimitukset toimivat hassusti?
Mitä käsitellään? Datan esittäminen (binaari)lukuina. Vähän tiedostoformaateista. Kääntäjä, virtuaalikone, tavukoodi ja konekieli. Roskankeruu. Lukuja kuvaavien tietotyyppien rajoituksia. Scala-työkaluja komentorivillä.
Mitä tehdään? Lähinnä luetaan. Jokunen minitehtävä.
Suuntaa antava työläysarvio:? Tunti. (Sisältää runsaasti valinnaista materiaalia, jonka parissa menee enemmänkin.)
Pistearvo: A5.
Oheismoduulit: Ei ole.
Muuta: Sisältää upotetun videon, joka on kuitenkin täysin valinnaista asiaa.
Johdanto: Scala-koodin vaiheet
Pidetään pieni tauko uusien käskyjen opettelussa. Tässä luvussa tutkimme, mitä vaiheita liittyy siihen, että ohjelmakoodi muuttuu konkreettiseksi toiminnaksi tietokoneessa. Seuraava esitys johdattelee aiheeseen:
Muun muassa IntelliJ-sovelluskehitin huolehtii esityksessä mainituista vaiheista automaattisesti ja huomaamattomasti. IntelliJ’kin kyllä käyttää useita eri työkaluja, esimerkiksi kääntäjää ja virtuaalikonetta, joiden yhteistoiminta vain on siinä määrin saumatonta, ettei IntelliJ’n käyttäjän ole pakko ajatella käyttävänsä useaa työkalua. Nämä vaiheet onkin tähän mennessä kurssilla jätetty puhtaasti sovelluskehittimen huoleksi. Kuitenkin nyt perehdymme tähän aiheeseen hieman.
Miksi?
Jos kerran vaikkapa IntelliJ tekee asiat automaattisesti, niin miksi niistä pitäisi nyt oppia?
Ensinnäkin on huomattava, että ohjelmointia voi tarkastella useilla eri abstraktiotasoilla. Esimerkiksi olio-ohjelman suoritusta voi kuvata korkealla abstraktiotasolla olioiden viestintänä. Tämä perustuu matalammalla tasolla ohjelmointikielen käskyjen suorittamiseen peräjälkeen. Vielä matalammalla tasolla kyse on bitteihin liittyvistä operaatioista prosessoreissa.
Tietyllä abstraktiotasolla työskennellessäsi on avuksi, että ymmärrät jonkin verran myös alla olevasta tasosta. Abstraktion sisään katsomisesta on apua etenkin ongelmatilanteissa, kun käyttämäsi käskyt eivät toimikaan kuten halusit tai luulit.
Sama pätee ohjelmoijan apuohjelmiin. Sovelluskehittimen käytössä on avuksi ymmärtää jotain sen sisäisestä toiminnasta.
Toinen hyvä syy on se, että "oikeassa maailmassa" kurssin ulkopuolella ohjelmoidaan muuallakin kuin IntelliJ’ssä. Työkalumme taustalla olevia perusperiaatteita on siis hahmotettava, jotta voit joustavammin vaihtaa työkaluja tarvittaessa.
Aiheeseen perehtyminen on perusteltua jo tietoteknisen yleissivistyksenkin vuoksi.
Ja tietysti siksikin, että joidenkin ohjelmoijien on kyettävä kehittämään työkaluja ohjelmoijien käyttöön. (Mutta se ei ole tämän luvun tai kurssin aihe.)
Katsotaan siis, kuinka Scala toimii.
Erillisiä työkaluja
Yksittäisiä Scala-koodin työstämisen vaiheita on helpompi hahmottaa, kun käytämme sovelluskehittimen sijaan irrallisia työkaluja.
Scala-kielen viralliseen työkalupakkiin (ks. http://www.scala-lang.org/) kuuluu joukko toisistaan erillisiä työkaluohjelmia:
Muita kääntäjiä
Scalacin lisäksi Scalaa käännetään usein myös fsc-kääntäjällä (Fast Scala Compiler) ja sbt-työkalulla (Scala Build Tool), joista löydät lisätietoja nettihauilla.
- Kääntäjäohjelma nimeltä scalac (Scala compiler).
- Virtuaalikoneen käynnistysohjelma nimeltään yksinkertaisesti scala.
- Scaladoc-työkalu dokumenttien tuottamiseen (luku 3.2).
- Muita osia, joita ei käsitellä tässä.
Seuraava esitys näyttää, miten luodaan ja ajetaan pikkuruinen Scala-ohjelma käyttäen editoria, kääntäjää ja virtuaalikonetta kutakin erikseen.
Onko komentorivi tuttu?
Esityksen seuraamisessa auttaa, jos työskentely komentoriviympäristössä on edes vähän tuttua — siis tekstikäskyjen käyttö esimerkiksi Windowsin Command Promptissa tai Linuxin terminaali-ikkunan komentotulkissa. Jos ei ole, löydät aiheesta ohjeita netistä. Esityksen pääpointit avautunevat kyllä kokemattomallekin.
Alempana on kerrottu lisää näistä työvaiheista ja -kaluista sekä siitä, miten vaiheet toteutuvat IntelliJ’tä käyttäessä.
Koska kyse on tiedon muokkaamisesta eri muotoihin, tarvitset vähän pohjatietoja siitä, missä muodoissa tietoa voidaan tietokoneessa esittää. Aloitetaan siis biteistä.
Luvut bitteinä, data lukuina
Binaariluvut muodostuvat ykkösistä ja nollista. Yhtä binaariyksikköä — nollaa tai ykköstä — kutsutaan bitiksi (bit eli binary digit).
Esimerkiksi kymmenjärjestelmän lukuja voi kuvata bitteinä seuraavasti:
14kymmen = 8 + 4 + 2 = 1*23 + 1*22 + 1*21 + 0*20 = 1110binaari
5kymmen = 4 + 1 = 1*22 + 0*21 + 1*20 = 101binaari
Tietokone on digitaalinen järjestelmä. Sen muistissa kaikki data on esitetty binaarilukuina tai muuten bittijonoina.
Kuvat ja teksti bitteinä
Kuva on väripisteiden eli pikselien yhdistelmä. Pisteiden värisävyt voidaan ilmaista lukuina ja nämä luvut bitteinä.
Teksti puolestaan muodostuu kirjoitusmerkeistä. Voidaan sopia, että kutakin erilaista merkkiä vastaa yksiselitteisesti jokin luku. Scalassa merkkijonojen kuvaamiseen käytetty Unicode (http://www.unicode.org/) on eräs tällainen merkistösopimus eli -standardi. Alla on esimerkkejä Unicode-standardin määräämistä lukuarvoista eräille merkeille:
Unicode-arvo | Merkki |
---|---|
32 | välilyönti |
38 | & |
48 | 0 |
49 | 1 |
65 | A |
66 | B |
97 | a |
98 | b |
122 | z |
945 | α (alfa) |
Kun meillä on merkille lukuarvo, voimme esittää merkin bitteinä (tavalla tai toisella, esim. suora binaarilukuesitys kuten yllä). Merkkijono voidaan siis kuvata jonona binaarilukuja.
Viittaukset bitteinä
Viittauksen käsite on jo tullut tutuksi: pieni datajyvänen, jonka perusteella voidaan käsitellä toista dataa (eli jotakin oliota). Kurssimateriaalin animaatioissa olet nähnyt olioihin kohdistuvia viittauksia, joita on esimerkiksi tallennettu muuttujiin ja välitetty parametreiksi. Pohjimmiltaan Scala-ohjelmassa esiintyvä viittauskin on bittijono (binaariluku), joka tavalla tai toisella identifioi tietokoneen muistista kohdan, josta löytyvät tietyn olion tiedot.
Ohjelma bitteinä
Ei vain ohjelman käsittelemä data vaan myös itse ohjelma pidetään tallessa bitteinä, yleensä tiedostoon tallennettuna. Itse asiassa: ohjelmakin on dataa ja ohjelmat voivat käsitellä ohjelmia!
Scalan tapauksessa ja muutenkin useimmiten ohjelman lähdekoodi on ns. plain text -muodossa, joka koostuu vain kirjoitusmerkkien bittikuvauksista. Kääntäjällä ohjelma muutetaan toiseen muotoon, ja samalla myös tapa, jolla ohjelma on kuvattu bitteinä, muuttuu.
Mutta mitä bitit oikeasti ovat?
Ennen vanhaan bittien tallennusmuoto oli helpommin havaittavissa kuin nykyisissä tietokoneissa, joissa niiden fyysinen muoto on kauempana arkitodellisuudesta.
Vielä 1970-luvulla tietokoneita ohjelmoitiin aivan yleisesti reikäkorteilla (punched card) eli pahviläpysköillä, joihin tehtyjen reikien sijainnit määrittivät ohjelman. Saatettiin esimerkiksi määritellä, että reikä tai sen puuttuminen tietyssä kohdassa määräsi yhden bitin ja usea bitti sitten yhdessä tietyn käskyn.
Reikäkortteja käytettiin jo Joseph Marie Jacquardin vuonna 1801 keksimien kangaspuiden ohjelmointiin. Jacquardin kangaspuita pidetään ensimmäisten varsinaisten tietokoneiden esiasteena.
Nykyaikaisissa tietokonelaitteissa yksittäinen bitti voidaan pitää tallessa esimerkiksi sähkövarauksena, joka tietyssä komponentissa joko on tai ei ole. Tai pikkuruisena kuoppana tai kuopattomuutena DVD-levyn pinnassa. Tai magneettisesti. Tapoja on monia, ja bittien fyysinen muoto vaihtelee laitteesta toiseen.
Vuonna 2016 julkaistiin tutkimus uudesta muistityypistä, joka käyttää yksittäisten bittien tallentamiseen yksittäisiä klooriatomeita kuparipinnalla. Artikkeli World’s Smallest Hard Drive Writes Data Atom-By-Atom kertoo näin: "When a chlorine atom is on top with a hole beneath it, it’s a one, the binary digit and when it’s the other way around it’s a zero — thus creating a hard drive." Bittien varastoimiseksi yksittäisiin atomeihin on saatu edistysaskelia sittemminkin.
Tulevaisuudessa biteistä kubiteiksi?
Jos haluat ravistella aivojasi, selvitä internetin avulla, mitä ovat kvanttitietokoneet (quantum computer) ja miten ne käyttävät kubitteja (qubit) bittien sijaan.
Palataan ohjelmoijan aputyökaluihin.
Tekstieditoreista
Plain text -tekstitiedosto — tai lyhyemmin sanoen vain "tekstitiedosto" — on tiedosto, johon on tallennettu bittien sarja, jonka pätkät vastaavat lukuja, jotka puolestaan vastaavat kirjoitusmerkkejä jonkin tietyn standardin mukaisesti. Näitä tiedostoja voi lukea ja muokata sovelluksilla, jotka on laadittu niin, että ne osaavat tulkita tiedostoon tallennetut bitit merkeiksi. Tällaisia ohjelmia ovat esimerkiksi IntelliJ’n editori, Emacs, Windowsin Notepad ja Macien TextEdit.
Muunlaiset tiedostoformaatit noudattavat eri standardeja kuin tekstitiedostot. Esimerkiksi Excel-taulukon, GIF-kuvatiedoston tai Word-dokumentin käsittelyyn tarvitaan sellainen ohjelma, joka osaa käsitellä tallennetut bitit tiedostoformaatin mukaisella logiikalla.
Voit tutkia itsekin
Jos haluat, voit avata esimerkiksi kuvatiedoston Notepadissä (tai muun ei-tekstitiedoston jossakin muussa tekstitiedostoille tarkoitetussa editorissa). Koska kuvatiedoston sisältämät bitit eivät kuvaa kirjoitusmerkkejä, ei Notepad osaa tulkita niitä oikein. Se kuitenkin tulkitsee bitit kuten parhaiten osaa eli merkkeinä. Tällöin editorissa näkyy "sotkua".
Koodia voi IntelliJ’n sijaan kirjoittaa missä tahansa muussakin tekstitietoja tallentavassa ohjelmassa. Esimerkiksi Notepad on mahdollinen koodausympäristö; se ei vain muilta ominaisuuksiltaan tue ohjelmointia hyvin, joten juuri kukaan ei halua sitä ohjelmointiin käyttää. On olemassa monia sovelluskehittimistä irrallisia tekstieditoreja, joissa on ohjelmakoodin editointia helpottavia ominaisuuksia.
IntelliJ’llä kirjoitetut kooditiedostot tallennetaan koneen tiedostojärjestelmään aivan samaan tapaan kuin muidenkin sovellusten tuottamat tallenteet. Kooditiedostot löytyvät IntelliJ-moduulien kansioista pakkausten mukaan nimetyistä alikansioista.
Konekielestä
Scala on korkean tason ohjelmointikieli (high-level language). Tämä tarkoittaa sitä, että kielen abstraktiotaso on korkea eivätkä sen ilmaisut ole suoraan sidoksissa esimerkiksi binaarilukuihin ja tietokoneen fyysisten osien toimintaan.
Minkään normaalin tietokoneen prosessori ei osaa Scalaa eikä mitään muutakaan korkean tason ohjelmointikieltä. Prosessorit aikaansaavat tietokoneiden toiminnan suorittamalla suuria määriä alkeellisia käskyjä, jotka on kirjoitettu konekielellä (machine code tai machine language).
Konekielen käskyt ovat bittijonoja, jotka vastaavat matalan abstraktiotason toimintoja kuten binaariluvuilla laskemista, bittien vertailua, nollien vaihtamista ykkösiksi ja muuta hyvin alkeellista toimintaa. Eri prosessorimalleilla on omat konekielensä, joissa erilaiset bittijonot vastaavat erilaisia käskyjä.
Konekieliohjelmointi on työlästä ja altista virheille. Hyvin suurten ohjelmistojen laatiminen konekielellä on käytännössä mahdotonta; voit verrata tätä vaikkapa täyskokoisen pilvenpiirtäjän rakentamiseen Lego-palikoista.
Kääntäminen, välikielet ja tavukoodi
Ohjelmoijan laatima lähdekoodi pitää muuntaa muotoon, joka soveltuu tietokoneen ajettavaksi.
Perinteisesti on erotettu kaksi tapaa muuntaa korkean tason kieltä suoritettavaan muotoon. Kääntämiseksi (compiling) sanotaan ohjelman muuntamista ennen kuin se määrätään suoritettavaksi. Tulkkaamiseksi (interpreting) taas sanotaan ohjelman suorittamista siten, että muunnos konekieleksi tehdään vähitellen suorittamisen yhteydessä.
Ensimmäinen Scala-ohjelmien käsittelyyn liittyvä vaihe on kääntäminen.
Kääntäminen on systemaattinen prosessi, ja se voidaan tehdä koneellisesti apuohjelmalla. Kääntämisen tuloksena syntyy koneen luettavaksi tarkoitettua koodia (ns. "binäärejä"). Kääntäessä samalla varmistetaan etukäteen, että ohjelma on kieliopin mukainen.
IntelliJ’llä on käytössään kääntäjäapuohjelma. Normaaliasetuksilla IntelliJ kääntää
Scala-lähdekoodia automaattisesti "tarpeen mukaan" eli esimerkiksi kun käynnistät ohjelman,
jota on muokattu ja joka täytyy siis kääntää uudelleen. Olet jo oppinut erikseen pyytämään
käännöksen IntelliJ’n Build-valikosta tai F10
:llä. (Kääntäminen on
tämän "rakentamisen"→build keskeinen osa.)
Osa virheilmoituksista syntyy juuri käännöstyön yhteydessä. Aiemmissa luvuissahan on ollut puhetta käännösaikaisista virheilmoituksista.
Monia ohjelmointikieliä käännetään suoraan aidoksi konekieleksi ja annetaan käyttäjille
konekielisessä muodossa. Esimerkiksi Windowsista tutut .exe
-tiedostot sisältävät
konekieltä, joka on yleensä saatu jostakin korkean tason kielestä kääntämällä. Kuitenkaan
tämä ei ole ainoa vaihtoehto:
Välikielet ja tavukoodit
Viime vuosikymmeninä yleistynyt vaihtoehto suoraan aidoksi konekieleksi kääntämiselle on käyttää välikieltä (intermediate language). Syistä lisää alempana; perusajatus on seuraava:
- Käytetään kääntäjätyökalua, jolla muunnetaan lähdekoodi välikielelle ennen ohjelman ajamista.
- Käytetään ajamisessa apuna sellaista apuohjelmaa, joka osaa muuntaa välikielisen ohjelman konekieleksi. Tämä muunnos tehdään ohjelma-ajon yhteydessä, ja tietokone suorittaa konekielisen version.
Esimerkiksi Scala-ohjelmat käännetään aluksi välikielelle.
Joistakin välikielistä käytetään nimitystä tavukoodi (bytecode), niin myös siitä
välikielestä, joksi Scala-ohjelmia tavallisesti käännetään. Scala-kääntäjä tuottaa
class
-päätteisiä luokkatiedostoja, jotka sisältävät tavukoodia. Yhdestä
.scala
-tiedostosta syntyy kääntäessä yksi tai useampia .class
-tiedostoja.
Etsi omat tavukooditiedostosi
Selaa oman kurssikansiosi sisältöä. Siellä on out
-kansio, jonka
alla on production
-kansio. Sen sisältä löytyvät IntelliJ’n
tuottamat käännetyt versiot kunkin moduulin Scala-koodista
class
-päätteisissä tavukooditiedostoissa.
Tavukoodi koostuu varsin alkeellisista käskyistä ja muistuttaa konekieltä. Konekielen
tavoin ja lähdekoodista poiketen tavukoodi ei ole tarkoitettu ensisijaisesti ihmisen
luettavaksi tai muokattavaksi. class
-päätteiset tavukooditiedostot eivät ole
tekstitiedostoja, eivätkä tekstieditorit osaa niiden sisältöä mielekkäällä tavalla
näyttää.
Tavukooditiedoston hyödyntämiseen tarvitaan siihen erikoistunut ohjelma. Tärkein tällainen ohjelma on virtuaalikone.
Virtuaalikoneet ja JVM
Sanalla virtuaalinen (virtual) on monia merkityksiä, muiden muassa nämä:
- keinotekoinen,
- matkittu,
- simuloitu,
- ei todellinen vaikka todellista muistuttaakin,
- ikään kuin — vaikkei varsinaisesti tai virallisesti — tietynlainen.
Virtuaalikone on tietokoneohjelma, joka toimii ikään kuin tietokone olematta kuitenkaan oikeasti fyysinen tietokone.
Kun käsket IntelliJ’tä ajamaan ohjelman, vaikuttaa pinnallisin puolin siltä kuin IntelliJ ottaisi Scala-kooditiedoston ja ajaisi sen. IntelliJ ei kuitenkaan oikeastaan itse aja ohjelmaasi, vaan käynnistää kulissien takana apuohjelman — virtuaalikoneen, joka huolehtii ohjelman suorittamisesta.
IntelliJ antaa virtuaalikoneelle ajettavaksi tavukoodiksi käännetyn version kirjoittamastasi Scala-lähdekoodista. Se myös välittää virtuaalikoneelle tiedon siitä, mistä kohdasta ohjelmaa kuuluu lähteä suorittamaan (valitsemasi käynnistysolion perusteella). IntelliJ toimii siis samaan tapaan kuin komentorivin scala Ave -käsky yllä näkemässäsi esityksessä.
Käyttämäsi virtuaalikone on ikään kuin tietokone, jonka prosessori osaa suorittaa tavukoodikäskyjä; sen virtuaalista konekieltä on tavukoodi. Virtuaalikone määrittää, mitä todellisia konekielikäskyjä kukin tavukoodikäsky vastaa ja välittää ne todellisen tietokoneen suoritettavaksi. Käytännössä siis kyseinen ohjelma tulee ajetuksi.
Virtuaalikone hallinnoi ohjelman ajamista. Se huolehtii ohjelman suorituksen etenemisestä ja muistiresurssien hallinnasta. Ne lukuisat animaatiot ohjelmien suorituksesta, joita olet kurssimateriaalissa nähnyt, kuvaavat juuri eräitä virtuaalikoneen vastuualueelle kuuluvia seikkoja: kutsupinon kehysten luominen ja poistaminen, olioiden luominen muistiin ja niin edelleen.
Yleisintä Scala-ohjelmien ajamiseen käytettyä virtuaalikonetta sanotaan JVM:ksi. Tämä on lyhenne sanoista Java Virtual Machine eli Java-virtuaalikone.
Hetkinen! Java-virtuaalikone?!
Eikö Java ole erään ihan toisen ohjelmointikielen nimi?
Onhan se, mutta silti Scala-ohjelmia ajetaan Java Virtual Machine -nimisellä apuohjelmalla.
Java-virtuaalikone on ohjelma, jolla voi ajaa tietynlaiseksi tavukoodiksi käännettyjä ohjelmia. Alun perin kyseinen tavukoodityyppi kehitettiin Java-kielistä ohjelmointia varten, ja siksi virtuaalikonetta sanotaan JVM:ksi. Kuitenkin tuota virtuaalikonetta voi käyttää minkä tahansa sellaisten ohjelmien ajamiseen, jotka on käännetty sen kanssa yhteensopivaksi tavukoodiksi, riippumatta siitä, millä kielellä lähdekoodi on kirjoitettu.
Java-virtuaalikoneella voi siis ajaa vaikkapa Scala-ohjelmia, jos ne vain on käännetty
sopivalla kääntäjällä .class
-tiedostoiksi. Nykyään JVM on suosittu alusta paitsi
Javalle myös sitä kehittyneemmille ohjelmointikielille, joista Scala on yksi esimerkki.
Myös yllä komentoriviesimerkissä annettu scala Ave -käsky käynnisti nimestään huolimatta nimenomaan Java-virtuaalikoneen. Käsky ei olisi toiminut, jos tietokoneelle ei olisi asennettu Scala-työkalujen lisäksi myös Java-virtuaalikonetta.
Scala, Java, JavaScript
Käytetyin Scala-toteutus, jota käytämme tälläkin kurssilla, on rakennettu juuri "Javan päälle". Käyttämiemme Scala-kielen kirjastojen toteutuksessa on käytetty apuna olemassa olevia Java-kielen laajoja kirjastoja. Onkin niin, että Java paistaa läpi Scala-ohjelmoinnissa sieltä täältä. Olet ehkä kiinnittänytkin huomiota esimerkiksi seuraaviin asioihin.
- Kurssimateriaalin ohjeissa IntelliJ’n ja Scalan asentamiseksi ohjeistetaan nappaamaan netistä Scalan lisäksi myös Java.
- Kun käynnistät Scala-sovelluksen tai REPLin IntelliJ’ssä, ruudulla näkyy teksti, jossa mainitaan Java.
- Luvussa 4.2 saimme ilmoituksen
NullPointerException
-virheestä. Tarkemmin sanoen virheilmoituksessa lukijava.lang.NullPointerException
, mikä johtuu siitä, että kyseinen virhetyyppi on määritelty Java-kielen peruskirjastossa ja Scala-kirjastot hyödyntävät sitä.
Scala-kielen määrittely ei kuitenkaan sinänsä ole Java-riippuvainen eikä estä muunlaisia toteutuksia Scala-kirjastoille ja -työkaluille. Sellaisia onkin kehitetty. Tärkein vaihtoehtoinen toteutus on Scala.js, joka nojautuu JavaScript-kieleen ja mahdollistaa Scala-ohjelmien ajamisen web-selaimessa.
Perusteluja välikielelle ja virtuaalikoneelle
Mitä hyötyä on siitä, että lähdekoodi käännetään välikielelle, joka sitten suoritetaan virtuaalikoneen avulla? Eikö olisi suoraviivaisempaa ja helpompaa kääntää suoraan konekielelle?
Vertaillaan eräillä keskeisillä kriteereillä:
Laatukriteeri | Välikieli & virtuaalikone | Suoraan konekieleksi |
---|---|---|
siirrettävyys (portability) | Välikielelle käännettyjä ohjelmia voi käyttää missä tahansa ympäristössä, jossa on tarjolla kyseiseen ympäristöön sopiva virtuaalikone. Esimerkiksi JVM:lle käännettyjä Scala-ohjelmia voi suorittaa millä tahansa alustalla, jolle on tehty JVM-toteutus. (Tämä on osin myös haitta: virtuaalikone on oltava.) | Ohjelmia voi suorittaa vain tietynlaisessa ympäristössä, jossa kyseinen konekieli toimii. Eri ympäristöihin on tuotettava erilaiset käännökset ohjelmasta. |
yhdisteltävyys (interoperability) | Samalle välikielelle voidaan kääntää useita kieliä. Tämä mahdollistaa eri kielillä kirjoitettujen ohjelman osien yhteistoiminnan. Voidaan myös käyttää yhden kielen kirjastoja toisesta kielestä. Esimerkiksi JVM:ssä ajettavista kielistä voi käyttää Java-kielen moninaisia kirjastoja. | Eri kielillä kirjoitettujen ohjelmakomponenttien sovittaminen yhteen voi vaatia erityisjärjestelyjä. |
suoritustehokkuus (efficiency) | Välikielen käytöstä seuraa jonkin verran tehokkuushaittoja kuten ohjelmien hidastumista. Haittoja voidaan lieventää erilaisilla optimoinneilla. Kun tietty virtuaalikone on laajasti käytetty (kuten JVM on), niin siihen tehdyistä optimoinneista hyötyvät usealla eri kielellä lähdekoodinsa kirjoittavat ohjelmoijat. | Kääntäjäohjelman voi laatia nojaten nimenomaisen lähdekieli–kohdekieli-parin ominaisuuksiin. Käännöksenä saadaan tehokkuusoptimoitua konekieltä. Riippuu tilanteesta, onko näin saavutettu tehokkuusetu käytännössä merkityksellinen. |
Virtuaalikoneet ovat nykyään aiempaa suositumpia, ja kehitys näyttäisi jatkuvan samaan suuntaan.
Tavukoodin suorittamisesta
Joidenkin erikoistietokoneiden laitteisto tukee JVM-tavukoodin suorittamista "suoraan". Useimpien kuitenkaan ei, vaan tavukoodi muunnetaan ensin konekieleksi.
Roskaa virtuaalikoneessa
Jotkin oliot luodaan väliaikaiseen tarkoitukseen. Olio voi olla käytössä esimerkiksi vain yhdellä ohjelman rivillä. Esimerkki: mitä tapahtuu luodulle puskurioliolle tällaisen tulostuskäskyn suorittamisen jälkeen?
println(Buffer(5, 2, 7, 32, 9)) // Huom. viittausta puskuriin ei jää mihinkään talteen
// tänne muita käskyjä, jotka eivät käytä äsken luotua puskuria
Olioita, jotka "leijuvat" muistissa ilman, että niihin pääsee ohjelmasta käsiksi, kutsutaan roskaksi (garbage). Roska vain vie muistitilaa turhaan.
Ohjelman tulisi vapauttaa roskalle varattu muisti muuhun käyttöön jo ohjelma-ajon aikana. Aina näin ei kuitenkaan käy. Klassinen bugityyppi on muistivuoto (memory leak): ohjelma varaa itselleen käyttöön tilaa tietokoneen muistista, mutta ei vapautakaan varaamaansa muistia myöhemmin, kun ei enää tarvitse sitä. Vähitellen vuotava ohjelma saattaa varata aina vain enemmän muistia kunnes resurssit ehtyvät ja ohjelma kaatuu.
Muistinhallinnasta
Joissakin ohjelmointiympäristöissä ohjelmoija huolehtii muistiresurssien vapauttamisesta nimenomaisin käskyin. Esimerkiksi C++ -ohjelmointikielellä olion voi luoda seuraavasti. Tämä käsky, joka varaa olion tiedoille muistia ja alustaa olion, muistuttaa aika lailla Scalaa:
JokuLuokka* testi = new JokuLuokka(); // Tämä ei ole Scalaa vaan C++:aa.
var testi = new JokuLuokka // Tämä on vastaava Scala-käsky.
Kun oliota ei enää tarvita, C++ -ohjelmoija voi poistaa testi
-muuttujan osoittaman
olion muistista näin:
delete testi; // Tämä ei ole Scalaa vaan C++:aa.
Näin tarpeeton olio ei jää roskaksi tietokoneen muistiin.
Emme ole oppineet delete
ä vastaavaa Scala-käskyä. Eikä sellaista olekaan.
Jos Scala-ohjelma on käynnissä pitkään, ja uusia olioita luodaan lisää, niin viekö se aina vain enemmän ja enemmän muistia?
Ei:
Automaattinen roskankeruu
Java-virtuaalikoneen toimenkuvaan kuuluu automaattinen roskankeruu (garbage collection). Virtuaalikone valvoo sitä, mitkä luoduista olioista ovat roskaa, ja vapauttaa roskalle varatun muistin muuhun käyttöön. Ohjelmoijan ei tarvitse puuttua asiaan.
JVM katsoo roskaksi kaikki sellaiset oliot, joihin osoittavia viittauksia ei ole enää mihinkään tallennettuna.
Automaattinen roskankeruu ehkäisee ison osan vuotovaarasta. Silti esimerkiksi Scala-ohjelmakaan ei ole immuuni muistivuodoille. Esimerkki: Ohjelma säilöö puskuriin viittauksia, jotka osoittavat olioihin. Jos näitä olioita ei poisteta puskurista, kun niistä on tullut ohjelman toiminnan kannalta tarpeettomia, niin JVM:n roskankerääjä ei siivoa niitä pois. Kuitenkin jos puskuriolio itse on roska, eikä siihen tallennettuihin olioihin viitata muualta kuin puskurista, niin myös puskurin sisältö tulkitaan roskaksi ja tulee kyllä automaattisesti siivotuksi. On ohjelmoijan vastuulla laatia ohjelma siten, että se ei jätä turhia viittauksia tallennetuiksi muuttujiin tai kokoelmiin.
Lisämateriaalia: oman ohjelman jakaminen
Miten voin jakaa Scalalla kirjoitetun sovellukseni toisten käytettäväksi?
Vaihtoehtoja on monta. Niistä lyhyesti alla ja lisää keväisellä Ohjelmointistudio 2 -kurssilla.
Jos käyttäjän koneella jo on Java-virtuaalikone (JVM) peruskirjastoineen, niin helpohko vaihtoehto on paketoida ohjelma suoritettavaksi JAR-tiedostoksi (executable JAR). Se on tiedosto, johon paketoidaan sekä lähdekoodista käännetyt
.class
-tiedostot että Scalan vaatimat kirjastot. Lisäksi tiedostoon kirjataan tieto siitä, mistä ohjelma lähtee käyntiin. Suoritettavien JAR-pakettien muodostaminen onnistuu esimerkiksi SBT-työkalulla (Simple Build Tool).-
Scala.js. Scalaa voi nyttemmin kääntää JVM-tavukoodin sijaan myös JavaScript-kieleksi, joka on nimestään huolimatta ihan eri asia kuin Java ja jota nykyaikaiset web-selaimet osaavat suorittaa. Tämä onnistuu Scala.js-työkalulla.
Natiivikääntäjät tuottavat tavukoodin sijaan tiettyyn ajoympäristöön sopivia käännöksiä; eri ympäristöihin täytyy tuottaa omat versionsa. Käännöksen mukaan voidaan liittää kirjasto, joka huolehtii mm. roskankeruusta. Scalaa ei useimmiten käännetä natiivikääntäjällä, mutta sekin onnistuu: Scala Native on tuore Scala-natiivikääntäjä. Se perustuu LLVM-kääntäjätyökaluihin.
"Java-wrapper" on ohjelma, jossa käännetty Java- tai Scala-koodipaketti on "kääritty" pienen suoritettavan ohjelman mukaan. Ajettaessa apuohjelma joko käyttää järjestelmään jo asennettua Java-virtuaalikonetta sovelluksen suorittamiseksi tai asentaa tarvittavan ympäristön.
Java Web Start on tekniikka, jossa ohjelman käynnistys tapahtuu käytännössä klikkaamalla linkkiä web-sivulla. Java Web Start -työkalu huolehtii ohjelman ja sen vaatiman Java-virtuaalikoneen asennuksista ja lopulta suorittaa ohjelman.
Osaa yllä mainituista vaihtoehdoista on esitelty tarkemmin esimerkiksi seuraavissa lähteissä:
Ole nettilähteitä tutkiessasi tarkkana, koska — kuten sanastosivummekin kertoo — "Java"-sanalla voidaan viitata joko Java-kieleen tai (Scala-ohjelmoijan kannalta relevantimmin) JVM:ään ja siihen liittyviin teknologioihin, eivätkä kirjoittajat aina ilmaise selvästi, kumpaa tarkoittavat.
Tärkeä maininta vielä loppuun: yleisiä ovat myös sellaiset web-sovellukset, joiden sisäisestä toimintalogiikasta huolehtii palvelinkoneessa ajettava ohjelma, vaikka käyttöliittymä toimii loppukäyttäjän omassa web-selaimessa. Scalaa käytetään myös tällaisten palvelinsovellusten laatimiseen, jolloin riittää, että tarvittavat työkalut (JVM yms.) on asennettu palvelinkoneelle eikä loppukäyttäjälle. Web-palvelimien ohjelmoinnista kerrotaan jatkokursseilla.
Lisämateriaalia: virtuaalikoneet ja kääntäminen
JVM eri ympäristöissä
JVM-toteutuksia on olemassa erittäin moniin erilaisiin ympäristöihin. Niitä löytyy paitsi erilaisista läppäreistä ja pöytäkoneista myös pienistä sulautetuista laitteista sekä palvelinfarmeista, joilla pyöritetään isoja verkkosivustoja. Vähäresurssisissa sulautetuissa järjestelmissä käytetään usein kevennettyä Java-versiota, mutta toiset järjestelmät pyörittävät samaa Java-versiota kuin pöytäkoneetkin.
Kyllä. Parkkilippukone onkin Javan takapiruna toimivan Oracle-yhtiön mainoksissa toistuva esimerkki.
Java Technology can be found in a broad spectrum of products across a diverse set of industries that produce anything from RFID readers to parking meters to ATMs to in-flight video systems to POS terminals to wearable systems (just to name a few) — — across a large number of hardware and OS platform configurations.
—Oraclen mainosteksti
JVM ei ole ainoa virtuaalikonetyyppi, jolla voi suorittaa Java- tai Scala-kielellä kirjoitettuja ohjelmia. Mm. Android-puhelimissa on JVM-standardista poikkeava virtuaalikonetoteutus.
Kaikissa yhteyksissä JVM:n kaltainen virtuaalikone ei ole hyvä ratkaisu. Tässä yksi esimerkki:
Kyllä. Taustalla toimiva roskankeruu vie silloin tällöin aikaa — vain hetken, mutta kuitenkin. Tämä yhdessä muiden seikkojen kanssa tekee vaikeaksi tai mahdottomaksi ennustaa erittäin tarkasti, kuinka kauan tietyn ohjelma-ajon vaiheen suorittaminen kestää. Ydinvoimalan kaltaisissa elintärkeissä järjestelmissä (life-critical system) tällainen ratkaisu ei kelpaa.
Elintärkeiden järjestelmien tarpeisiin on omia käyttöjärjestelmiään ja ohjelmointikieliään (ks. esim. real-time operating system, real-time computing, SPARK-ohjelmointikieli). Niissä käytetään muodollisempia ja varmempia työskentelytapoja kuin tavanomaisessa ohjelmointityössä (ks. esim. model checking, formal verification).
Voiko käännettyä ohjelmaa kääntää takaisin lähdekoodiksi?
Periaatteessa onnistuu ja osin ehkä käytännössäkin.
Kun käännetään korkean tason kielestä konekieleksi, osa lähdekoodin sisältämästä informaatiosta menetetään. Esimerkiksi muuttujien nimiä, kommentteja ja korkean tason kielen muita rakenteita, jotka jäsentävät kokonaisuutta, voidaan menettää.
Takaisinpäin käännetty koodi voi olla erittäinkin vaikeaselkoista, mikä riippuu käännettävästä ohjelmasta, ohjelmointikielestä ja kääntäjistä. Koodi ei välttämättä ole ymmärrettävissä niin helposti, että yritys olisi käytännössä vaivan arvoista.
On olemassa työkaluja, joilla takaisin päin kääntämistä voi tieten taiten hankaloittaa.
Hakusanoja: decompiler, reverse engineering, code obfuscation.
Kääntäminen vs. tulkkaaminen
Tulkkauksen ja kääntämisen raja on hämärä, varsinkin nykyään. Aina ei ole helppoa — tosin ei ehkä tarpeellistakaan — sanoa kummasta on kysymys. Välikielten ja virtuaalikoneiden käyttö sotkee asiaa osaltaan. Lisäksi soppaa hämmentää ajonaikainen kääntäminen (just-in-time compilation eli JIT), josta voit hakea lisätietoa internetistä vapaaehtoisena lisäharjoituksena.
Joihinkin ohjelmointiympäristöihin ei ollenkaan kuulu rajanvetoa lähdekoodin kirjoittamisen, kääntämisen/tulkkaamisen ja ajamisen välillä. Yksi esimerkki tästä on tuttu Scala-REPL.
JVM:n tavukoodin tutkiminen
(Tämä vapaaehtoinen tehtävä sopinee parhaiten opiskelijoille, jotka ovat jo ohjelmoineet ennen tätä kurssia. Tehtävän aiheeseen palataan Ohjelmointi 2 -kurssilla.)
Käännetyn Scala-ohjelman sisältämiä tavukoodikäskyjä voi tutkia javap- ja scalap-nimisillä apuohjelmilla. Selvitä internetistä, miten näitä apuohjelmia käytetään, ja tutki jotakin käännettyä Scala-luokkaa niillä.
Eri virtuaalikoneita
Tutki internetin avulla, mitä muita yleisesti käytettyjä virtuaalikoneita ja välikieliä on kuin JVM tavukoodeineen. Selvitä esimerkiksi, mikä on CLR sekä miten JavaScript-kieltä nykyään käytetään muiden ohjelmointikielten välikielenä WWW-selaimissa.
Lisämateriaalia: avoin lähdekoodi
Avoin lähdekoodi
Avoin lähdekoodi (open source) on jälleen yksi termi, jonka täsmällinen merkitys vaihtelee puhujasta riippuen. Keskeistä joka tapauksessa on, että ohjelman lähdekoodi on julkisesti saatavilla ja että sallitaan muidenkin kuin alkuperäisen tekijän hyödyntää sitä omissa ohjelmistoprojekteissaan.
On paljon erilaisia avoimen lähdekoodin lisenssejä, joilla koodin voi julkaista. Lisenssit eroavat toisistaan esimerkiksi sen suhteen, sallitaanko kaupallinen käyttö, ja vaaditaanko, että hyödyntäjä myös vastaavasti julkaisee oman alkuperäistä versioivan tai muuten käyttävän ohjelmansa.
Lähdekoodin avoimuus voi helpottaa ohjelmistojen kehittämistä eri tahojen — firmojen ja/tai yksityishenkilöiden — yhteistyössä. Lähdekoodin parissa työskentelevä yhteisö voi esimerkiksi ideoida ja toteuttaa muutoksia koodiin sekä arvioida yhteisön jäsenten tuotoksia laadun varmistamiseksi.
Avoimeen lähdekoodiin liittyy myös arvoja ja ideologioita. Lisää niistä ja teemasta muutenkin voi lukea esimerkiksi Wikipediasta.
Avoimen lähdekoodin projekteja on vähintään satoja tuhansia. Kuuluisin niistä lienee suomalaislähtöinen Linux-käyttöjärjestelmä. Muita esimerkkejä ovat ilmaisten ohjelmistojen puolesta liputtavan Mozilla-järjestön selain Firefox ja sähköpostiohjelma Thunderbird. Ja IntelliJ. Ja Scala.
Kunkin Scala API:n luokkaa kuvaavaan Scaladoc-sivun alkupäässä on Source-linkki, jonka kautta voit tarkastella kyseisen luokan koodia. Scalan lisenssi on hyvin salliva.
Lisämateriaalia: eettisiä kysymyksiä
Ohjelmoinnin etiikkaa
Edellä mainittiin avoin lähdekoodi. Se on yksi monesta teemasta, joita käsitellään alle upotetussa konferenssiesitelmässä. Videon loppupuolella esitellään eräs avoimen lähdekoodin hanke, ja yleisemminkin ottaen videolla oleva esitys toimii esimerkkinä siitä, millaisiin tavoitteisiin jotkut avoimen lähdekoodin kannattajat pyrkivät.
Muita esityksessä esille nousevia teemoja ovat: ohjelmoijan eettinen vastuu tuottamistaan ohjelmista; liike-elämän, ohjelmoinnin ja maailman parantamisen yhteen sovittaminen; joukkovalvonnan merkitys niillekin, joilla ei ole mitään salattavaa; turvallinen sähköposti ja miten sen leviämistä voi edistää.
Esitys on pääosin seurattavissa ilman ihmeempiä esitietoja, kunhan englannin kuuntelu sujuu. Harmi kyllä, videon alun lyhyt johdanto on varmaankin monelle kurssilaiselle sen epäselvin osa, joten selvennetään tuota alkua vähän: Kahdesta puhujasta ensimmäisenä äänessä pääsevä Martin Fowler viittaa aluksi useasti ketterään ohjelmistokehitykseen (agile software development), josta on tällä vuosituhannella muodostunut keskeinen ohjelmistokehityksen muoto ja jonka julkkiksiin hän itse lukeutuu. Tiiviisti sanoen ketteryys tarkoittaa tässä ohjelmistojen tuottamista siten, että muuttuviin tilanteisiin ja vaatimuksiin pystytään reagoimaan nopeasti ja asiakkaiden kanssa tehdään jatkuvaa yhteistyötä (lisää esim. Wikipediassa). Tämä esitys ei varsinaisesti käsittele ketterää ohjelmistokehitystä, vaan Fowler käyttää tuota teemaa johdatellessaan puheen kohti edellä mainittuja aiheita. Loppuosa on ymmärrettävissä vaikkei ketteryydestä mitään tietäisikään.
Vielä biteistä, luvuista ja tietotyypeistä
Luvun alkupäässä nousi aiheeksi se, että kaikki data, mm. luvut, esitetään tietokoneelle bitteinä. Monissa yhteyksissä datan sisäisestä binaarisesta esitysmuodosta ei kuitenkaan tarvitse välittää. Scala-ohjelma on melko korkean tason abstraktio tietokoneen sisäisestä toiminnasta, ja usein tällainen korkea abstraktiotaso riittää. Kuitenkin on tilanteita, joissa on tarpeen hahmottaa jotain Scala-ohjelmienkin toiminnasta bittitasolla. Äskeiset kaksi koodinpätkää ovat tästä esimerkkeinä. Selvitetään, mistä niiden oudoissa tulosteissa on kyse.
Bittimääristä
Luvun esittämiseen tarvittavan bittimäärän voi laskea muuntamalla luvun binaarimuotoon. Toisaalta voidaan tarkastella, montako erilaista arvoa tietyllä bittimäärällä voi kuvata:
- Nollalla bitillä ei voi kuvata kuin yhden arvon. (Ja yhdellä
arvolla ei voi ilmaista informaatiota; vrt.
Unit
luvusta 1.6.) - Yhdellä bitillä voi kuvata korkeintaan kaksi eri arvoa. Bitti riittää kuvaamaan esimerkiksi yhden totuusarvon, tai sen, onko luku 0 vai 1.
- Kahdella bitillä voi kuvata korkeintaan neljä eri arvoa. Voidaan esimerkiksi sopia, että: 00=0, 01=1, 10=2 ja 11=3, tai että 00=0, 01=1, 10=-2 ja 11=-1.
- Kolmella bitillä voi kuvata korkeintaan kahdeksan eri arvoa.
- Kun bittejä on jokin määrä n, niillä voi kuvata korkeintaan 2n eri arvoa.
Int
-arvoista
Scala-kieleen on suoritustehokkuussyistä määritelty, että kullekin Int
-arvolle varataan
tietty vakiomäärä bittejä — 32 kpl — joilla sen arvo varastoidaan. Scala on tässä
aivan tyypillinen kieli; monissa muissakin ohjelmointikielissä lukutyypeillä on tietty
bittimäärä vastaavasti.
Int
-tyyppisen muuttujan koko tietokoneen muistissa on siis aina 32 bittiä eli neljä
tavua (byte); yksi tavu on kahdeksan bittiä.
32 bitistä saa 232 erilaista nollan ja ykkösen yhdistelmää. Näin ollen
erilaisia mahdollisia Int
-arvoja on 232 kappaletta. Mielivaltaista
määrää lukuja ei tällä tyypillä voi esittää.
On määritelty, että Scalassa nämä 232 arvoa kattavat kokonaisluvut välillä:
-2147483648 ... +2147483647
eli-231 ... +231-1
Kääntäjä ei pidä huolta lukujen "mahtumisesta" tyyppeihin kuten ei virtuaalikonekaan.
On ohjelmoijan vastuulla, että hän ei käytä tämän kokonaislukuvälin ulkopuolisia arvoja
Int
-tyypin kanssa.
Itseisarvoltaan liian suurilla luvuilla laskemisella voi olla yllättäviä seurauksia,
kuten esimerkkilausekkeemme 2147483647 + 1
evaluoiminen osoitti. Kuten huomaat,
ensimmäinen laskettava ei ole arvalla valittu vaan juuri se suurin luku, joka
Int
-tyyppiin mahtuu. Seurauksena on tuloksen "pyörähtäminen ympäri".
Aina ei suju ammattilaisiltakaan
Lukutyyppien rajat voivat näkyä sovellusten käyttäjillekin, ellei ohjelmoija tätä ehkäise. Tälle kurssille aiemmin osallistuneet opiskelijat ovat havainneet ilmiön peleissä:
Vuonna 2014 Gangnam Style rikkoi YouTuben: "YouTuben ylläpitäjät joutuivat päivittämään koko palvelun, jotta se osaisi jatkaa laskemista. Gangnam Stylen katselulaskurin ylittämä kriittinen raja oli 32-bittinen kokonaisluku eli 2 147 483 647."
Vakavammasta tapauksesta oli kyse vuonna 1996 Cluster-avaruusaluksen räjähtäessä heti lähdön jälkeen. Englanninkielinen Wikipedia kertoo:
[The launch] ended in failure due to an error in the software design [which] caused inadequate protection from integer overflow. This resulted in the rocket veering off its flight path 37 seconds after launch, beginning to disintegrate under high aerodynamic forces, and finally self-destructing by its automated flight termination system. The failure has become known as one of the most infamous and expensive software bugs in history. The failure resulted in a loss of more than US$370 million.
Lentopelkoiset laittakoot silmät kiinni tämän kappaleen ajaksi. Vuonna 2015 nimittäin uutisoitiin, että Boeing 787 Dreamliner -lentokoneissa on ominaisuus, joka voi kytkeä koko koneesta virrat pois ellei konetta "buutata" vähintään 248 päivän välein. Mitähän 248 päivää on muissa mittayksiköissä?
Joissakin yhteyksissä ohjelmoijan virheestä voi myös muodostua tietoturvakysymys.
Double
-arvoista ja liukuluvuista
Kullekin Double
-arvolle on Scalassa varattu kahdeksan tavua eli 64 bittiä. Näiden 64
bitin avulla kukin Double
-arvo esitetään etumerkkibitin, kertoimen ja eksponentin
yhdistelmänä ns. liukulukuna (floating-point number; Double
on double precision
floating-point number).
Liukulukuesityksen yksityiskohdat eivät ole tämän kurssin kannalta kiinnostavia; niihin
voit halutessasi tutustua jonkin digitaalitekniikan alkeiskirjan tai internetistä
löytyvän materiaalin avulla. Nyt tärkeintä on hahmottaa, että koska resurssit
ovat rajalliset (eli bittejä on tarjolla vakiomäärä) Double
-arvoa ei voi esittää
muistissa mielivaltaisen tarkasti. Bittimäärä rajoittaa sitä, kuinka suuria ja tarkkoja
Double
-arvot voivat olla. 64 bitillä saavutetaan noin viidentoista merkitsevän numeron
tarkkuus.
Liukulukumuodon rajallisen tarkkuuden vuoksi Double
-arvoilla laskiessa syntyy usein
pieniä "epätarkkuuksia", jollaisesta näit esimerkin, kun evaluoit lausekkeen 100.0 / 11.0 * 11.0
.
Osamäärää ei voitu esittää "loputtoman tarkasti", ja kun epätarkka osamäärä kerrottiin
taas yhdellätoista, ei lopputulos ollutkaan täsmälleen sata.
Rajallista tarkkuutta havainnollistaa myös tämä esimerkki:
2.999999999999999res0: Double = 2.999999999999999 2.9999999999999999res1: Double = 3.0
Rajallisen tarkkuuden vuoksi liukulukuarvoja ei usein ole järkevää esimerkiksi vertailla
keskenään ==
-operaattorilla.
Ilmiön seuraukset voivat muutenkin olla joskus hämmentävän näköisiä. Kokeillaan lähimpään
kokonaislukuun pyöristävää round
-metodia:
5.499999999999999.roundres2: Long = 5 5.4999999999999999.roundres3: Long = 6
Tässä siis jälkimmäinen literaali tarkoittaa itse asiassa liukulukua 5.5, joka pyöristyy ylöspäin.
Mikäs tuo Long
tuossa äskeisessä REPL-esimerkissä muuten oli?
Scalan muista lukutyypeistä
Lukujen esittämiseen käytetään Scalassa tyypillisimmin Int
- ja Double
-tietotyyppejä.
Aina ne eivät kuitenkaan ole tarkoitukseen riittäviä tai sopivimpia, joten muitakin
vaihtoehtoja on tarjolla. Ohjelmoijan tulee valita sopiva tietotyyppi, johon käsiteltävät
luvut "mahtuvat". Joissain tilanteissa ohjelmoija haluaa optimoida resurssien käyttöä ja
käyttää Int
iä ja Double
a vähäbittisempiä tietotyyppejä.
Tässä eräitä Scalan tietotyyppejä:
Tarvetta Long
ille?
Stars-ohjelmassa tallensimme kunkin tähden Henry Draper -tunnisteluvun
Int
-tyyppiseen id
-muuttujaan. Tuohon taivaalla näkyviä
tähtiä käsittelevään ohjelmaamme ratkaisu riittää helposti.
Jos tavoitteemme olisivat tähtitieteellisemmät, Int
-tyypin
rajat alkaisivat tulla vastaan.
- Suuria kokonaislukuja voi esittää käyttäen kahdeksantavuista
tietotyyppiä
Long
(long integer), jonka puhti riittää 1018-suuruusluokkaan. - Resursseja voi säästää käyttämällä kokonaislukujen kuvaamiseen
tietotyyppiä
Short
(short integer; 2 tavua) taiByte
(1 tavu). Liukuluvuille on vastaavasti käytettävissä tyyppiFloat
(floating-point number; 4 tavua). Tämä kannattaa vain silloin, kun tietää resurssioptimoinnin tarpeelliseksi kyseisessä yhteydessä. - Kun mikään ei riitä, voi käyttää
scala.math.BigInt
- jascala.math.BigDecimal
-tyyppejä, joilla voi kuvata mielivaltaisen kokoisia lukuja. Niillä ei ole vakiobittimäärää, vaan tilaa varataan muistista joustavasti kyseisen arvon perusteella. Haittapuolena on suuremman muistinkäytön lisäksi se, ettei tietokone laske näillä tyypeillä yhtä nopeasti kuin vakiobittisillä lukutyypeillä.
Tällä kurssilla sinun ei tarvitse käyttää muita lukutyyppejä kuin Int
iä ja Double
a.
Voit hakea muista tyypeistä lisää tietoa internetistä, jos aihe kiinnostaa.
Tehtävä: värejä lukuina
Yllä mainittiin, että kukin kuvan pikseli voidaan esittää lukuina, jotka vastaavat pikselin värisävyä.
Yksi tapa kuvata pikseli on RGB-muoto, jossa väri muodostuu punaisen (Red), vihreän (Green) ja sinisen (Blue) komponentin yhdistelmänä. Kukin komponenteista on luku. Lukujen suuruusluokka riippuu siitä, montako värisävyä on käytössä. Esimerkiksi näin:
- Kukin pikselin kolmesta RGB-komponentista on yksi 256:sta lukuarvosta nollan ja 255:n väliltä. Komponentit kertovat "paljonko punaista", "paljonko vihreää" ja "paljonko sinistä" kyseisessä pikselissä on.
- Tällöin kullakin pikselillä on yksi 2563:sta eli noin 16,8 miljoonasta värisävystä. Kutakin pikseliä kuvaavien lukujen tallentamiseen tarvitaan 3 * 8 bittiä eli kolme tavua muistia, koska kahdeksalla bitillä eli yhdellä tavulla voi esittää juuri 28 eli 256 eri arvoa.
Kurssin o1
-kirjaston tuttu tyyppi Color
mallintaa värejä juuri tuollaisessa RGB-muodossa.
Asia on helppo havaita:
Lisää väreistä
Väriolioilla voi tehdä muutakin. Lisää voit lukea dokumentaatiosta, mutta tässä pari esimerkkiä.
Väristä on helppoa tehdä vähän tummempi tai vaaleampi versio:
val vahanVaaleampi = Brown.lightervahanVaaleampi: Color = Color(174, 64, 64) val kahdestiTummempi = Brown.darker.darkerkahdestiTummempi: Color = Color(133, 33, 33) val testikuva = rectangle(100, 100, Brown).leftOf(rectangle(100, 100, vahanVaaleampi)) .leftOf(rectangle(100, 100, kahdestiTummempi))testikuva: Pic = combined pic testikuva.show()
Värillä on R-, G- ja B-komponenttien lisäksi myös ns. alfakanava eli läpinäkymättömyyttä (opacity) kuvaava tieto:
Blue.opacityres6: Int = 255 val lapikuultavaPunainen = Color(255, 0, 0, 100)lapikuultavaPunainen: Color = Color(255, 0, 0, 100) rectangle(300, 100, lapikuultavaPunainen).onto(circle(200, Blue)).show()
opacity
on
vain 100, on varsin läpikuultava. Nolla
olisi tarkoittanut täysin läpinäkyvää.Yhteenvetoa
- Tietokoneohjelmien käsittelemä data — mukaan lukien ohjelmat itse — esitetään tietokoneen muistissa bitteinä.
- Scala-ohjelmakoodia ei ajeta sellaisenaan. Tyypillisimmin Scala-lähdekoodi käännetään ensin tavukoodiksi kutsutulle välikielelle. Kääntämisestä vastaa erillinen apuohjelma.
- Tavukoodi puolestaan muunnetaan kohdekoneelle sopivaksi konekieleksi
ohjelma-ajon yhteydessä. Tavukoodiohjelman ajamisesta vastaa
virtuaalikoneeksi kutsuttu apuohjelma.
- Scala-ohjelmien ajamiseen käytetään tavallisesti JVM-nimistä virtuaalikonetta, joka on alun perin luotu Java-ohjelmoijia varten.
- Virtuaalikoneen käyttö parantaa ohjelmien siirrettävyyttä ja erilaisten komponenttien yhdisteltävyyttä.
- Usein käytetyille lukutyypeille kuten
Int
jaDouble
on määritelty tietty bittimäärä, jolla niiden arvot kuvataan tietokoneen muistissa. Tämä asettaa tiettyjä rajoituksia näiden tyyppien käytölle. - Lukuun liittyviä termejä sanastosivulla: sovelluskehitin eli IDE; lähdekoodi, korkean tason kieli, konekieli, kääntäjä, prosessori; välikieli, tavukoodi; virtuaalikone, JVM eli Java-virtuaalikone; Java, JavaScript, Scala.js, komentorivi, roskankeruu; bitti, tavu; liukuluku; RGB.
Syvälle Scala-työkalujen tai tietokoneen laitteiston sisään ei tässä menty. Tässä luvussa luotu yleiskuva syventyy jatkokurssilla CS-A1120 Ohjelmointi 2, jolla bitit viuhuvat.
P.S.
Tässä vielä yksi:
0.1 + 0.1 + 0.1res7: Double = 0.30000000000000004
Palaute
Huomaathan, että tämä on henkilökohtainen osio! Vaikka olisit tehnyt lukuun liittyvät tehtävät parin kanssa, täytä palautelomake itse.
Tekijät
Tämän oppimateriaalin kehitystyössä on käytetty apuna tuhansilta opiskelijoilta kerättyä palautetta. Kiitos!
Materiaalin luvut tehtävineen ja viikkokoosteineen on laatinut Juha Sorva.
Liitesivut (sanasto, Scala-kooste, usein kysytyt kysymykset jne.) on kirjoittanut Juha Sorva sikäli kuin sivulla ei ole toisin mainittu.
Tehtävien automaattisen arvioinnin ovat toteuttaneet: (aakkosjärjestyksessä) Riku Autio, Nikolas Drosdek, Joonatan Honkamaa, Jaakko Kantojärvi, Niklas Kröger, Teemu Lehtinen, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä ja Aleksi Vartiainen.
Lukujen alkuja koristavat kuvat ja muut vastaavat kuvituskuvat on piirtänyt Christina Lassheikki.
Yksityiskohtaiset animaatiot Scala-ohjelmien suorituksen vaiheista suunnittelivat Juha Sorva ja Teemu Sirkiä. Teemu Sirkiä ja Riku Autio toteuttivat ne apunaan Teemun aiemmin rakentamat työkalut Jsvee- ja Kelmu.
Muut diagrammit ja materiaaliin upotetut vuorovaikutteiset esitykset laati Juha Sorva.
O1Library-ohjelmakirjaston ovat kehittäneet Aleksi Lukkarinen ja Juha Sorva. Useat sen keskeisistä osista tukeutuvat Aleksin SMCL-kirjastoon.
Tapa, jolla käytämme O1Libraryn työkaluja (kuten Pic
) yksinkertaiseen graafiseen
ohjelmointiin, on saanut vaikutteita tekijöiden Flatt, Felleisen, Findler ja Krishnamurthi
oppikirjasta How to Design Programs sekä Stephen Blochin oppikirjasta Picturing Programs.
Oppimisalusta A+ luotiin alun perin Aallon LeTech-tutkimusryhmässä pitkälti opiskelijavoimin. Nykyään tätä avoimen lähdekoodin projektia kehittää Tietotekniikan laitoksen opetusteknologiatiimi ja tarjoaa palveluna laitoksen IT-tuki. Pääkehittäjänä on tällä hetkellä Markku Riekkinen, jonka lisäksi A+:aa ovat kehittäneet kymmenet Aallon opiskelijat ja muut.
A+ Courses -lisäosa, joka tukee A+:aa ja O1-kurssia IntelliJ-ohjelmointiympäristössä, on toinen avoin projekti. Sen ovat luoneet Nikolai Denissov, Olli Kiljunen, Nikolas Drosdek, Styliani Tsovou, Jaakko Närhi ja Paweł Stróżański yhteistyössä Juha Sorvan, Otto Seppälän, Arto Hellaksen ja muiden kanssa.
Kurssin tämänhetkinen henkilökunta löytyy luvusta 1.1.
Joidenkin lukujen lopuissa on lukukohtaisia lisäyksiä tähän tekijäluetteloon.
opacity
on suurin mahdollinen.