Ohjelmoinnin perusteet, syksy 2001 Välikoe 12.11, tehtävä 3. Arvostelu Jussi Kollin, jussi.kollin@helsinki.fi Tehtävänanto: Laadi edellisen tehtävän luokkaa käyttäen sovellus Laivuri, jolla voi "ohjata laivaa". Laivaa ohjataan näppäimistöltä seuraavasti: * luku 1: käännetään vasemmalle, ohjelma kysyy astemäärän * luku 2: käännetään oikealle, ohjelma kysyy astemäärän * luku 3: säädetään nopeutta, ohjelma kysyy uuden nopeuden * muut luvut lopettavat purjehduksen Ohjelma tulostaa joka operaation jälkeen laivan tilan. Tehtävä oli ollut kurssilla jo harjoitustehtävänä, joten harjoituksia tehneille tämän ei olisi pitänyt tuottaa ongelmia. Tehtävä jakautui neljään osa-alueeseen: silmukan toteutukseen, valintalauseisiin, muuttujien käyttöön sekä metodien kutsuun, erityisesti konstruktorin. Pisteet näistä osista annettiin seuraavan listan mukaan: - silmukka: * tulostaa tilan 1p * jos pelkkä laiva.toString(); niin 1p sijaan -1p. Siis 2p menetys. * Lue.kluku() / Lue.merkki() silmukan sisäpuolella, 1p * terminoi oikein eli toistoehto on oikein ja silmukka päättyy oikein, 1p * ei mitään silmukkaa, max 0p osiosta * jos 'if' 'while':n tilalla, max 0p osiosta = max 3p - valintalauseet: * vaikuttavat oikeisiin lauseisiin, 2p * logiikka oikein (ehtolausekkeet, ei virheellistä elseä) 2p * jos ( ) unohtunut ehtojen ympäriltä, niin kahdesta pisteestä toinen pois * samoin, jos käytetty sijoitusoperaattoria = yhtäsuuruusoperaattorin sijaan niin kahdesta pisteestä toinen pois. * jos if-else-rakenne väärin niin molemmat pisteet pois. Tämä siis vain jos silmukan rakenne edellytti tällaista. = max 4p - muuttujat: * esitelty oikein ja oikeissa paikoissa 1.5p * toistoehdossa esiintyvä ei toistolauseen sisällä * kenttänä määritelty muuttuja ei ole static (-1p eikä -1.5p) * Laiva-olio näkyy vain main-metodissa, mutta käytetään muuallakin * int nopeus = Lue.dluku(); * HUOM! laiva.asetaNopeus(Lue.kluku()); ei virhe * nimet eivät yksikirjaimisia 0.5p * olioilla ei saa olla yksikirjaimisia nimiä * perustietotyypeillä int i, char c, double d ja vastaavat hyväksytty. = max 2p - oliot & metodit: * sulkeet metodikutsun perässä 1p * Jos konstruktorin perästä uupui sulkeet, menetti heti tämän pisteen. Muissa tapauksissa raja oli 50% kutsuista. * ei luotu ilmentymää Laiva-luokasta tai ei kutsuttu ilmentymän metodeja -4p * new uupui -1½p tai new.Laiva(); , ei yhdessä edellisen kanssa -2p * static-määre uupui metodilta -1p * metodin sisäisellä muuttujalla private/public määre esittelyn edessä: public static void main(String[] args) { private int valinta; ... } -1p = max 1p - virheet: * oudot tarkistukset, joita ei oltu kommentoitu ja jotka eivät olleet loogisia, -1p * if (asteet > 0) asteet = -asteet; // vasemmalle kääntyminen, -1p * if (asteet < 0) asteet = 0; // ei pistemenetystä * do { ... } while (asteet < 0 || asteet > 360); // ei pistemenetystä * nopeuden (<0, >0) merkitys kääntymisen suunnassa toteutettu Laivurissa, -1p Tehtävässä oleellisinta oli luokan Laiva ilmentymän luominen ja tämän olion metodien kutsuminen. Valitettavan moni ei näitä ollut osannut. Ensin muuttujalle määritellään tyyppi, esim. Laiva-tyyppinen, pulju- niminen muuttuja esitellään näin: Laiva pulju; Olio luodaan kutsumalla luokan konstruktorimetodia, siis lopussa on sulkeet, vaikka konstruktori ei saisikaan parametreja: pulju = new Laiva(); Monet olivat tehneet myös omia pääohjelman pikku apulaismetodeja auttamaan tehtävän kanssa, mutta useimmat olivat tehneet tässä myös pistevähennysten veroisia virheitä. Mikä on seuraavassa vikana? public class Laivuri { public static void main(String[] args) { Laiva ruuhi = new Laiva(); ... käänny(); ... } private void käänny() { System.out.println("Anna asteluku?"); ruuhi.käännäRuoria(Lue.kluku()); } } * Metodi käänny() ei ole luokka- vaan oliometodi. Sen kutsuminen edellyttäisi Laivurin ilmentymän luomista, -1p. * Muuttuja ruuhi on esitelty pääohjelman sisällä, mistä se ei näy metodiin käänny(). Tätä varten ruuhi pitäisi välittää parametrina, -1.5p. Aivan liian monessa paperissa oli kuviteltu, että pelkkä kutsu laiva.toString() tulostaa laivan tilan. Näin ei todellakaan ole, vaan laiva.toString() palauttaa vain laivan esityksen merkkijonona. Jos metodin kuvitteli tekevän jotain muuta, niin siitä menetti yhden pisteen. Toisen pisteen menetti siitä, jos tilaa ei tulostettu; käytännössä tämä siis tarkoitti kahta menetettyä pistettä. Hyvään ohjelmointityyliin kuuluu selkeiden muuttujanimien käyttö. En tällä kertaa ottanut kantaa kysymykseen "onko 'testi' järkevä muuttujan nimi luokan Laiva ilmentymälle", vaan tyydyin vähentämään puoli pistettä jos vastauksessa oli käytetty yksikirjaimista muuttujan nimeä. Poikkeuksena tähän sääntöön olivat int i, double d sekä char c, koska nämä ovat yleisesti käytettyjä lyhenteitä perustietotyyppien yhteydessä. Olion yhteydessä armoa ei löytynyt. Lopulta vain muutama menetti pisteen ylöspäin pyöristyksen ansiosta. Jotkut olivat käyttäneet if-else- ja break-lauseita silmukan sisällä. Seuraava virhe esiintyi muutamilla: do { valinta = Lue.kluku(); if (valinta == 1) { ... // (1) } if (valinta == 2) { ... // (2) } if (valinta == 3) { ... // (3) } else break; // Poistuu do-while-silmukasta ... } while (true); Jos käyttäjä antoi syötteenä esimerkiksi ykkösen, niin ensimmäinen vertailu on tosi ja (1) suoriteta. Seuraavaksi verrataan syötettä kakkoseen, joka nyt on epätosi eikä (2):sta suoriteta. Seuraavana on vertailu kolmoseen, joka myös on epätosi eikä (3):sta suoriteta. Koska tätä if-lausetta seuraa else, niin tämä haara, eli break-lause, suoritetaan. Siten silmukka päättyy, vaikka käyttäjä antoi aivan kelvollisen käskyn. Jos tämän välttämättä halusi tehdä break-lauseen avulla, olisi ehtolauseiden pitänyt olla seuraavanlaiset: do { valinta = Lue.kluku(); if (valinta == 1) { ... } else // valinta != 1 if (valinta == 2) { ... } else // valinta != 1 && valinta != 2 if (valinta == 3) { ... } else break; // valinta != 1 && valinta != 2 && valinta != 3 ... } Vasemmalle kääntyminen oli monissa papereissa tehty varsin oudosti; käyttäjän odotettiin syöttävän negatiivinen luku. Jos tieto tästä oli joko kommenteissa tai kerrottu käyttäjälle tulostuslauseissa, en ottanut pistettä. Tehtävä kuitenkin oli tarkoitettu tulkittavaksi siten, että vasemmalle käännyttäessä kirjoitetaan positiivinen luku, josta otetaan vastaluku ja tämä välitetään laivalle parametrina. Jos laiva ei kääntynyt negatiivisella luvulla, ei virhepisteitä tullut. Mitään tarkistuksia tässä luokassa ei kuulunut tehdä. Niistä ei saanut lisäpisteitä, mutta virhepisteitä saattoi silti tulla. Kuitenkin on huomattava, että jos Laiva-luokasta jätti pois tarkistukset ja sijoittikin ne Laivuriin, kakkostehtävästä tod.näk. lähti reippaasti pisteitä. Poikkeuksena on vastaus, jossa perusteltiin varsin tarkkaan miksi tarkistukset jätettiin Laivuriin eikä Laivaan. Pointtina on, että jos esim Laivan maksiminopeus paranee, riittää muuttaa luokan asetaNopeus(double) metodista yläraja eikä Laivuri-luokkaan tarvitse koskea. Samoin on laivan asia itse päätellä mitä ruorin kääntäminen -50 astetta tarkoittaa; jotkut toteuttivat Laivurissa laivan kääntymisen oikealle, kun nopeus oli negatiivinen. Tästä saadaan helposti tuloksena laivan kääntyminen luovittaessa väärään suuntaan, jos tämä tarkistus oli tehty myös Laiva-luokassa (kuten kakkostehtävän tehtäväanto antoi ymmärtää). Lyhyt esimerkkiratkaisu: public class Laivuri { public static void main(String[] args) { // Tässä voisi tulostaa käyttöohjeita Laiva ruuhi = new Laiva(); int valinta; // Tämä on määriteltävä silmukan ulkopuolella! do { // Käyttäjältä on kysyttävä silmukan sisäpuolella // komentoa; muuten jäädään junnaamaan samaa käskyä // yhä uudelleen ja uudelleen eikä silmukasta päästä // koskaan pois valinta = Lue.kluku(); if (valinta == 1) { System.out.println("Montako " + "astetta vasemmalle?"); ruuhi.käännäRuoria(-Lue.kluku()); } if (valinta == 2) { System.out.println("Montako " + "astetta oikealle?"); ruuhi.käännäRuoria(Lue.kluku()); } if (valinta == 3) { System.out.println("Mikä on " + "uusi nopeus?"); ruuhi.asetaNopeus(Lue.dluku()); } System.out.println(ruuhi); } while (valinta >= 1 && valinta <= 3); } } Pistejakauma: 0 p 19 kpl ******************* 1 p 15 kpl *************** 2 p 13 kpl ************* 3 p 4 kpl **** 4 p 10 kpl ********** 5 p 8 kpl ******** 6 p 23 kpl *********************** 7 p 35 kpl *********************************** 8 p 41 kpl ***************************************** 9 p 59 kpl *********************************************************** 10 p 69 kpl ********************************************************************* Vastauksen palautti 296 henkilöä, keskiarvo 7.0