Luku 5.5: Silmukoita
Tausta: muuttuvahintaisia esineitä
Luvussa 5.1 toteutit toivottavasti ainakin FixedPriceSale
-luokan, joka kuvasi
vakiohintaan kaupattavia esineitä. Luvussa oli myös vapaaehtoiset tehtävät, joissa
toteutettiin luokat EnglishAuction
ja DutchAuction
. Käytämme näistä kohta
EnglishAuction
ia.
Jos et tehnyt tuota tehtävää, niin se ei tässä haittaa. Kuten FixedPriceSale
-olio
myös EnglishAuction
-olio kuvaa kaupattavaa esinettä. Tällaisella huutokaupalla on hinta
(price
), mahdollinen ostaja ( buyer
) ja advanceOneDay
-metodi aivan vastaavasti
kuin FixedPriceSale
-oliolla. Erona on, että EnglishAuction
-oliossa hinta nousee eri
ostajaehdokkaiden tarjoutuessa maksamaan esineestä suurempia summia. Siinä kaikki, mitä
sinun on välttämätöntä nyt tietää asiasta.
Silti halutessasi voit...
... käydä tässä välissä tekemässä tuon tehtävän luvusta 5.1 tai katsoa sen ratkaisun tästä:
EnglishAuction
in toteutus
class EnglishAuction(val description: String, val startingPrice: Int, duration: Int): private var highestBid = Bid(None, startingPrice) private var secondHighestBid = Bid(None, startingPrice) private var remainingDays = duration def daysLeft = this.remainingDays def advanceOneDay() = if this.isOpen then this.remainingDays -= 1 def isOpen = this.remainingDays > 0 def hasNoBids = this.highestBid.isInitialBid def isExpired = !this.isOpen && this.hasNoBids def buyer = this.highestBid.bidder def price = if this.secondHighestBid.isInitialBid then this.startingPrice else min(this.secondHighestBid.limit + 1, this.highestBid.limit) def requiredBid = if this.hasNoBids then this.startingPrice else this.price + 1 def bid(bidder: String, amount: Int) = val newBid = Bid(Some(bidder), amount) if this.isOpen && amount >= this.requiredBid then this.secondHighestBid = if newBid.beats(this.highestBid) then this.highestBid else newBid.winner(this.secondHighestBid) this.highestBid = newBid.winner(this.highestBid) end if this.highestBid == newBid override def toString = this.description end EnglishAuctionHuutoja tehdään
bid
-metodilla, joka siis vaikuttaa esineen nykyhintaan.Apuluokka
Bid
löytyy AuctionHouse1-moduulista.EnglishAuction
käyttää sitä sisäisesti, eikä tuo luokka ole tärkeä.
Luvun tavoite: AuctionHouse
-luokka
Oletetaan siis, että käytössämme on EnglishAuction
-luokka. Otetaan nyt tavoitteeksi
laatia lisäksi luokka AuctionHouse
: yksi AuctionHouse
-olio edustaa huutokauppapaikkaa,
joka sisältää EnglishAuction
-olioita eli hinnaltaan muuttuvia esineitä.
Tässä alkua AuctionHouse
-luokalle:
class AuctionHouse(val name: String):
private val items = Buffer[EnglishAuction]()
def addItem(item: EnglishAuction) =
this.items += item
def removeItem(item: EnglishAuction) =
this.items -= item
override def toString = this.name
// Muita metodeita tulee tänne.
end AuctionHouse
Kullakin AuctionHouse
-oliolla on oma puskuri, johon
säiliömuuttuja items
viittaa. Siihen voi lisätä
huutokauppoja, ja niitä voi poistaakin.
Nyt haluttaisiin luoda AuctionHouse
-luokkaan metodeita, joilla voi
edistää jokaista huutokauppaa päivällä (eli kutsua jokaisen
items
-puskurissa olevanEnglishAuction
-olionadvanceOneDay
-metodia),laskea kaikkien huutokauppojen yhteishinnan,
laskea huutokauppojen keskimääräisen hinnan,
etsiä huutokaupoista kaikkein hintavimman,
laskea avoinna olevien huutokauppojen lukumäärän, ja
tuottaa luettelon kaikista tietyn ostajan voittamista huutokaupoista.
Alla on esimerkkejä siitä, miten näiden metodien tulisi toimia.
Käyttöesimerkki
Uusi kauppapaikka olisi tarkoitus voida luoda näin:
val house = AuctionHouse("ReBay")house: AuctionHouse = ReBay
Yhtään esinettä ei vielä ole myynnissä. Siksi esimerkiksi priciest
-metodi,
jonka tehtävä on palauttaa kallein esine, tuottaa paluuarvoksi None
:
house.priciestres0: Option[EnglishAuction] = None
Pistetään kolme huutokauppaa pystyyn ja lisätään ne addItem
-metodilla
AuctionHouse
en. Kullakin esineellä on kuvaus, alkuhinta ja kesto päivissä.
val bag = EnglishAuction("A glorious handbag", 100, 14)bag: EnglishAuction = A glorious handbag house.addItem(bag)val camera = EnglishAuction("Nikon COOLPIX", 150, 3)camera: EnglishAuction = Nikon COOLPIX house.addItem(camera)house.addItem(EnglishAuction("Collectible Easter Bunny China Thimble", 1, 10))
Seuraava tuloste osoittaa, että nextDay
-metodikutsut vähentävät sekä laukun että
kameran (ja siis kaikkien käynnissä olevien huutokauppojen) kestoaikaa päivällä:
println(bag.daysLeft + ", " + camera.daysLeft)14, 3 house.nextDay() house.nextDay() println(bag.daysLeft + ", " + camera.daysLeft)12, 1
totalPrice
ja averagePrice
palauttavat tilastotietoja:
house.totalPriceres1: Int = 251 house.averagePriceres2: Double = 83.66666666666667
Kun kaupattavista esineistä tarjotaan suurempia summia, niiden hinta nousee, mikä näkyy mainituissa tilastoissa.
bag.bid("Minna", 200)res3: Boolean = true bag.bid("Mikko", 170)res4: Boolean = false camera.bid("Minna", 190)res5: Boolean = true house.totalPriceres6: Int = 322
Huutoja tehdään bid
-metodilla. Kilpailevat huudot voivat nostaa
EnglishAuction
-olion hintaa. (Tarkemmat yksityiskohdat eivät
ole tässä merkityksellisiä mutta on kuvattu luokan EnglishAuction
dokumentaatiossa. Nyt oleellista on, että bid
voi muuttaa
esineen hintaa.)
AuctionHouse
-olion pitäisi osata laskea ja raportoida
huutojen vuoksi päivittynyt kokonaishinta
Hinnankorotusten jälkeen hintavin esine on laukku:
house.priciestres7: Option[EnglishAuction] = Some(A glorious handbag)
Tässä vaiheessa kaikki kolme esimerkkihuutokauppaamme ovat vielä auki, mutta kameralla
on vain yksi myyntipäivä jäljellä. Kauppa sulkeutuu seuraavasta nextDay
-kutsusta.
Tämän havaitsee vaikkapa kysymällä AuctionHouse
-oliolta, montako avointa kauppaa on
jäljellä:
house.numberOfOpenItemsres8: Int = 3 house.nextDay()house.numberOfOpenItemsres9: Int = 2
Katsotaan vielä, mitä kukakin on saamassa:
house.purchasesOf("Minna")res10: Vector[EnglishAuction] = Vector(A glorious handbag, Nikon COOLPIX) house.purchasesOf("Mikko")res11: Vector[EnglishAuction] = Vector()
purchasesOf
-metodin palauttamissa vektoreissa ovat sekä sellaiset päättyneet huutokaupat,
jotka kyseinen ostaja on jo voittanut (kuten tässä kamera, joka sulkeutui Minnan tehtyä
korkeimman tarjouksen), että sellaiset vielä avoimet huutokaupat, joissa hän on johtoasemassa
(kuten tässä Minnan laukku).
Mitä tarvitaan?
Mitä yhteistä toivotuilla metodeilla on?
Kunkin niistä toteuttamiseksi on osattava käydä läpi
AuctionHouse
-olioon liitetyt huutokaupat eliitems
-puskurin alkiot.Kuhunkin niistä sisältyy ajatus siitä, että yhtä tai useampaa käskyä toistetaan useita kertoja.
Alkiokokoelman läpikäynti ja toimenpiteen suorittaminen kunkin kokoelman sisältämän alkion kohdalla on ohjelmissa erittäin yleinen tarve. Melkein kaikki vähänkään kiinnostavat tietokoneohjelmat toistavat käskyjä tavalla tai toisella.
Tässä luvussa opit käyttämään Scala-käskyä, jolla homma onnistuu. Tarkastellaan metodi
kerrallaan, miten AuctionHouse
saadaan toteuttua halutunlaiseksi.
nextDay
-metodi
Suunnitellaan algoritmi nextDay
-metodille:
def nextDay() = Vuorotellen jokaiselle alkiolle tämän olion huutokauppaluettelossa tee näin: - Kutsu kyseisen huutokaupan advanceOneDay-metodia.
Tarkennetaan:
def nextDay() = Vuorotellen jokaiselle alkiolle tämän olion huutokauppaluettelossa tee näin: - Ota tuorein alkio (viittaus viimeisimpään läpikäytyyn huutokauppaan) talteen paikalliseen muuttujaan. - Kutsu talteen otetulle huutokaupalle advanceOneDay-metodia.
Tarkennetaan:
def nextDay() = Vuorotellen jokaiselle alkiolle puskurissa this.items tee näin: - Ota alkio talteen tuoreimman säilyttäjään current. - Kutsu current.advanceOneDay().
Metodin olisi siis toimittava kuten seuraava animaatio näyttää.
Vaikka tämä olikin vasta pseudokoodia, niin animaatio korosti, miten samat koodin osat
tulevat suoritetuiksi useita kertoja. Kuitenkin joka "kierroksella" luodaan aina uusi
current
-muuttuja uudella arvolla. Metodiin muodostuu niin sanottu silmukka (eli
"luuppi"; loop), joka toistaa tiettyä toimenpidettä.
for
-silmukat
Pseudokoodista Scalaksi
Alla on eräs kätevä tapa toteuttaa nextDay
-luonnoksemme Scalalla. Tämä koodi toimii
juuri niin kuin äskeisessä animaatiossa näytettiin.
def nextDay() =
for current <- this.items do
current.advanceOneDay()
Tällaisen silmukan avainsanat ovat for
ja do
.
Alun voi lukea "for each item current
in this.items
,
do" tai "vuorotellen jokaiselle alkiolle current
, joka löytyy
kokoelmasta this.items
, tee seuraavaa".
Tässä tulee määritellyksi muuttuja, jolle voimme valita nimen;
olemme nyt valinneet nimeksi current
. Tämä nimi viittaa
jokaisella silmukan "kierroksella" eri alkioon. Alkiot poimitaan
kokoelmasta, joka...
... on mainittu vasemmalle osoittavan nuolen <-
perässä.
Silmukan runko tulee toistetuksi kerran jokaiselle this.items
-kokoelman alkiolle. Runko on sisennetty pykälää syvemmälle.
Voit kirjoittaa silmukalle loppumerkin. Usein ne
jätetään kirjoittamatta, mutta jos kyse on hieman mutkikkaammasta
metodista, saattaa loppumerkki selkiyttää koodia. Saat kirjoittaa
vaikka joka silmukan loppuun end for
, jos siltä tuntuu.
for
-silmukan rakenne
Tämä pseudokoodi kuvaa yleisemmin, miten alkiokokoelmaa voi käydä läpi ja sen kutakin
alkiota käsitellä for
-silmukalla:
for muuttuja <- alkiokokoelma do Tee jotain alkiolla, joka on varastoitu muuttujaan. end for // Tämän viimeisen rivin voi poistaakin.
Silmukan määrittelyyn sisältyy muuttujan määrittely. Tähän muuttujaan tallentuu aina viimeisin läpikäydyistä alkioista, ja se on siis rooliltaan tuoreimman säilyttäjä.
Läpikäytävä alkiokokoelma voi olla esimerkiksi puskuri (kuten tässä ensimmäisessä esimerkissä), vektori (luku 4.2), merkkijono (luku 5.6), taulukko (luku 12.1), lista (luku 7.2) tai hakurakenne (luku 9.2).
Kun for
-silmukka käy läpi puskurin tai vektorin kaltaista kokoelmaa, jossa alkioilla
on indeksit, alkiot tulevat käsitellyksi indeksien mukaisessa järjestyksessä.
Rooleista
Luvussa 2.6 totesimme, että muuttujia voi luokitella niiden roolin mukaan:
Luku 2.6 näytti esimerkkejä muuttujista, joiden rooliksi mainittiin tuoreimman
säilyttäjä. Tällainen oli esimerkiksi työntekijäluokan ilmentymämuuttuja nimi
, jolle
saattoi sijoittaa uuden arvon korvaamaan vanhan; vain viimeisin asetettu nimi jäi voimaan:
class Tyontekija(var nimi: String, val syntynyt: Int, var kkpalkka: Double):
// ...
end Tyontekija
Tässä luvussa taas olemme nähneet aivan toisenlaisessa tilanteessa paikallisen
current
-muuttujan, jota sitäkin sanottiin tuoreimman säilyttäjäksi:
for current <- this.items do
current.advanceOneDay()
Ulkoisesti näiden muuttujien käyttötapa näyttää hyvin erilaiselta. Kuitenkin molemmissa tapauksissa ajatuksena on, että muuttujassa säilytetään viimeisintä arvosekvenssissä kohdattua arvoa: viimeisin asetettu nimi, viimeisin kohdattu huutokauppaolio. Koodissa näkyvä ero kumpuaa siitä, että yksi on ilmentymämuuttuja ja toinen paikallinen muuttuja. Molemmat ovat tyyppiesimerkkejä tilanteista, joissa tuoreimman säilyttäjästä on hyötyä:
"Asetettavissa oleva ominaisuus" kuten
nimi
on tyypillinen tuoreimman säilyttäjä -ilmentymämuuttujan käyttötapa. Se esiintyy tilanteissa, joissa tuoreinta arvoa on tarpeen säilyttää ilmentymämuuttujassa myös metodikutsujen välillä."Viimeisin silmukassa läpikäytävistä arvoista" kuten
current
on tyypillinen paikallisen tuoreimman säilyttäjä -muuttujan käyttötapa. Se esiintyy tilanteissa, joissa tuoreinta arvoa tarvitaan vain tietyn metodin toteutuksessa sisäisesti apuna.
Monelle tutulle muuttujaroolille löytyy uusia käyttötapoja, kun tuo rooli annetaan
paikalliselle muuttujalle, jota käytetään yhdessä silmukan kanssa. Tämä käy ilmi,
kun seuraavaksi toteutamme AuctionHouse
-luokan muita metodeita.
Paikallinen kokooja + silmukka
totalPrice
-metodi
Pseudokoodiluonnos totalPrice
-metodista:
def totalPrice = Vuorotellen jokaiselle huutokaupalle puskurissa this.items tee näin: - Selvitä huutokaupan hinta ja lisää summaan. Lopuksi palauta summa.
Millaisia paikallisia muuttujia tarvitaan?
Tuoreimman säilyttäjä (tyyppiä
EnglishAuction
) pitämään kirjaa siitä alkiosta (huutokaupasta), jossa ollaan menossa. Siis aivan kuinnextDay
-esimerkissä.Kokooja (tyyppiä
Int
), johon kootaan vaihe vaiheelta huutokauppojen hintojen summa.
Tarkennetaan.
def totalPrice = Olkoon totalSoFar kokooja, jonka arvo on aluksi nolla. Vuorotellen jokaiselle huutokaupalle puskurissa this.items tee näin: - Selvitä huutokaupan hinta ja korota totalSoFarin arvoa sen verran. Palauta lopuksi kokoojan totalSoFar arvo.
Sama Scalalla:
def totalPrice =
var totalSoFar = 0
for current <- this.items do
totalSoFar += current.price
totalSoFar
Kyseenalaistus
Oliko tuo nyt tarpeen? Ei olisi tarvittu silmukkaakaan, kun olisi tehty näin:
class AuctionHouse(val name: String):
private val items = Buffer[EnglishAuction]()
private var priceSoFar = 0
def addItem(item: EnglishAuction) =
this.items += item
this.priceSoFar += item.price
def removeItem(item: EnglishAuction) =
this.items -= item
this.priceSoFar -= item.price
def totalPrice = this.priceSoFar
end AuctionHouse
Tässä ratkaisussa kokoojamuuttuja onkin ilmentymämuuttuja:
kunkin AuctionHouse
-olion ominaisuudeksi kirjataan tieto
sen sisältämien huutokauppojen yhteishinnasta.
Huutokauppoja lisättäessä ja poistaessa tätä ilmentymämuuttujaa päivitetään.
totalPrice
-metodi on nyt todella yksinkertainen.
Toimisiko äskeinenkin toteutus? Vastaa kysymykseen itse miettimällä seuraavaa koodia.
val kamari = AuctionHouse("Huutelu.net")
val esine = EnglishAuction("telkkari", 5000, 10)
kamari.addItem(esine)
println(kamari.totalPrice)
esine.bid("Matti", 10000)
esine.bid("Teppo", 15000)
println(kamari.totalPrice)
Jos et keksi, mikä on ongelmana, kommentoi asiaa luvun lopussa palautelomakkeella, niin aihetta voidaan vaikkapa käsitellä viikkokoosteessa.
averagePrice
-metodi
Keskiarvometodin saa tehtyä aivan helposti, kun luokassa on myös summanlaskemismetodi:
def averagePrice = this.totalPrice.toDouble / this.items.size
Paikallinen askeltaja + silmukka (feat. if
)
Pseudokoodi numberOfOpenItems
-metodille:
def numberOfOpenItems = Vuorotellen jokaiselle huutokaupalle puskurissa this.items tee näin: - Jos huutokauppa on auki, kasvata laskuria yhdellä. Lopuksi palauta laskurin arvo.
Tarkennetaan käyttäen muuttujaa askeltajana:
def numberOfOpenItems = Olkoon openCount askeltaja, jonka arvo on 0 ja joka kasvaa myöhemmin yksi kerrallaan. Vuorotellen jokaiselle huutokaupalle puskurissa this.items tee näin: - Ota huutokauppa talteen tuoreimman säilyttäjään current. - Jos huutokaupan isOpen palauttaa true, niin nosta openCountin arvoa yhdellä. Lopuksi palauta openCountin arvo.
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.
Paikallinen säiliö + silmukka
purchasesOf
-metodin kuului palauttaa vektori, jossa ovat tietyn asiakkaan ostokset.
Ostoksiksi lasketaan ne esineet, jotka tuo asiakas on jo ostanut, ja ne, joissa hän on
johtava huutaja.
Luonnos:
def purchasesOf(buyer: String) = Luo uusi tyhjä puskuri, johon viittaa säiliömuuttuja nimeltä purchases. Vuorotellen jokaiselle huutokaupalle puskurissa this.items tee näin: - Jos esineen ostajaksi on kirjattu parametrin buyer arvo, niin lisää esine purchases-säiliöön. Lopuksi palauta vektori, jossa on purchases-muuttujan mukaiset esineet.
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.
Miksi vain EnglishAuction
eita?
Mitä jos halutaan AuctionHouse
en myös FixedPriceSale
ja
ja DutchAuction
eita (luku 5.1)? Niitähän ei saa tuohon
Buffer[EnglishAuction]
-tyyppiseen puskuriin, jollaisia
items
ja oheinen purchases
ovat?
Houkuttaisi tehdä items
-puskuri, jossa olisi eri tavoin
myytäviä esineitä sekaisin, ja nämä tämän luvun metodit sitten
käsittelisivät näitä kaikkia kerralla. Onnistuuko?
Onnistuu. Tähän opitaan välineitä luvussa 7.3, ja AuctionHouse-moduuli syntyy uudelleen entistä ehompana.
Paikallinen sopivimman säilyttäjä + silmukka
priciest
ensin pseudokoodina:
def priciest = Olkoon hintavin tähän mennessä löydetty huutokauppa aluksi puskurin this.items ensimmäinen alkio. Vuorotellen jokaiselle huutokaupalle puskurissa this.items tee näin: - Vertaa huutokauppaa hintavimpaan aiemmin löydettyyn ja pistä muistiin näistä isompihintainen. Lopuksi palauta huutokauppa, joka jäi muistiin isohintaisimpana.
Millaisia paikallisia muuttujia tarvitaan?
Tuoreimman säilyttäjä (tyyppiä
EnglishAuction
) pitämään kirjaa siitä alkiosta (huutokaupasta), jossa ollaan menossa.Sopivimman säilyttäjä (myös tyyppiä
EnglishAuction
), jossa pidetään kirjaa isohintaisimmasta toistaiseksi löydetystä huutokaupasta.
Pseudokoodiamme voi tarkentaa esimerkiksi näin:
def priciest = Olkoon priciestSoFar sopivimman säilyttäjä, jonka alkuarvo on this.items.head. Vuorotellen jokaiselle huutokaupalle puskurissa this.items tee näin: 1. Ota huutokauppa talteen tuoreimman säilyttäjään current. 2. Tutki, onko current isompihintainen kuin priciestSoFar. Jos on, niin: - Sijoita priciestSoFarin uudeksi arvoksi current. Lopuksi palauta sopivimman säilyttäjään priciestSoFar jäänyt arvo.
Ja sama Scalalla:
def priciest =
var priciestSoFar = this.items.head
for current <- this.items do
if current.price > priciestSoFar.price then
priciestSoFar = current
priciestSoFar
Mitä unohtui?
Metodin oli määrätty toimivan niin, että se palauttaa Option[EnglishAuction]
-tyyppisen
arvon eikä aiheuta virhettä silloinkaan, kun huutokauppoja ei ole kauppakamarissa
ensimmäistäkään. Tällä hetkellä paluuarvon tyyppi on kuitenkin EnglishAuction
, ja
jos items
on tyhjä, metodin kutsuminen aiheuttaa ajonaikaisen poikkeustilanteen (koska
tällöin edes indeksillä 0 ei ole alkiota).
Ongelma on samankaltainen kuin se, joka tuli vastaan luvussa 4.2, kun ensimmäisen kerran
käytimme sopivimman säilyttäjää. Silloin unohdimme käsitellä addExperience
-metodissa
tapauksen, jossa aiempaa suosikkikokemusta ei ollutkaan.
Korjataan. Yksi korjaustapa on käsitellä tyhjän puskurin tapaus erikseen if
-käskyllä.
def priciest =
if this.items.isEmpty then
None
else
var priciestSoFar = this.items.head
for current <- this.items do
if current.price > priciestSoFar.price then
priciestSoFar = current
Some(priciestSoFar)
Palautetaan None
, jos läpikäytävää sisältöä ei ole.
Muussa tapauksessa puskurin ensimmäisen alkion voi huoletta poimia, koska alkioita on ainakin yksi...
... ja lopputulos kääritään Some
-olioon.
Jäikö häiritsemään?
Tässä toteutuksessa priciestSoFar
ja current
viittaavat silmukan
ensimmäisellä kierroksella samaan huutokauppaolioon. Tällöinhän
niiden vertaileminen on ihan turhaa. Käytännössä tästä ei ole
merkittävää haittaa, mutta hieman epäsiistiltä se silti tuntuu.
Voit pitää asian mielessä kurssin edetessä. Tämäkin metodi on toteutettavissa erilaisilla tavoilla, joista kaikissa ei tuota tarpeetonta itseensävertailua tehdä.
Entä se vanha Category
-luokka metodeineen?
Luvussa 4.3 laadittiin erilaisia versioita GoodStuff-ohjelman Category
-luokasta. Muun
muassa tämä:
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(newExperience: Experience) =
this.experiences += newExperience
this.fave match
case None =>
this.fave = Some(newExperience)
case Some(oldFave) =>
val newFave = newExperience.chooseBetter(oldFave)
this.fave = Some(newFave)
end Category
Tämänhän voisi tehdä myös samaan tapaan kuin priciest
yllä, eikö totta? Tehdään
tuoreimman säilyttäjästä fave
ilmentymämuuttujan sijaan paikallinen muuttuja ja
käytetään for
-silmukkaa:
class Category(val name: String, val unit: String):
private val experiences = Buffer[Experience]()
def favorite =
if this.experiences.isEmpty then
None
else
var fave = this.experiences(0)
for current <- this.experiences do
fave = current.chooseBetter(fave)
Some(fave)
def addExperience(newExperience: Experience) =
this.experiences += newExperience
end Category
Molemmat näistä ratkaisuista tosiaan toimivat.
Ratkaisutapojen vertailua
Koska molemmat ratkaisutavat toimivat, mutta niillä on puolensa, meille avautuu tilaisuus tarkastella lyhyesti eräitä ohjelmien laatukriteereitä.
Kriteeri |
|
Paikallinen |
---|---|---|
Käsitteellisen mallin luonnollisuus |
Suosikki on luontevasti miellettävissä kategoriaolion ominaisuudeksi. |
Suosikki on luontevasti miellettävissä
myös |
Toteutuksen helppous |
|
|
Suoritus- tehokkuus |
Ohjelma toimii periaatteessa nopeammin
ainakin silloin, jos kokemuksia on paljon
ja |
Tietokone käy kaikki puskurin alkiot läpi
aina, kun |
Muistin tarve |
Kukin kategoriaolio tarvitsee hitusen lisämuistia viittaukselle suosikkiolioon. (Tällä ei tosin ole tässä ohjelmassa minkäänlaista käytännön merkitystä.) |
Vain välttämätön tieto on muistissa. Suosikki voidaan aina selvittää puskurin sisältöä tutkimalla. |
Koodin luettavuus ja muokattavuus |
Suosikkiuteen liittyvää koodia on sekä
ilmentymämuuttujien määrittelyssä,
|
Kaikki suosikkiuteen liittyvä on
keskitetysti metodissa |
Suurta eroa ei tässä tapauksessa ratkaisutapojen välille voi väittää, mutta taulukon perusteella voi sanoa, että paikallista muuttujaa ja silmukkaa käyttävä ratkaisu on jossain määrin parempi. Luettavuus ja muokattavuus ovat tärkeitä!
Silmukkatehtäviä
Silmukanlukemistehtävä
Silmukankoodaustehtävä
Avaa moduuli ForLoops ja sieltä o1.looptest
-pakkauksen task1.scala
. Lue tuon
esimerkkiohjelman koodi ja kirjoita se uudelleen niin, että se tuottaa täsmälleen saman
tulosteen kuin aiemminkin mutta for
-silmukkaa käyttäen. Syntyvän koodin tulee olla
selvästi alkuperäistä lyhyempi.
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.
Abstrahointia, taas
Toistokäskykin on eräänlainen abstraktio: tässä tehtävässä teit annettujen konkreettisten koodirivien perusteella abstraktimman yleistyksen.
Lisää koodia: silmukat ja muuttujat
Yhteenvetoa
for
-silmukalla voi toistaa käskyjä kullekin alkiokokoelman sisältämälle arvolle.Yhdistämällä
for
-silmukoita ja erilaisia paikallisten muuttujien käyttötapoja saadaan aikaan metodeita, joilla voi suorittaa erilaisia toimenpiteitä kokoelmalle.Lukuun liittyviä termejä sanastosivulla: silmukka,
for
-silmukka, iteraatio; kokoelma; muuttujan rooli.
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, Kaisa Ek, Joonatan Honkamaa, Antti Immonen, Jaakko Kantojärvi, Onni Komulainen, Niklas Kröger, Kalle Laitinen, Teemu Lehtinen, Mikael Lenander, Ilona Ma, Jaakko Nakaza, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä, Joel Toppinen, Anna Valldeoriola Cardó 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, Juha Sorva ja Jaakko Nakaza. 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; sitä 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 suunnitteluun ja toteutukseen on osallistunut useita opiskelijoita yhteistyössä O1-kurssin opettajien kanssa.
Kurssin tämänhetkinen henkilökunta löytyy luvusta 1.1.
Joidenkin lukujen lopuissa on lukukohtaisia lisäyksiä tähän tekijäluetteloon.
Oleellista tämän luvun kannalta on vain se, että esineen hinta ei ole vakio vaan määrittyy tehtyjen huutojen perusteella.