slide1 n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
CSE-A1110 Ohjelmointi 1 PowerPoint Presentation
Download Presentation
CSE-A1110 Ohjelmointi 1

Loading in 2 Seconds...

play fullscreen
1 / 41

CSE-A1110 Ohjelmointi 1 - PowerPoint PPT Presentation


  • 180 Views
  • Uploaded on

CSE-A1110 Ohjelmointi 1. Luento 4. Kierrokselta 3 kierrokselle 4. Juha Sorva juha.sorva@aalto.fi. Luennon sisältö. Palautteesta poimittua Johdanto kierroksen 4 alkupäähän Palautteesta poimittua Johdanto kierroksen 4 loppupäähän. Luvun 3.7 Interval -tehtävästä:

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about 'CSE-A1110 Ohjelmointi 1' - mahina


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
slide1

CSE-A1110 Ohjelmointi 1

Luento 4

Kierrokselta 3 kierrokselle 4

Juha Sorva

juha.sorva@aalto.fi

luennon sis lt
Luennon sisältö
  • Palautteesta poimittua
  • Johdanto kierroksen 4alkupäähän
  • Palautteesta poimittua
  • Johdanto kierroksen 4 loppupäähän
slide3

Luvun 3.7 Interval-tehtävästä:

"Tätä tänne tultiin tekemään. Motivaatio oli koetuksella ja turhautuminen lähellä. Parilla ensimmäisellä palautus-kerralla v-käyrä nousi vauhdilla, kun hieman tarkemmin luki dokumentaatiota tajusi vian löytyvän väärin nimetyistä parametreista (hups...) Sen jälkeen huomasikin autuaasti unohtaneensa kirjoittaneensa joitain metodeja, mutta kun näistä ja huolimattomuus-virheistä rämmittiin läpi oli viimeisen arvostelun kalahtaessa sisään aivan uskomaton voittajaolo. Oppimista tapahtui enemmän kuin missään pitkiin aikoihin."

slide4

”Miksi ylpiäätään käynnistysolio pitää luoda? Onko tämä tyypillistä vain Scalalle, vai onko se näin myös muilla ohjelmointikielillä?”

  • Scala on ”puhdas” oliokieli sikäli, että Scala-sovelluksen kaikki pääosat on kuvattu olioina (mistä lisää myöhemmin tänään).
  • Scala-sovelluksessa on useita olioita, jotka toimivat yhteen (luku 2.2). On määriteltävä, mikä olio aloittaa toiminnan.
  • Vaikka käynnistysolio on usein yksinkertainen, sekin on ihan kunnon olio ja sille voi määritetellä metodeita yms.
  • Useissa muissakin ohjelmointikielissä vastaava ”käynnistyskohdan määrittely” on pakollinen. Toisissa ei ole.
  • Esim. Javassa on määriteltävä sovellukselle ”käynnistysmetodi” jonkin luokan sisään.

(On olemassa myös sellainen tapa ajaa tietynlaisia Scala-ohjelmia, jossa käynnistysoliota ei käytetä. Kiinnostuneet voivat etsiä lisätietoja esimerkiksi hakusanoilla Scala application vs. script.)

slide5

Lisäys edelliseen:

Scalassakin on olemassa tapa määritellä käynnistysoliolle erikseen käynnistysmetodi. Tämä tapa on hieman monimutkaisempi emmekä käytä sitä kurssilla, mutta voit törmätä siihen muissa yhteyksissä.

Käynnistysmetodin nimi on main, kuten useammassa muussakin kielessä.

object MunSovellus {

def main(parametritSovellukselle: Array[String]) = {

// suoritettavat käskyt tänne

// samaan tapaan kuin ”extends App”-käynnistysoliossa

}

}

slide7

Onko käynnistysolio sama tai osittain sama kuin ohjelmien .exe-tiedostot?

    • Kyllä laajasti puhuen kyse on samasta teemasta eli ohjelmien käynnistämisestä.
    • exe-päätteiset tiedostot (http://en.wikipedia.org/wiki/EXE) ovat windowsinsukuisissa käyttöjärjestelmissä käytettyjä tiedostoja, joihin tallennetaan ohjelmia ja joskus myös ohjelmien käyttämiä muita resursseja.
    • Käynnistysolio on Scala-ohjelman osa, jota vastaavaa tiedostoa voidaan käyttää Scala-ohjelman käynnistämiseen.
    • Scala-ohjelmien ajamisesta lisää luvussa 5.2.
    • (Scala-ohjelmakin on mahdollista kääntää exe-muotoiseksi, joskaan näin ei erityisen usein tehdä.)
slide8

Syyt tehtävän 1 vastauksille?

iso > pieni

pieni < iso

!(iso > pieni)

"P" == "NP"

"kissa" + "kala" != "kissakala"

"mun isä" > "sun isä"

false == true

true == true

false == false

(10 < 20) != (20 > 10)

"kissa" < 100

"100" < 100

"100" == 100

false, toisin kuin eräissä muissa kielissä.

false Unicode-”aakkosjärjestyksen” perusteella. Ei pituusvertailu!

Merkkijonon ja luvun välistä suuremmuussuhdetta ei ole määritelty.

Yhtäsuuruusvertailu sen sijaan onnistuu. Merkkien 1, 0 ja 0 muodostama merkkijono on eri asia kuin lukuarvo sata, eli tämä on false.

slide9

Syyt tehtävän 6 vastauksille?

if (kommentti == "Jee") "positiivinen" else "neutraali" + " reaktio"

false

koska arvo on "Jaa"

(

)

 neutraali reaktio

if (kommentti == "Jaa") "neutraali" else "positiivinen" + " reaktio"

true

(

)

 neutraali

(if (kommentti == "Jaa") "neutraali" else "positiivinen") + " reaktio"

true

(

)

 neutraali reaktio

???

"Se oli " + if (kommentti == "Jaa") "neutraali" else "positiivinen" + " reaktio."

)

(

illegal start of simple expression

true

"Se oli " + (if (kommentti == "Jaa") "neutraali" else "positiivinen") + " reaktio."

)

(

 Se oli neutraali reaktio.

slide10

Sisäkkäiset/peräkkäiset if-käskyt?

Tehtävä 8 oli vaikea!

Tutkitaan asiaa debuggerissa (luku 4.2).

slide13

... miksei tällainen?

override def toString = {

if (this.isGoalless)

"maalittoman pelin kuvaus"

else

""

if (this.isTied)

"tasatilanteen kuvaus"

else

""

if (this.isHomeWin)

"kotijoukkue voitolla"

else

""

if (this.isAwayWin)

"vierasjoukkue voitolla"

else

""

}

Tällainen toimi, mutta...

override def toString = {

if (this.isGoalless)

"maalittoman pelin kuvaus"

else if (this.isTied)

"tasatilanteen kuvaus"

else if(this.isHomeWin)

"kotijoukkue voitolla"

else if (this.isAwayWin)

"vierasjoukkue voitolla"

else

""

}

Muista: Palautusarvo on viimeiseksi evaluoitavan lausekkeen arvo.

Näistä jompikumpi lauseke evaluoidaan.

Sitten näistä jompikumpi.

Ja näistä.

Tasan yksi haaroista suoritetaan. Kussakin haarassa on vain yksi lauseke. Kyseisen lausekkeen arvo on metodin palautusarvo.

Lopuksi evaluoidaan näistä kahdesta yksi. Se on viimeinen evaluoitava lauseke. Metodi toimii oikein vain away win-tapauksessa ja palauttaa muuten tyhjää.

Edeltävät if-käskyt olivat merkityksettömiä.

slide14

Miksei tämä toimi? Tarkoitus olisi, että metodin palautusarvon tyyppi on String ja se palauttaa kuvauksen saamastaan luvusta.

def kokeilu(luku: Int) = {

if (luku < 0)

"negatiivinen"

else if (luku > 0)

"positiivinen"

}

Mitä, jos luku onkin nolla? Mitä silloin palautetaan? Metodia laadittaessa ei ole huomioitu tätä tapausta, ja tämän seurauksena metodin palautusarvon tyyppi ei tule onnistuneesti päätellyksi.

slide15

Okei, mutta miksei tämäkään toimi?

def kokeilu(luku: Int) = {

if (luku < 0)

"negatiivinen"

else if (luku > 0)

"positiivinen"

else if (luku == 0)

"nolla"

}

Nämä kolme haaraa kattavat kaikki mahdolliset tapaukset, minkä ohjelmoija itse helposti huomaa.

Kuitenkaan tietokone ei tällaisten if-haarojen kattavuutta pysty yleisesti ottaen arvioimaan eikä se sitä edes lähde yrittämään.

Scala-työkaluston näkökulmasta olisi edelleen mahdollista, että jokin näistä kolmesta ehdosta ei toteudu; niinpä lopputulos on samankaltainen kuin edellisessäkin yrityksessä.

slide16

No, miten se sitten saadaan toimimaan?

def kokeilu(luku: Int) = {

if (luku < 0)

"negatiivinen"

else if (luku > 0)

"positiivinen"

else

"nolla"

}

Tarkastus if (luku == 0) ei ole vain tarpeeton, vaan suorastaan haitallinen.

Otetaan se pois.

if (luku == 0)

Nyt else-haaraan mennään kaikissa tapauksissa, joissa kumpikaan edellisistä ehdoista ei toteutunut. On tietokoneenkin näkökulmasta ilmeistä, että kaikissa tapauksissa päädytään palauttamaan jokin merkkijono.

slide17

”Goblin on kyllä aika armoton metodien nimien kanssa”

  • ”Ohjelmani toimi alussa täysin moitteettomasti, mutta palautettuani sen sain aluksi 0 pistettä, sillä en ollut luonut muuttujia end ja start.”
    • Mainitut muuttujat ovat osa pyydetyn luokan julkista rajapintaa ja oli siksi mainittu dokumentaatiossa.
    • Interval-luokan dokumentaation lukeneen henkilön tulisi voida luottaa siihen, että lukuväliä kuvaavan olion alkuun ja loppuun pääsee käsiksi kyseisillä nimillä.
    • Spesifikaation perusteella laaditun ohjelmakomponentin toiminta ei ole moitteetonta, ellei se toteuta määrättyä rajapintaa nimineen kaikkineen.
    • Julkisten (scaladoceissa näkyvien) muuttujien ja metodien nimeämisessä pitää mennä dokumentaation mukaan.
    • Luokkien yksityiset osat voi nimetä haluamallaan tavalla.
slide18

Miksi pitää luoda tavallaan sama asia kahdesti? Esimerkiksi ottelun homeGoals vs. homeCount tai kategorian favorite vs. fave

    • Vastaus on sidoksissa siihen, mitä kyseisellä luokalla halutaan tehdä: minkä toimintojen halutaan olevan osa julkista rajapintaa?
    • Esimerkiksi Match-luokan haluttiin esimerkissämme olevan sellainen, että maalit lisätään yksi kerrallaan.
    • Otteluoliolta voi kysyä esim. kotimaalien määrää, mutta sitä ei voi mielivaltaisesti asettaa vaan on käytettävä addHomeGoal-metodia.

Ei haluttu, että tämän muuttujan arvoa voi muuttaa luokan ulkopuolelta. Siksi private.

class Match(val home: Club, val away: Club) {

private var homeCount = 0

private var awayCount = 0

def addHomeGoal() = {

this.homeCount = this.homeCount + 1

}

def homeGoals = this.homeCount

Luokka Match huolehtii sen päivittämisestä asianmukaisesti.

Koska kuitenkin halutaan, että luokan ulkopuolelta voidaan metodikutsulla tiedustella maalimäärää, niin laaditaan tätä varten julkinen metodi.

slide19

Category-luokan tapauksessa taas haluttiin, että suosikkia ei voi asettaa mielivaltaisesti, vaan suosikki määrittyy hallitusti sen mukaan, mitä kokemuksia kategoriaan lisätään.

  • Kuitenkin haluttiin, että kategoriaoliolta voi kysyä sen suosikkia.

class Category(val name: String, val unit: String) {

private val experiences = Buffer[Experience]()

private var fave: Option[Experience] = None

def favorite = this.fave

def addExperience /* jne. */

Yksityinen muuttuja, jolla sisäisesti pidetään kirjaa suosikista.

Julkinen metodi, jolla voi kysyä suosikkia (eli käytännössä sisäisen fave-muuttujan kysymishetkistä arvoa) mutta jolla ei voi muuttaa sitä.

Vastaava "saa katsoa muttei koskea"-tyyppinen tavoite ja ratkaisumalli esiintyivät myös luvun 2.8 Counter-esimerkissä.

slide20

"Miten muuten yleensä kannattaisi hoitaa tilanteet, joissa olion tulisi voida kertoa jonkin var-muuttujansa arvo, mutta sitä ei saisi luokan ulkopuolelta muuttaa? Syntyy ärsyttävä nimeämisongelma, kun pitäisi keksi var-muuttujalle jokin muu nimi kuin palauttamisen hoitavalla metodilla."

On totta, että tämä on joskus vähän ärsyttävää.

Kovin massiivinen ongelma se ei kuitenkaan ole.

  • Jos kahta yhtä hyvää nimeä ei keksi, niin kannattanee käyttää parempaa julkisen metodin nimenä ja huonompaa yksityisen muuttujan nimenä.
  • Näin priorisoidaan luokan käytön helppous.
  • Jotkut harrastavat lyhenteitä sisäisten muuttujien nimissä. Pitää kuitenkin varoa, ettei mene liian epäselväksi.
  • Kurssin tehtävissä julkiset nimet on yleensä spesifioitu etukäteen.

Jotkut harrastavat etuliitteitä, esim. mFavorite (muuttuja, member).

  • Eräillä kielillä (esim. Java) ohjelmoidessa harrastetaan nimeämistä tyyliin favorite (muuttuja) ja getFavorite (metodi).
  • Tämä ei yleensä tee luokasta maksimaalisen kätevää käyttää.
  • Scalassa näin ei ole tapana tehdä.
slide22

Interval-tehtävästä:

  • "Onko tämmöinen samojen funktioiden pyörittely virallinen ja yleisesti käytössä oleva tapa kirjoittaa koodia?"
  • Saatat tarkoittaa yhtä tai molempia seuraavista:
  • Laaditaanko usein paljon metodeita, jotka tekevät samankaltaisia asioita keskenään (esim. contains-, isIn-, isLaterThan-metodit)?
    • Vastaus: Riippuu siitä, mitä luokalla halutaan tehdä, eli millainen rajapinta halutaan tarjota luokan käyttäjälle. Mutta ei se harvinaista ole.

Thin versus rich interfaces represents a commonly faced trade-off in object-oriented design. The trade-off is between the implementers and the clients of an interface. A rich interface has many methods, which make it convenient for the caller. Clients can pick a method that exactly matches the functionality they need. A thin interface, on the other hand, has fewer methods, and thus is easier on the implementers. Clients calling into a thin interface, however, have to write more code. Given the smaller selection of methods to call, they may have to choose a less than perfect match for their needs and write extra code to use it. http://www.artima.com/pins1ed/traits.html

B) Toteutetaanko metodeita usein siten, että kutsutaan niistä toisia

metodeita (kuten edellisen sivun esimerkkiratkaisussa)?

Vastaus: Kyllä. Näin usein kannattaa tehdä, jos mahdollista.

Se vähentää toistoa koodissa, ja on usein hyvin kätevää.

slide23

Ei ole harvinaista, että elegantti ja muutenkin laadukas toteutus on myös ohjelmakoodin määrältä pienempi kuin huonommat.

Tämä kannattaa pitää mielessä muun muassa silloin, kun kuulet puhuttavan lines of code -laskelmista (http://en.wikipedia.org/wiki/Source_lines_of_code), joita on joskus käytetty jopa ohjelmointityön tuottavuusmittareina.

If we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent".

EDSGER W. DIJKSTRA

Toki on erittäin mahdollista myös liioitella tiivistäessä ja huonontaa luettavuutta saavuttamatta mitään etuja. Kohtuus kaikessa!

slide24

null vs. Nonevielä kerran?

    • Molempia voi käyttää ilmaisemaan "ei mitään".
    • null on yleiskäyttöinen arvo, jota voi ilman erillistä ilmoitusta esim. sijoittaa muuttujaan merkiksi siitä, että muuttujalla ei ole varsinaista arvoa.
    • None on tyyppiä Option-oleva arvo, jota käytetään sellaisissa kohdissa, jotka on erikseen merkitty "valinnaisiksi" Option-tyyppiä käyttäen.
    • Kurssilla käytetään ensisijaisesti Option-tyyppiä ja Nonea.
    • nullista kannattaa olla tietoinen, mutta sitä ei tällä kurssilla tarvitse käyttää mihinkään ja tuskin kannattaakaan.
slide25

Selitys tehtävälle 13?

Virhe liittyy siihen, että yritetään selvittää olemattoman olion arvosana.

Totta. Koska suosikkikokemusta ei ole, sen arvosanaa ei ole määritelty eikä tietokone tiedä, miten isBetterThan-metodin sisältämän lausekkeen another.rating voisi evaluoida.

Virhe liittyy arvoon, joka fave-muuttujalla on aluksi.

Totta. Luodun kategoriaolion fave-muuttujan arvo oli tässä ohjelmaversiossa null. Juuri siksi null on välitetty ensin chooseBetter- ja edelleen isBetterThan-metodille.

Virhe syntyy, kun käynnissä on metodikutsu, jossa this-muuttujassa on null-viittaus.

Tarua. Kaikissa this-muuttujissa on kyllä viittaus olemassaolevaan olioon. this ei itse asiassa koskaan voi olla null;null pointer exception nimenomaan estää tällaisen mahdottoman tilanteen.

Virhe syntyy välitettäessä null-arvoa parametriksi kokemusolion metodille; nullia ei voi antaa parametriksi.

Tarua; ks. toinen väite.

slide26

"Kuinka paljon null-viittausta käytetään muissa kielissä? Kerta se on Scalassa vähemmän käytetty."

    • Riippuu kielestä, mutta useimmissa tämän hetken valtakielissä (esim. C/Java) sitä käytetään erittäin paljon.
    • osin kätevien vaihtoehtojen puutteen vuoksi
    • osin ohjelmointikulttuurillisista syistä
    • osin myös tapauskohtaisista suoritustehokkuus- tai kätevyyssyistä
    • yms.

Option vaatii tiettyjä lisätoimenpiteitä tietokoneelta ohjelman suorituksen aikana (Some-olioiden luominen, arvojen noutaminen niiden sisältä). Tällä ei usein ole mitään käytännön merkitystä, mutta joskus on.

slide27

Usein lainatut ohjelmien tehokkuusoptimoinnin "kultaiset säännöt":

    • 1. sääntö: Don't.
    • 2. sääntö: Don't... yet.
    • 3. sääntö: Profile first.

Profiloimalla selvitetään tarvittaessa, mitkä ohjelman osat ovat sellaisia, joiden vaikutus kokonaisuuden suoritustehokkuuteen on käytännössä aidosti merkityksellinen. On yleistä, että pieni osuus ohjelmakoodista ratkaisee kokonaisuuden tehokkuuden.

Tehokkuudella on merkitystä suuria datamääriä käsitellessä ja/tai, kun ohjelmia ajetaan pientietokoneilla tms. Muut laatukriteerit ovat monessa yhteydessä oleellisempia. Riippuu tilanteesta!

Aiheesta lisää jatkokursseilla.

slide28

"Milloin sitten arvoa null kannattaa käyttää?"

  • "Jos null kerran on niin ongelmallinen, miksi sitä ylipäätään käytetään?"
    • Osa ohjelmoijista onkin sitä mieltä, että esim. Scalassa ei null-arvoa pitäisi ollakaan.
    • Kuitenkin se on eduksi esimerkiksi edesauttamassa Scalan yhdisteltävyyttä muiden kielten kanssa (mistä vähän lisää luvussa 5.2).
    • Tilanteesta riippuen null-arvon käyttäminen saattaa olla kätevää tai ohjelman suorituskyvyn kannalta perusteltua.
    • Myös tuttuus muihin kieliin tottuneille ohjelmoijille voi joskus olla syy.
slide29

Miksi null esiteltiin kurssilla? Mitä siitä pitäisi saada irti?

    • null-arvo on monessa muussa ohjelmointikielissä erittäin yleinen ja kuuluu kenen tahansa ohjelmoijan yleissivistykseen.
    • Kurssi ei ole vain Scala-kurssi.
    • Kun etsit itse tietoa netistä tai tutustut muiden tekemään Scala-koodiin, voit törmätä null-arvoon, vaikka sitä ei jatkossa tämän kurssin materiaalissa suositakaan.
    • Aiheen esilletuominen mahdollisti sivistävän eri ratkaisutapojen vertailun sekä lyhyen pohdinnan ohjelmointikielten suunnittelu-periaatteista, joka auttaa ymmärtämään, miksi teemme niin kuin teemme.
    • Saatat myös (kaikesta huolimatta) ohjelmoidessasi törmätä NullPointerException-virhetilanteisiin, ja on hyvä tietää, mistä on kyse.
slide31

"Ovatko Option-oliot ja Some-oliot sama asia? Vai onko siis Option-olion arvona Some-olio?"

    • Option on Somen ja Nonen yläkäsite.
    • Kaikki Some-oliot ovat Option-olioita.
    • Vrt. "kaikki ihmiset ovat kuolevaisia", "kaikki kissat ovat eläimiä".
    • Jokainen Option-tyyppinen olio on joko jokin Some-oliotai None.
    • Vrt. "jokainen ihminen on joko joku kuolevainen tai Chuck Norris".
    • Option-tyyppisen muuttujan arvona voi olla Some-tyyppinen olio tai None.

Option

None

Some

slide32

Miksi muuttujan tyypiksi tulee Option[String]?

    • Muuttujan tyyppi määräytyy siihen sijoitettavan lausekkeen perusteella. Tässä sijoitetaan if-lausekkeen arvo.
    • if-lausekkeen arvo on joko Some[String]-olio tai None-olio. Muuttujan pitää olla sellaista tyyppiä, johon voi tallentaa näistä kumman vaan.
    • Option on Scalassa määritelty Somen ja Nonen yläkäsitteeksi.
    • Option-tyyppisessä muuttujassa voi olla tallessa viittaus näistä kumpaan vaan.
    • Tähän nojaten Scalan tyyppipäättely määrittää muuttujan tyypiksi juuri Option[String].

val realGoodExperience = if (exp.rating >= 10) Some(exp.name) else None

slide33

Entä miksi muuttujan tyypiksi tulee Any, jos else-osion poistaa? Mikä se on?

    • Tämä (ei kovin mielekäs) sijoitus tarkoittaa käytännössä samaa kuin jos siinä lukisi if (exp.rating >= 10) Some(exp.name) else (), missä tyhjät sulut tarkoittavat Unit-arvoa.
    • Any on Scalan "yleistietotyyppi", "kaiken yläkäsite".
    • Koska Somella ja Unitilla ei ole muuta yläkäsitettä, päätellään muuttujan tyypiksi Any.
    • Any-tyyppiset muuttujat eivät useimmissa yhteyksissä ole kovin käteviä, eikä niitä esimerkiksi tällä kurssilla tarvita. Aiheesta vähän lisää luvussa 6.5.
    • Jos if-käskyä käytetään sijoituksessa, on käytännössä aina järkevää määritellä myös else-osio.

val kokeilu = if (exp.rating >= 10) Some(exp.name)

slide34

GoodStuffin Category-luokasta:

"Miksei alkuarvoksi voi antaa nollaa ja määrittää että kokemuksen on oltava suurempi kuin nolla, jolloin ensimmäiseksi suosikiksi tulisi automaattisesti ensimmäinen kokemus?"

"Olisiko ongelmasta selvitty laittamalla null-arvon tilalle yksinkertaisesti nollan?"

Hienoa, että mietitään vaihtoehtoisia ratkaisuja! Silloinkin, kun ne eivät toimi, syyt ovat usein opettavaisia.

  • Ihan noin helposti ongelma ei ratkea. Nimittäin muuttujan tyyppi määrää, millaisia arvoja siihen voi sijoittaa. Jos muuttuja on Int-tyyppinen, siihen ei voi sijoittaa myöhemmin Experience-oliota.
  • Ja joka tapauksessa tällä ratkaisumallilla olisi samat huonot puolet verrattuna Option-ratkaisuun kuin null-ratkaisulla.

No, okei, semmoinen Any-tyyppinen muuttuja on mahdollista tehdä, johon voi (luku 6.5), mutta sillä on sitten omat rajoitteensa, eikä se ole lainkaan kätevää.

slide35

"Olisiko fave-muuttujaan voinut laittaa alkuarvoksi dummy-olion, jonka arvosana on 0?"

    • Tämänsuuntainen ratkaisu sopii joihinkin ongelmiin. Tässäkin se on mahdollinen muttei erityisen elegantti...

Kokemusolio, jota käytetään suosikkimuuttujan arvona, kun suosikkia ei ole. fave-muuttujan tyyppi on yksinkertaisesti Experience.

class Category(val name: String, val unit: String) {

private val experiences = Buffer[Experience]()

private var fave = new Experience("ei suosikkia", "", 0.0, 0)

def favorite = this.fave

def addExperience(newExperience: Experience) {

this.experiences += newExperience

this.fave = newExperience.chooseBetter(this.fave)

}

Mitä tämä metodi nyt palauttaa, kun suosikkia ei ole? Kuuluuko metodin kutsujan tarkistaa, onko kyseessä nolla-arvosanainen olio?

addExperience-metodin toteutus on todella helppo. Mitään null- tai Option-kikkailua ei tarvita!

slide36

"Onko mitään syytä, miksi ei kutsuisi getOrElse-metodia pelkän get-metodin sijaan? Jos sille antaa parametriksi vaikka "Tämän ei pitäisi näkyä missään", niin eikö virheen löytäminen muuttuisi huomattavasti helpommaksi, jos sattuukin käymään niin että optionissa ei olekaan arvoa talletettuna, verrattuna siihen että tulee runtime error siitä, että arvoa yritetään kaivaa esille jostain, missä sitä ei ole?"

    • Ei muutu helpommaksi (ainakaan yleensä). Ja voi käydä päinvastoin: tilanteessa, jossa parametriksi annettu ja sitten getOrElsen palautusarvona saatu "merkityksetön" merkkijono voi tulla välitetyksi toiselle ohjelman osalle, joka käsittelee sitä jotenkin. Virhe voi ilmetä välillisesti tuon toisen osan toiminnasta eikä sitä välttämättä ole yhtä helppo yhdistää varsinaiseen ongelmasan.
  • Lisäksi esimerkiksi merkkijonon "Tämän ei pitäisi näkyä missään" käyttö getOrElsen parametrina tuskin johtaa hyvään tulokseen, jos kyseessä ei ole Option[String]-tyyppinen olio. Yleensäkin getOrElseä kannattaa käyttää lähinnä tilanteissa, joissa sille on annettavissa parametriksi jokin mielekäs "vaihtoehtoarvo", joka on samantyyppinen Option-oliossa mahdollisesti sisällä olevan kanssa.
slide37

Se luvun 3.6 lisätehtävä, missä kehotetaan miettimään, miten getOrElsellä voisi toteuttaa addExperience-metodin (ifin ja getin sijaan) on muuten ihan kiva pähkinä, jos tuota metodia haluaa pohdiskella.

Ja tässä vielä pikkubonuksena Option-olion map-metodia käyttävä toimiva ja varsin elegantti toteutus:

Mutta palataan tuohon metodiin ja alaviivaan myöhemmin (luvut 7.6, 8.1).

def addExperience(newExperience: Experience) {

this.experiences += newExperience

this.fave = this.fave.map( _.chooseBetter(newExperience) )

}

slide38

"Voiko optionin sisään pistää montakin arvoa, esim. tyyliin Option[Int, Int]? Entäs monta erilaista arvoa, esim. Option[Int, String]?"

  • Ei varsinaisestivoi; ei ihan noin.
  • Mutta: Scalassa voi muodostaa arvoista pareja (ja kolmikoita yms. monikoita). Tällaisen parin voi laittaa Optionin sisään.
  • Monikko muodostetaan suluilla ja pilkuilla. Kokeile esim. REPLissä näitä:
    • ("laama", 10)
    • Some(("laama", 10))
slide39

"Voiko ohjelmaan importata kirjastofunktioita vain tilapäisesti? Onko tästä edes hyötyä, eli vievätkö tuodut funktiot tilaa muistissa?"

    • importilla kirjataan vain määrittelyjä siitä, mitä tarkoitetaan, kun käytetään tiettyjä luokkien (yms.) nimiä kyseisessä ohjelmakoodissa.
    • Ohjelman käyttämät kirjastot toki pitää olla muistissa, jotta niitä voi käyttää, mutta import-määrittelyt eivät ole ohjelman muistinkäytön kannalta merkityksellisiä.
    • Ensimmäiseen kysymykseen: Scalassa on kyllä mahdollista tehdä paikallisia import-määrittelyjä, kuten tuossa oikealla.

import pakkaus1._

class X {

import pakkaus2._

def metodiA = {

import pakkaus3._

// ...

}

def metodiB = {

// ...

}

}

class Y {

// ...

}

slide40

3.1:n bonusosiosta:

"Graafisen olion tekeminen oli kivaa!"