Johdatus ohjelmointiin, kesä 1998 Helsingin yliopisto Avoin yliopisto ============================================================================ 1. ============================================================================ Johdatus ohjelmointiin, Avoin yliopisto, kesä 1998 Kertauskuulustelu 24.06.1998 Arvosteluperusteet ja esimerkkiratkaisuja (Sami Nikander) 1. Yleistä tehtävän arvostelusta: Tehtävässä oli 6 kohtaa, joista jokaisesta sai max. 2 pistettä. Jaottelin kunkin kohdan ydinasian (ehkä turhankin tarkasti) osiin, joita selittämällä pisteitä jaettiin. Muutamista kohdista oli jaossa "yli 2" pistettä, jolloin ei edes tarvinnut selittää "ihan kaikkea" asiaan liittyvää saadakseen täydet pisteet ko. kohdasta. Jos vastasi kysymyksiin monisteessa olevalla tarkkuudella, sai täydet pisteet, mutta kaikenlaiset muutkin selitykset hyväksyttiin kyllä, jos niistä kävi ilmi, että asia on ymmärretty. Ohessa jonkinsorttiset esimerkkiselitykset, jotka ovat paljon laajasanaisemmat kuin koevastauksilta edellytettiin. Suppeammilla ja tiiviimmilläkin selostuksilla siis sai pisteet, kunhan asia oli sanottu oikein. Muutamissa kohdissa olin melko ankara, esim. lausekelauseen selittämättä jättämisestä tai kuormittamiskysymyksessä näkyvyysalueen unohtamisesta en heltynyt. Pakkausten ja poikkeusten yhteydessä melko epätäsmällinenkin kuvaus sai usein pisteitä, koska asiat olivat kurssin loppupuolelta ja kiireessä opiskeltuja. Yht. 14½ p (max. 12 p) Metodin paikallinen muuttuja (local variable) (Monisteen s. 25, 85) --------------------------------------------------------------------- Metodin sisäinen muuttuja (myös muodolliset parametrit ovat paikallisia muuttujia!), joka on käytettävissä vain metodin omassa lohkossa (½ p). Paikalliselle muuttujalle varataan tilaa metodin suorituksen alkaessa ja tila vapautetaan sen päättyessä (½ p). Se ei siten säily kutsukerrasta toiseen, vaan luodaan uudelleen joka kutsukerralla, näin ollen sitä ei voi käyttää minkään tiedon "muistamiseen" kutsukerrasta toiseen (½ p). Se ei saa oletusalkuarvoa, vaan se pitää alustaa metodissa (½ p). Paikallisen muuttujan on oltava yksikäsitteinen metodin sisällä (½ p), ja se peittää näkyvistä samannnimiset olion muuttujat (luokassa määritellyt, luokan muuttujat) (½ p). Yht. 3 p (max. 2 p) Lause ja lauseke (statement and expression) (Monisteen s. 50-57) --------------------------------------------------------------------- Lause on algoritmin yksi "käsky" tai osa, rivi tai komento joka "vie suoritusta eteenpäin", "tekee jotakin". Sillä ei ole arvoa. Se päättyy puolipisteeseen tai on koottu lohkosulkujen { } sisään. Esimerkkeinä valintalause, toistolause, rakenteinen lause... (3/4 p). Lauseke on ohjelmointikielen ilmaus, esimerkiksi laskutoimitus, jolla on lopputulos (arvo), ja tämä arvo on aina jotain tyyppiä (mitä tahansa Javan tyyppiä, esim. int, boolean tai jokin viittaustyyppi). Esimerkiksi arvon palauttavan metodin (funktion) kutsu, ilmaus "(x+5) % 3 - a" ja sijoituslause ovat lausekkeita (3/4 p). Lauseketta voidaan toisinaan käyttää myös lauseen tapaan. Tällöin lausekkeen arvo lasketaan normaaliin tapaan, mutta sitä ei käytetä mihinkään, vaan lausekkeen laskennan tarkoitus oli vain edistää algoritmin suoritusta, esim. jonkin sivuvaikutuksen (muuttujan arvon muutos, metodin kutsu tms) kautta. Esimerkiksi sijoitusta ja metodikutsua voidaan käyttää lauseina (½ p). Tässä tehtävässä hyvin harva oli selostanut lausekelauseita, vaikka lauseen ja lausekkeen kuvaus olisikin ollut oikein. Yht. 2 p Parametri (parameter) (Monisteen s. 21-22, 59) ----------------------------------------------------------------- Parametri on metodille annettava lähtötieto, jolla kutsuva metodi voi vaikuttaa algoritmin toimintaan (eri tavoin eri kutsukerroilla) (½ p). Parametri määritellään metodin otsikossa metodin nimen jälkeen sulkeissa (toisin kuin muut paikalliset muuttujat!) (½ p). Tätä muuttujaa kutsutaan muodolliseksi parametriksi. Metodin kutsussa sulkeisiin kirjoitetaan todelliset parametrit, joiden arvo lasketaan kutsuhetkellä ja kopioidaan muodollisten parametrien alkuarvoksi (muodollisten ja todellisten parametrien on vastattava toistensa tyyppejä!) (½ p). Kaikki parametrit ovat arvoparametreja, eli niiden muuttaminen metodissa ei vaikuta todellisten parametrien arvoon kutsuvassa ohjelmanosassa (tosin viittausmuuttujat osoittavat aina samaan olioon, joten olion muuttaminen näkyy kyllä kutsujallekin) (½ p). Parametrit ovat mitä tahansa Javan alkeis- tai viittaustyyppiä (kuten muutkin muuttujat!) (½ p). Myös komentoriviparametrit ovat parametreja, mutta näiden mainitsemista ei edellytetty. Yht. 2½ p (max. 2 p) Kuormittaminen (overloading) (Monisteen s. 24, 59) ----------------------------------------------------------------- Kun samassa luokassa (tai oikeammin näkyvyysalueessa) on useita samannimisiä metodeja (myös konstruktoreita), kyse on kuormituksesta (½ p). Kuormitettujen metodien tulee erota toisistaan parametrien lukumäärältä tai tyypeiltä, ei riitä, että metodin tyyppi (paluuarvo) tai määre (public, static tms.) on erilainen (1 p). Java-kääntäjä erottaa kuormitetut metodit toisistaan ja osaa valita oikean metodin käännösaikana kutsussa esiintyvien todellisten parametrien perusteella (½ p). Tässä tehtävässä valitettavan monet olivat kyllä ihan oikein selostaneet, että kuormitettaessa tehdään samannimisiä metodeita, mutta jättäneet mainitsematta, että kuormitusta tämä on vain silloin, kun samannimiset metodit sijaitsevat samassa näkyvyysalueessa! Javassahan voi olla vaikka minkälaisia samannimisiä metodeja, kunhan ne ovat eri luokissa/pakkauksissa... tästä olin tiukkana, kuormituksen kannalta tämä näkyvyysalue on olennainen seikka. Yht. 2 p Pakkaus (package) (Monisteen s. 62, 82-84) ----------------------------------------------------------------- Pakkaus on kokoelma käännettyjä luokkatiedostoja (.class), jotka yleensä liittyvät toisiinsa "teemaltaan" tai käyttötarkoitukseltaan (½ p). Pakkaus ei ole varsinaisesti olio-ohjelmoinnin väline, vaan tiedostonhallinnan, ohjelmien ylläpidon ja ohjelmistotuotannon työkalu (½ p). Luokka sijoitetaan osaksi pakkausta luokkatiedoston alkuun kirjoitettavalla package-määreellä (package Omapakkaus;), ja pakkauksesta otetaan käyttöön kulloinkin tarvittavat osat joko viittaamalla suoraan pakkauksen nimeen (esim. Lue.kluku()) tai kirjoittamalla luokkatiedoston alkuun import-määre (import pak.alipak; tai import pak.*;) (½ p). Pakkaus muodostaa oman nimiavaruutensa, ja sen avulla voidaan säädellä nimien näkyvyyttä luokkien välillä - kaikki luokat ovat jossain pakkauksessa, ainakin nimettömässä oletuspakkauksessa (½ p). Käyttöjärjestelmässä pakkaukset ja alipakkaukset näkyvät puumaisena hakemistorakenteena (½ p). Javan valmiista rakenteista suuri osa on toteutettu pakkauksina, esimerkiksi java.lang, java.io, java.util... (½ p) Yht 3 p (max. 2 p) Poikkeus (exception) (Monisteen s. 89-93) ----------------------------------------------------------------- Poikkeus on virhetilanne (tai muu epätavallinen tila) ohjelman suorituksessa (esim. nollalla jako, tiedostovirhe, taulukon olemattomaan alkioon viittaaminen, olemattomaan olioon viittaaminen...). Javassa on valmiit työkalut näiden poikkeusten käsittelyyn ja virhetilanteista toipumiseen (tai hallittuun kaatumiseen) (½ p). Poikkeuksia on kahdenlaisia (checked ja unchecked). Unchecked-poikkeus keskeyttää oletusarvoisesti ohjelman suorituksen (kaataa ohjelman), ellei ohjelmoija erityisesti halua käsitellä virhetilannetta hallitusti (½ p). Esimerkkinä taulukon väärä indeksointi. Checked-poikkeus vaatii ohjelmoijaa ottamaan kantaa poikkeukseen (esim. tiedostonkäsittely). Yksi tapa on delegoida virhe kutsuvalle ohjelmanosalle (throws-määre poikkeuksen synnyttävän metodin otsikossa) eli "antaa ohjelmalle lupa kaatua" (½ p). Toinen tapa on varautua poikkeukseen metodissa itsessään try-catch -rakenteella, jossa try-lohkoon sijoitetaan "vaarallinen" toimenpide, ja catch-lohko suoritetaan vain, jos toimenpide synnytti poikkeuksen (½ p). Arvostelu oli melko lievä, esim. riitti maininta try-catch -rakenteesta, jos asia muuten oli osattu. Yht. 2 p ============================================================================ 2. ============================================================================ Kokeen 24.6. tehtävän 2 arvosteluperusteita ------------------------------------------- Tarkastaja: Olli Lahti // Esimerkkiratkaisu, Sami Nikander // (Marko Ullgrenia mukaillen), 11.6.1998 public class Kilpailija { // Attribuutit ** 1 p. ** private String nimi; private int oikeellisuuspisteet; private int tyylipisteet; // Konstruktori (3 parametria) ** 1 p. ** public Kilpailija (String nimi,int oikeellisuus, int tyyli) { this.nimi=nimi; this.oikeellisuuspisteet=oikeellisuus; this.tyylipisteet=tyyli; } // Aksessorit ** kolmesta ensimmäisestä yhteensä 1 p. ** public String annaNimi() { return this.nimi; } public int annaOikeellisuuspisteet() { return this.oikeellisuuspisteet; } public int annaTyylipisteet() { return this.tyylipisteet; } // ** Kahdesta vertailumetodista yhteensä 2 p. ** // palauttaa true, jos this.nimi on aakkosissa toisen nimen jalkeen public boolean isompiNimi (Kilpailija toinen) { if(this.nimi.compareTo(toinen.nimi)>0) return true; return false; } // palauttaa true, jos this:n pisteet eivat ole pienemmat kuin toisen public boolean isommatPisteet (Kilpailija toinen) { if (( this.oikeellisuuspisteet * this.tyylipisteet) >= (toinen.oikeellisuuspisteet * toinen.tyylipisteet)) return true; return false; } public String toString() { // ** 1 p. ** return "Nimi: "+this.nimi+"\tOikeellisuus: "+ this.oikeellisuuspisteet+ " \tTyyli: "+this.tyylipisteet+"\tYht: " +(this.oikeellisuuspisteet*this.tyylipisteet); } } public class Teht45 { // Kysyy käyttäjältä sitkeästi pisteitä ** 1 p. ** private static int kysyPisteet() { int pisteet; do { System.out.println("Anna pisteet (0-50) :"); pisteet = Lue.kluku(); } while (pisteet < 0 || pisteet > 50); return pisteet; } // ** Pääohjelman pääosat 2 p. ** public static void main(String[]args) { String nimi; int oPisteet,tPisteet; Kilpailija[] kisaajat = new Kilpailija[20]; // kysytään käyttäjältä kilpailijoiden tiedot for (int i=0; i 0 && kisaajat[j-1].isompiNimi(apu) ) { kisaajat[j] = kisaajat[j-1]; j--; } kisaajat[j] = apu; } // Tulostetaan kilpailijat System.out.println("Kilpailijat järjestettynä nimien perusteella"); for (int i = 0 ; i 0 && !kisaajat[j-1].isommatPisteet(apu) ) { kisaajat[j] = kisaajat[j-1]; j--; } kisaajat[j] = apu; } //Tulostetaan kilpailijat System.out.println("Kilpailijat järjestettynä pisteiden perusteella"); for (int i = 0 ; i < kisaajat.length ; i++) System.out.println(kisaajat[i]); } } // ** yhteensä max. 12 p. ** Kommentteja arvostelusta: ------------------------- - Arvostelu on ollut sikäli joustava, että täydet pisteet on saanut, vaikka kaksi pistettä puuttuisikin yllä esitetyistä (yhteensä 14). - Vastauksen ei tarvinnut noudattaa esimerkkiratkaisun rakennetta, mutta sen kaikkien osien oli hyvä esiintyä jossakin tavalla tai toisella. - Epäoliomaisesta taulukkototeutuksesta on saanut enintään 6 p. - Pikkuvirheistä (esim. tyhjät sulut parametrittoman metodin kutsussa tai epäyhtälön merkin suunta) ei ole juurikaan sakotettu, jos ratkaisu on muuten hyvä ja selkeä. - Kaikki oikein toteutetut järjestämisalgoritmit on hyväksytty. Saman- pisteisten kilpailijoiden aakkosjärjestystä oli hyvä ainakin pohtia. Yleisiä virhekohtia, jotka ovat aiheuttaneet n. parin pisteen menetyksen: - Kilpailija-luokan tietojäsenet tulisi piilottaa (private). - Kilpailijaolion attribuutit pitäisi asettaa heti lopullisikseen luontihetkellä konstruktorissa. Varsinkaan koko taulukon täydeltä ei pidä luoda 'tyhjiä' ilmentymiä ennen niiden tietojen kysymistä. - Pisteiden oikeellisuus ja nimen ainutkertaisuus pitää tarkistaa. - Kaikkien kilpailijoiden järjestäminen ei ole Kilpailijan metodi. - Pääohjelman (main) omat muuttujat, taulukot jne. eivät näy apulais- metodeille (ilman parametrinvälitystä). - Sisäkkäiset for-silmukat on järjestämisen yhteydessä kytkettävä toisiinsa. (Sisemmän toisen rajan pitää riippua ulomman indeksin arvosta.) - Merkkijonoja EI voi verrata suoraan yhtäsuuruus- ja vertailuoperaattorein. ============================================================================ 3. ============================================================================ Johdatus ohjelmointiin, Avoin yliopisto, kesä 1998 Kertauskuulustelu 24.06.1998 Arvosteluperusteet ja esimerkkiratkaisuja (Sami Nikander) 3. Yleistä tehtävän arvostelusta: Tehtävässä piti selostaa luokan lataaminen (4 p), olion luonti (4 p) sekä ilmentymämuuttujien ja luokkamuuttujien eroa (4 p). Monisteessa (Luku 4.2, s. 70-73) esitetty tarkkuus asioiden selostamisessa riitti. Täten täydet pisteet luokan lataamisesta/olion luonnista sai melko vähäisellä kirjoittamisella. Alla on minimaalinen vastaus, jolla sai 4+4 pistettä kahdesta ensimmäisestä osatehtävästä: suora lainaus luvun 4.2 kappaleesta "Luokan lataaminen ja olion luonti". "Kun suoritetettava ohjelma viittaa ensimmäisen kerran johonkin luokkaan, tuo luokka ladataan (load) muistiin. Tällöin luokan staattinen kalusto asetetaan alkutilaan: luokkamuuttujat saavat alkuarvonsa, mahdolliset staattiset alustuslohkot suoritetaan. Aina kun suoritettava ohjelma luo olion eli luokan ilmentymän new-operaatiolla, tuon olion oma kalusto asetetaan alkutilaan: ilmentymän muuttujat saavat alkuarvonsa ja jokin konstruktori suoritetaan (nimenomaan tässä järjestyksessä!). " Luokkamuuttujan ja ilmentymämuuttujan eroista ymmärryksen osoitukseksi riitti joko antaa kattava ja havainnollinen Java-kielinen tai vaikkapa piirrosesimerkki selityksineen, tai vaihtoehtoisesti riittävän selkeä ja kattava sanallinen selostus, josta kävi ilmi, että asia on hallinnassa. Yleisin puute vastauksissa taisi olla liian suppea tai vajavainen esimerkki/selostus. Täydet pisteet sai esim. seuraavankaltaisella selostuksella: "Ilmentymämuuttuja syntyy, kun olio luodaan. Jokaisella oliolla eli luokan ilmentymällä on oma versionsa ilmentymämuuttujasta. Sen arvon muuttaminen yhdessä oliossa ei vaikuta muiden olioiden versioon samasta muuttujasta. Luokkamuuttuja taas liittyy luokkaan, ja kaikki ilmentymät jakavat sen yhteisesti, sen arvo näkyy kaikkiin ilmentymiin samana ja sen muuttaminen yhdessä oliossa muuttaa sen kaikissa olioissa. Esim. class Esim { public int i; // ilmentymämuuttuja public static int j; // luokkamuuttuja ... } Esim a = new Esim(); Esim b = new Esim(); a.i++; a.j++; System.out.println(b.i); // Tulostaa b.i:n alkuarvon 0 System.out.println(b.j); // Tulostaa b.j:n muuttuneen arvon 1" Seuraavassa muutamia yleisimpiä virheitä/puutteita, joita ilmeni vastauksissa (hämmästyttävää kyllä, yleisimmät virheet olivat samoja kuin aiemmissa kokeissa, vaikka kysymykset, virheet ja esimerkkiratkaisut olivat vapaasti saatavilla tärpeiksi... :-) - Olion luonnissa ei mainita konstruktorin kutsua - Konstruktorin kutsu väitetään tapahtuvaksi ennen ilmentymä- muuttujien alkuarvojen asettamista. - Staattisista alustuslohkoista ei ole mainintaa - Puhutaan epämääräisesti "kaluston alkutilaan saattamisesta" määrittelemättä tarkemmin, mitä tämä kalusto on. - Luokan lataaminen väitetään tapahtuvaksi aina kun olio luodaan. - Luokkamuuttujan idea sekoitettu näkyvyydensäätelyyn tai olion kapselointiin ============================================================================ 4. ============================================================================ Johdatus ohjelmointiin, kesä 1998 koe 24.6.1998 malliratkaisu ja arvosteluperusteet tehtävään 4 6.7.1998 Antti Kerminen Malliratkaisu ------------- public class Etsi { /* Etsii haettavan merkkijonon alkuindeksin merkkijonosta mjono. Tällä metodilla voi korvata String-luokan indexOf()-metodin, jos sitä ei sattunut muistamaan. */ private static int indeksi(String mjono, String haettava) { char[] taulu1 = mjono.toCharArray(); char[] taulu2 = haettava.toCharArray(); ohi: for (int i = 0; i <= taulu1.length - taulu2.length; i++) { for (int j = 0; j < taulu2.length; j++) { if (taulu1[i+j] != taulu2[j]) { continue ohi; } } return i; } return -1; } /* Pääohjelmametodi. */ public static void main(String[] parametrit) { System.out.print("Anna tiedoston nimi: "); SyoTdsto syöttö = new SyoTdsto(Lue.rivi()); System.out.print("Anna haettava merkkijono: "); String haettava = Lue.rivi(); String rivi; while ((rivi = syöttö.lue()) != null) if (rivi.indexOf(haettava) >= 0) // tai indeksi(rivi, haettava) >= 0 System.out.println(rivi); } } Arvosteluperusteet ------------------ Virheistä vähennettiin seuraavasti: kirjoitusvirhe 0 p pieni kielioppivirhe 1 p periaatteellinen virhe 2-4 p ohjelmasta puuttui esim. merkkijonon hakeminen 5 p Miinuksista riipumatta sai pisteitä kuitenkin seuraavasti: syöttötiedoston nimen ja haettavan merkkijonon lukeminen 1 p SyoTdsto-luokan oikea käyttö 1 p tiedoston läpikäynnin idea oikein 1-2 p merkkijonon hakemisen idea oikein 1-2 p