Kurssin viimeisimmän version löydät täältä: O1: 2024
Luku 5.5: Silmukoita
Tästä sivusta:
Pääkysymyksiä: Miten käsittelen paljon dataa kerralla? Miten toistan jonkin toimenpiteen kaikille kokoelman sisältämille alkioille?
Mitä käsitellään? Kokoelman läpikäynti for
-silmukalla.
Algoritmien luominen käyttämällä paikallisia muuttujia silmukan
kanssa.
Mitä tehdään? Luetaan ja tehdään jokunen pikkutehtävä. Lisää tehtäviä samasta aiheesta löytyy seuraavassa luvussa.
Suuntaa antava työläysarvio:? Reilu tunti.
Pistearvo: A30.
Oheismoduulit: AuctionHouse1, ForLoops (uusi).
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. Halutessasi voit käydä tässä välissä tekemässä
tuon tehtävän tai katsoa sen vastauksen tästä:
EnglishAuction
-luokan toteutus
class EnglishAuction(val description: String, val startingPrice: Int, duration: Int) { private var highest = new Bid(None, startingPrice) private var secondHighest = new Bid(None, startingPrice) private var remainingDays = duration def daysLeft = this.remainingDays def advanceOneDay() = { if (this.isOpen) { this.remainingDays -= 1 } } def isOpen = this.remainingDays > 0 def hasNoBids = this.highest.isInitialBid def isExpired = !this.isOpen && this.hasNoBids def buyer = this.highest.bidder def price = if (this.secondHighest.isInitialBid) this.startingPrice else min(this.secondHighest.limit + 1, this.highest.limit) def requiredBid = if (this.hasNoBids) this.startingPrice else this.price + 1 def bid(bidder: String, amount: Int) = { val newBid = new Bid(Some(bidder), amount) if (this.isOpen && amount >= this.requiredBid) { this.secondHighest = if (newBid.beats(this.highest)) this.highest else newBid.winner(this.secondHighest) this.highest = newBid.winner(this.highest) } this.highest == newBid } override def toString = this.description }Oleellista tämän luvun kannalta on vain se, että esineen hinta ei ole vakio vaan määrittyy tehtyjen huutojen perusteella.Huutoja tehdäänbid
-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 tänne.
}
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 = new AuctionHouse("ReBay")house: AuctionHouse = ReBay
Yhtään esinettä ei vielä ole myynnissä. Siksi esimerkiksi priciest
-metodi,
jonka tehtävä on palauttaa kallein esine, tuottaa palautusarvoksi 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 = new EnglishAuction("A glorious handbag", 100, 14)bag: EnglishAuction = A glorious handbag house.addItem(bag)val camera = new EnglishAuction("Nikon COOLPIX", 150, 3)camera: EnglishAuction = Nikon COOLPIX house.addItem(camera)house.addItem(new 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
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 kokonaishintaHinnankorotusten 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 voi havaita 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 toteutetuksi halutunlaiseksi.
nextDay
-metodi
Suunnitellaan algoritmi nextDay
-metodille:
def nextDay() = { Vuorotellen jokaiselle alkiolle tämän olion huutokauppaluettelossa: - Kutsu kyseisen huutokaupan advanceOneDay-metodia. }
Tarkennetaan:
def nextDay() = { Vuorotellen jokaiselle alkiolle tämän olion huutokauppaluettelossa: - 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: - 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) {
current.advanceOneDay()
}
}
current
in this.items
" tai
"tee vuorotellen jokaiselle alkiolle current
, joka löytyy
kokoelmasta this.items
".current
. Tämä nimi viittaa jokaisella
silmukan "kierroksella" eri alkioon. Alkiot poimitaan kokoelmasta,
joka...<-
perässä.this.items
-kokoelman alkiolle.for
-silmukan rakenne
Tämä pseudokoodi kuvaa yleisemmin, miten alkiokokoelmaa voidaan käydä läpi ja sen kutakin
alkiota käsitellä for
-silmukalla:
for (muuttuja <- alkiokokoelma) { Tee jotain alkiolla, joka on varastoitu muuttujaan. }
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ä.
Sivumaininta
for
-silmukkaa käytetään tässä ja tulevissa luvuissa paljon.
Scalan for
-silmukka taipuu moneen, eikä kaikkia sen
hienouksia käsitellä tällä peruskurssilla.
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 11.1), lista (luku 7.1) tai hakurakenne (luku 8.4).
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) {
// luokan muita osia
}
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) {
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 hyötykäyttöjä, 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: - 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: - 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) {
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
}
AuctionHouse
-olion ominaisuudeksi kirjataan tieto sen
sisältämien huutokauppojen yhteishinnasta.totalPrice
-metodi on nyt todella yksinkertainen.Toimisiko äskeinenkin toteutus? Vastaa kysymykseen itse miettimällä seuraavaa koodia.
val kamari = new AuctionHouse("Huutelu.net")
val esine = new 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: - 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: - 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 ne esineet, jotka
parametriarvona annettu asiakas on joko jo ostanut tai 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: - 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 laitettua tuohon
Buffer[EnglishAuction]
-tyyppiseen puskuriin, jollaisia items
ja
oheinen purchases
ovat?
Houkuttaisi tehdä items
-puskuri, jossa olisi eri tavoin myyntiin
laitettuja 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.2, ja AuctionHouse-moduuli syntyy uudelleen entistä ehompana luvussa 7.5.
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: - 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: 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) {
if (current.price > priciestSoFar.price) {
priciestSoFar = current
}
}
priciestSoFar
}
Mitä unohtui?
Metodihan oli speksattu niin, että se palauttaa Option[EnglishAuction]
-tyyppisen
arvon eikä aiheuta virhettä silloinkaan, kun huutokauppoja ei ole kauppakamarissa
ensimmäistäkään. Tällä hetkellä palautusarvon tyyppi on kuitenkin EnglishAuction
, ja
metodin kutsuminen aiheuttaa ajonaikaisen poikkeustilanteen, jos items
on tyhjä (koska
tällöin edes indeksillä 0 ei ole alkiota). Ongelma on analoginen sen kanssa, 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) {
None
} else {
var priciestSoFar = this.items.head
for (current <- this.items) {
if (current.price > priciestSoFar.price) {
priciestSoFar = current
}
}
Some(priciestSoFar)
}
}
None
, jos läpikäytävää sisältöä ei ole.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)
}
}
}
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) {
None
} else {
var fave = this.experiences(0)
for (current <- this.experiences) {
fave = current.chooseBetter(fave)
}
Some(fave)
}
}
def addExperience(newExperience: Experience) = {
this.experiences += newExperience
}
}
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 | fave -ilmentymämuuttuja |
Paikallinen fave ja silmukka |
---|---|---|
Käsitteellisen mallin luonnollisuus | Suosikki on luontevasti miellettävissä kategoriaolion ominaisuudeksi. | Suosikki on luontevasti miellettävissä
myös favorite -toimintoon liittyväksi
piirteeksi. |
Toteutuksen helppous | favorite -metodin toteutus on triviaali,
addExperience n monimutkaisempi. |
addExperience -metodin toteutus on
triviaali, favorite n monimutkaisempi. |
Suoritus- tehokkuus | Ohjelma toimii periaatteessa nopeammin
ainakin silloin, jos kokemuksia on paljon
ja favorite -metodia kutsutaan usein.
(Tällä ei tosin ole tässä ohjelmassa
minkäänlaista käytännön merkitystä.) |
Tietokone käy kaikki puskurin alkiot läpi
aina, kun favorite -metodia kutsutaan,
joten toteutus voi olla periaatteessa
hitaampi. |
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ä.) | Mitään "ei-välttämätöntä" tietoa ei ole muistissa. Suosikki voidaan aina selvittää puskurin sisältöä tutkimalla. |
Koodin luettavuus ja muokattavuus | Suosikkiuteen liittyvää koodia on sekä
ilmentymämuuttujien määrittelyssä,
favorite -metodissa, että
addExperience -metodissa. Kukin osa
riippuu toisista osista. Tämä huonontaa
koodin luettavuutta ja muokattavuutta vähän. |
Kaikki suosikkiuteen liittyvä on
keskitetysti metodissa favorite .
Riippuvuudet luokan osien välillä vähän
pienemmät. Koodin muokattavuus on
parempi. |
Suurta eroa ei tässä tapauksessa ratkaisutapojen välille voi väittää, mutta taulukon perusteella voitaneen 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ä yksittäisolio o1.looptest.Task1
. 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 tulisi 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, 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.