Ohjelmointi 1, syksy 2007 -- Demo 6

Tehtävät julkaistaan:
 Tiistain luennolla 16.10.2007
Palautus viimeistään:
 23.10.2007 klo 18
100% tehtävistä tarkoittaa:
 5 kpl tehty

Tässä demossa 100% on vain 5 tehtävää. Syy on se, että aikaa jäisi Eclipse-ympäristön asentamiseen kotikoneelle ja käytön opettelemiseen. Oletan, että ison ohjelmiston asentamisessa voi tulla yllättäviä vaikeuksia. IDEn käytössä on paljon klikkailtavaa ja sinne saattaa alussa vähän "eksyä valikoihin".

Asennuksen vaikeuksista, onnistumisista ja netistä löydetyistä ohjeista kannattaa laittaa raportteja sähköpostilistalle, jotta saadaan kaikille työkalupuolen viimeinenkin osa kuntoon. Samalla kalustolla sitten onnistuu Ohjelmointi 2 ja isojen Java-ohjelmien tekeminen mitä tahansa tarkoitusta varten!

Eli tässä on viikkotehtävä suositun TV-sarjan tyyliin: Pyri ottamaan Eclipsen työkaluksesi. Sinänsä se ei ole välttämätön Ohjelmointi 1:llä (teoriassahan koskaan ei tarvita IDEä ohjelmointiin!), mutta todennäköisesti se vähentää kokonaisvaivaa valtavasti.

Contents

Eclipsen käyttö

Tehtävä 1: Klikkailua

Seuraava ohjelmakoodi on Javan syntaksin mukaista, mutta sellaisenaan käyttökelvoton. Eclipsen (tai ylipäätään hyvän IDEn) avulla sen korjaaminen ei kuitenkaan ole konsti eikä mikään. Alla on opastus kohta kohdalta siihen, miten koodi saadaan klikkailemalla kuntoon ja prikulleen Java Code Conventions -ohjeistuksen mukaiseksi.

Huono, sanotaanko patologinen, Java-koodi:

/*ohjelma*/ /** luokka. */ public class TaasYksiJooJooShow {
/** Hämmentää ihmisen mieltä.  @param tau - harava @return
syksynlehti */ public static double ka(double[] tau){ double
s = 0.0; int n = tau.length; for (int pii=0;pii<n;pii++){ s
+= tau[pii]; } return s /
n; } public static void main(String[] args){ double[] aava =
{-10.0, -7.0, 3.0}; double[] ula_pasta = {1.2, 3.4, 5.6, -7.8,
9.1, 2.3, 4.7, 8.9, 0.0}; System.out.printf(
"Siitä tulee %.3f%n", ka(aava));System.out.printf
("Tai siitä tulee %.3f; en mie tiiä%n", ka(ula_pasta)); } }

Kuntoon Eclipsellä:

  1. Luo "workspace", jonka alle voit tehdä projekteina demot 6-10 ja harjoitustyön. Voit tehdä sen myös samaan paikkaan, jossa on jo demot 1-5. Luennolla näytettiin, kuinka "New Project" -toiminnon avulla saa otettua olemassaolevat koodihakemistot mukaan workspaceen. Muista säätää Workspacen Compiler Compliance -asetus uuden Java-version mukaiseksi ja koodauskäytänteiksi "Java Conventions" sillä muutoksella että käytetään pelkkiä välilyöntejä eikä koskaan tabulaattoria. Sisennykseksi neljä saraketta. Tallenna asetukset uudella nimellä.

  2. Tee projekti demolle 6. Esim. rakennenäkymästä oikeata nappia ja ponnahdusvalikosta "New"->"Project"->"Java Project" jne.

  3. Tee demon 6 Eclipse-projektiin uusi luokka, jonka nimi on sama kuin yo. ohjelman nimi eli TaasYksiJooJooShow. Siis klikkaa oikeata nappia "(default package)" -tekstin päällä ja valitse "New"->"Class". Sitten tarvitsee kirjoittaa "Name"-kenttään luokan nimi.

    (Avautuva helppokäyttötoiminto valittanee, että "use of the default package is discouraged". Se valittaa kyllä ihan aiheesta, mutta emme kuitenkaan käytä vielä paketteja; sijoita siis luokat toistaiseksi "(default package)" -nimisen paketin alle, joka löytyy aina projektista. Se tarkoittaa, että lähdekoodit sijoittuvat projektin kansioon, eivät mihinkään alihakemistoon. Isommissa projekteissa lähdekoodit on syytä jakaa paketteihin ja alipaketteihin niiden roolin mukaan, jotta kokonaisuus pysyy jäsentyneenä)

  4. Deletoi automaattisesti syntyvä koodi, ja kopioi tilalle yo. huono koodi vaikkapa demojen WWW-versiosta.

  5. Varmista että kopioinnissa ei tullut virheitä, eli suorita luokka: Rakennenäkymässä oikeata nappia luokan päällä ja "Run As"->"Java Application"...

  6. Sovella automaattista muotoilua: Koodi-ikkunassa oikeata nappia ja ponnahdusvalikosta "Source"->"Format".

    HUOM: Voit alkaa samalla opettelemaan näppäinyhdistelmiä. Kuten huomaat, valikossa lukee (ainakin oletuksena) formatointivalinnan perässä "Ctrl+Shift+F". Se tarkoittaa, että ei tarvitse edes hiirellä klikata, vaan voit painaa Ctrl ja Shift pohjaan ja sitten vielä F-kirjain (mikä tapahtuu noin millisekunnissa) ja sinulla on kauniisti asemoitu Java-koodi... Muutkin useimmin tarvittavat toiminnot ovat saatavilla vastaavien näppäinyhdistelmien takaa.

  7. Koodissa on huonot nimet. Korjaa ne käyttämällä Eclipsen refaktorointi-toimintoa. Vinkkinä siihen, mitä koodi oikeasti tekee: "ka" saattaisi olla huonosti nimetty "keskiarvo"? Nimen muuttaminen Eclipsellä:

    • kursorin pitää olla muutettavan nimen jonkun esiintymän kohdalla
    • sitten esim. oikeata hiirenkorvaa ja valikosta "Refactor"->"Rename". Syötä parempi nimi ja kliksuttele OK:ta dialogi-ikkunoihin.
  8. Eclipsen tekstieditorilla voi korjata kommenttien sisällön.

  9. Jossain vaiheessa nimeä koko luokka paremmin. Siis tietenkin automaattisella refaktoroinnilla kliksutellen.

Muista jatkuvasti, että workspacesta ja sen sisältämistä projekteista syntyy konkreettinen hakemistorakenne tiedostojärjestelmään. Siis Eclipsen käyttäminen koodien tekemisessä tekstieditorin sijasta ei millään tavoin vaikuta esim. demojen palautuskäytäntöön: Lopuksi sinulla on .java -päätteisiä lähdekooditiedostoja jossain kohtaa hakemistorakennetta, eivätkä ne edelleenkään ole mitään sen kummempaa.

Tehtävä 2: Debuggeri ja koodin suoritusjärjestys.

Vastauksesi tähän on tekstitiedosto, jossa on vastauksia. Tentissä tulee olemaan vastaava tehtävä, jossa sinun on osattava "ajatella kuin ohjelmaa suorittava tietokone", mm. suoritusjärjestyksen ja muuttujien sisällön osalta. Tehtävässä on joku lyhyt ohjelma, kuten tämä (suoraan oikotieläisten tentistä itse asiassa):

public class Tenttiohjelma1{
    public static String teeJonoLuvusta(int luku){
        return Integer.toString(luku);
    }

    public static void main(String[] args){
        int lukum = 4;
        String[] taulukko = new String[lukum];

        for (int i = 0; i<lukum; i++){
            if (i == lukum / 2){
                taulukko[i] = null;
            } else {
                taulukko[i] = teeJonoLuvusta(i);
            }
        }
        String tuloste = taulukko[0] + taulukko[1];
        System.out.println(tuloste);
    }
}

Tentissä tulee vastaavan mittainen ennaltanäkemätön koodi sekä joitakin täsmäkysymyksiä, joihin voit nyt selvittää vastauksen käyttämällä apuna Eclipsen Debugger-työkalua. Tentissä tätä luksusta ei ole, mutta siihen asti voit käyttää debuggeria tällä tavoin erilaisten ohjelmointirakenteiden suoritusjärjestyksen oppimiseksi. Kysymykset:

  1. Mitkä rivit ohjelmasta suoritetaan, ja missä järjestyksessä? Eli kirjoita lauseita sisältävien rivien rivinumerot siinä järjestyksessä kuin virtuaalikone suorittaa ne (vastaukseen tulee monta numeroa).

    Debuggeri kertoo kaiken suoraan, kun klikkailet vaan ja katselet (ohjeet alla); tenttiin asti on aikaa oppia tekemään sama mielessään.

  2. Mitä ohjelma tulostaa?

  3. Moneenko olioon on olemassa viite jossakin, kun riviä 18 (System.out.println(tuloste);) suoritetaan?

  4. Mitkä kaikki paikalliset muuttujat ovat tässä vaiheessa olemassa? (Kirjoita lähdekoodissa käytetyt nimet.)

  5. Mitkä ovat olemassaolevien paikallisten primitiivimuuttujien arvot tässä vaiheessa (primitiivi siis perustyyppi, ei viite olioon kuten taulukkoon tai merkkijonoon)

Miniohjeet Eclipsen debuggerin käyttöön:

  1. Ota koodi demo 6:n Eclipse-projektiisi (luokan luonti ja copy-paste kuten edellisessä tehtävässä, nimeksi tietysti Tenttiohjelma1)

  2. Aseta ohjelman alkuun breakpoint debuggausta varten: Koodi-ikkunassa klikkaa hiiren oikeata nappia koodi-ikkunan reunuksessa main-metodin määrittelyrivillä. Sitten ponnahdusvalikosta "Toggle Breakpoint". Pitäisi ilmestyä jonkinlainen pallukkamerkki sinne reunukseen. Debuggeri tulee pysäyttämään suorituksen tässä kohtaa.

  3. Käynnistä debuggaus: Rakennenäkymässä luokan kohdalla ponnahdusvalikosta "Debug As"->"Java Application". Sitten tulee ikkuna, jossa kysytään perspektiivin vaihtamista. Vaihda toki debuggaus-asetteluun. Ikkunat vaihtaa paikkaa jännästi. Ohjelma käynnistyy, mutta sen suoritus pysähtyy mainin ensimmäiselle riville!

  4. Suorita ohjelmaa rivi riviltä. Nähtävästi voit käyttää Debug-näkymästä kuvaketta "Step Into". Se askeltaa ohjelmaa pienin mahdollinen pala kerrallaan eli menee aina aliohjelmiin sisälle ja tulee sieltä pois sitä mukaa kun koodirivit suoritetaan koneessa.

    (Jos et haluaisi nähdä jonkun aliohjelmakutsun suoritusta, voisit valita "Step Over" jolloin suoritus etenisi läpi kaikista aliohjelmakutsuista ja debuggeri pysähtyisi seuraavan kerran ennen nykyisen aliohjelma-aktivaation seuraavaa koodiriviä. "Step Return" puolestaan etenisi kunnes nykyinen aliohjelmasuoritus päättyy)

    Havainnoi, mitä näytöllä näkyy:

    • Debug-näkymässä näkyy aliohjelmien kutsupino eli millä koodirivillä mikäkin aliohjelma on kutsunut mitäkin että on päädytty nykyiseen, pysäytettyyn (suspended) tilanteeseen. Sieltä on luettavissa pinon ylimmältä riviltä nykyinen luokka ja koodirivin numero.

    • Koodi-ikkuna näyttää vihreällä taustalla ja nuolisymbolilla seuraavaksi suoritettavan koodirivin.

    • Variables-näkymä näyttää meneillään olevan aliohjelma-aktivaation lokaalit muuttujat sekä niiden arvot. Mikäli muuttuja on viite olioon, oliosta nähdään sen sisäinen tila, tässä tapauksessa taulukoiden sisältämät viitteet sekä merkkijonojen sisältämät merkit.

      (Sisäinen tila on lähdekoodin mielessä piilotettu, mutta debuggerissa sitä voi jopa muuttaa! Se on debuggerin idea: konepelti auki ja kaikki seinät läpinäkyviksi! Tämä mahdollistaa syvemmän ymmärryksen ohjelman toiminnasta niin virheenkorjausta kuin oppimistakin varten!)

  5. Kun main() -metodi päättyy, suoritus jatkuu jostakin muualta, mitä et ole itse koodannut. Voit keskeyttää "stop-napista" tai jatkaa luonnolliseen loppuun "play-napin" näköisestä "Resume" -toiminnosta.

  6. Debugger- ja ohjelmointi-näkymien välillä voi vaihdella valikon "Window"->"Open Perspective" avulla. Jos saat ikkunat joskus asettumaan ruutuun täydellisesti, voinet tallentaa sen johonkin nimellä "Täydellinen koodausperspektiivi" ja vaihtaa em. valikosta siihen aina tarvittaessa. Oletus Java-ohjelmoinnille mahtaa olla perspektiivi nimeltä "Java".

Tentissä teet siis saman ilman debuggeria (helposti, koska siihen mennessä ymmärrät, miten ohjelma toimii... oppimisessa debuggeri ja rivi riviltä suorittaminen on verraton apuväline)

Ohjelmointitehtäviä

Tehdään vähän ohjelmointitehtäviä; tämän pitäisi olla nyt helpompaa, koska on käytössä järeämpää työkalustoa. Huomaa mm.:

  • Uusi luokka tehdään projektin ponnahdusvalikosta "New"->"Class". Klikkailemalla tässä kohtaa rukseja ruutuun, voi tehdä automaattisesti mainin ja kommentit eli ruksita "public static void main(..." -kohta sekä "Generate comments". MUISTA TÄYDENTÄÄ kommentteihin sisältö, koska sitähän automaatti ei voi arvata.
  • Aliohjelmalle saa kommenttityngän "Source"->"Generate element comment" (tekstikursorin pitää tietty olla kommentoitavan aliohjelman sisällä)
  • Automaattinen tekstintäydennys antaa aina pisteen kirjoittamisen jälkeen valikon asioista, joita on mahdollista kirjoittaa pisteen jälkeen. Kun kirjoittaa lisää merkkejä, valinta yksikäsitteistyy; voi myös selata nuolilla ja painaa enteriä kun kohdalla on juuri haluamasi täydennys.

Tehtävä 3:

Viime demon tehtävässä 3 tehtiin merkkijonon merkkien korvaamista toisella. Tee kaksi laajennusta:

  • Lisää "käärealiohjelma", joka tarjoaa saman toiminnon (merkin esiintymän korvaaminen toisella) mutta muuttumattomille eli String -luokan merkkijonoille. Siis uusi aliohjelma, joka käyttää aiemmin tehtyä mutta jonka oma signatuuri on String metodi(String, char, char) eikä void metodi(StringBuilder, char, char). Kuormita nimi.

  • Lisää toinen aliohjelma, joka mahdollistaa usean merkin korvaamisen samalla kutsulla. Aliohjelma ottaa vastaan kaksi samanmittaiseksi oletettavaa merkkitaulua, joista ensimmäisessä on korvattavat ja toisessa korvaavat merkit. Käyttöesim:

    Jono alussa: Kuumaa kahvia aamulla
    Jono sitten: xUUm-- k-hvi- --mUll-
    

    Tämän toiminta on niin erilainen, että kannattaa ehkä nimetä eri tavoin(?).

Tehtävä 4: Palindromitarkistimen suunnittelu

HUOM: Muutin tehtävänantoa alkuperäisestä! Vastaukseksi tulee nyt tekstitiedosto, jossa suunnittelet algoritmin! Suunnittelu on tärkeää, eikä koodin tekeminen voi onnistua ellei ole ensin valmisteltu ideaa!

Määritelmä:

Suomenkielinen palindromi olkoon merkkijono, jonka kaikki kirjainmerkit a-z, å, ä, ö ovat samassa järjestyksessä, luettiinpa jono kummin päin tahansa (lopusta alkuun tai alusta loppuun). Muilla kuin em. kirjainmerkeillä ei ole merkitystä merkkijonon palindromisuuteen eikä myöskään sillä ovatko merkit pieniä vai isoja (A-Z,Å,Ä,Ö).

Oletus:

Sinulla on tarkasteltavana mielivaltainen merkkijono, jossa on mitä tahansa Unicode-merkistön merkkejä mikä tahansa määrä. Esimerkkejä numeroituvasti äärettömästä määrästä vaihtoehtoja:

"caipiroska"

"Anna"

"v4wrqluiytewvb 98l4qwy5bv978yt 7tv"

"Atte -kumiorava, varo óÜ imukeTta!!  !+# "

"21346981234697812346978123"

Näistä suomenkielisiä palindromeja ovat "Anna" (selvästi), "Atte -kumiorava, varo óÜ imukeTta!! !+# " (koska erikoismerkit tai Münchenin ü ja óle ó eivät vaikuta) sekä "21346981234697812346978123" (vaikka kirjaimia ei ole yhtäkään; tämä on triviaali palindromi, koska numeroilla ei ole väliä). Muut eivät ole.

Tehtävä:

Valmistaudu tarkistamaan onko mielivaltainen merkkijono em. määritelmän mukainen palindromi. Merkkijono toimitetaan käyttöösi esim. isolla ruutupaperilla, jossa on yksi merkki aina yhdessä ruudussa.

Ohjeet:

Pohdi ongelman ratkaisemista ja tee algoritmi, joka käsittelee yksittäisiä merkkejä ja niistä muodostuvaa jonorakennetta. Älä mieti Java-kieltä tai APIa! Vaan mieti, mitä operaatioita sinä ihmisenä tekisit ongelman ratkaisemiseksi, vaikkapa kynän (ehkä kumin) ja riittävän ison ruutupaperipinkan kanssa. Tuota algoritmi, jolla selviydyt tehtävästä jossakin ajassa vaikka merkkijonossa olisi tuhansia tai miljoonia merkkejä. Kunhan algoritmi on yleispätevä ja ratkaisee ongelman mille tahansa merkkijonolle.

Algoritmin suunnittelussa voit:

  • osittaa tehtävää "alialgoritmeiksi": mitä noin karkeasti ottaen pitää tehdä ensin, mitä sitten jne.. miten yksitellen ajateltuna kukin "alialgoritmi" jakautuu edelleen...
  • toisaalta löytää heti pohjatasolta yksityiskohtia, joiden hoitaminen jollain tavoin näyttää välttämättömältä karkeampien asioiden hoitamiseksi. Esim. "ilmeisesti yksi asia on, että pitää tarkistaa kahden kirjaimen samuus riippumatta siitä, onko iso vai pieni... sanotaanpa tätä vaikka nimellä 'onkoIsoTaiPieniMuttaSama(merkkiA, merkkiB)'".
  • käyttää "ohjelmoinnillisia" rakenteita: muuttujia, ehto- ja toistorakenteita, "alialgoritmin" suorittamista karkeamman algoritmin osana.

Lopulta kun on karkeita ja hienojakoisia asioita, niin tavoite on saada niistä koostettua kaikissa mahdollisissa tilanteissa toimiva kokonaisuus. Ei ohjelmointi ole mitään muuta tekemistä kuin tätä.

Alkuperäinen tehtävä, jonka myös saa tehdä, mutta ei ole pakko:

Tee ja testaile aliohjelma, joka tarkistaa, onko parametrina annettu merkkijono palindromi (ts. ovatko kirjainmerkit samat etuperin ja takaperin luettuna). Aliohjelma tietysti palauttaa totuusarvon. Se ei saa hämääntyä whitespacesta tai erikoismerkeistä ja isoilla/pienillä kirjaimilla ei saa olla väliä. Merkkijono-APIt käyttöön. (jos löytyy valmiiksi isPalindrome() tai muu yhden rivin vastaus, niin sitä ei saa käyttää, mutta muuten kaikkea mikä APIsta löytyy).

Esim. jono "Atte -kumiorava, varo imukeTta!!  !+# " on tunnistettava palindromiksi erikoismerkeistä huolimatta.

Syy tehtävän korvaamiselle on, että Java-koodin tuottaminen edellyttää joka tapauksessa ongelman ratkaisun ajatteluprosessia. Koetetaan hahmottaa se ensin. Vasta sen jälkeen on mahdollista yrittää löytää ratkaisukeinoja jostakin ohjelmointikielestä. Yritys ajatella "suoraan ohjelmointikielellä" on uskoakseni pullonkaula alkuvaiheessa: kun ongelman luonne on hahmottumaton, ei voi löytää toteutuskeinoa. Eri asia sitten jossain vaiheessa, kun harmaisiin aivosoluihin juurtuu ratkaisumalleja kuin apteekin hyllylle.

Tehtävä 5: Rivittäjä konsolitulostusta varten:

Tee ja testaile aliohjelma, joka ottaa parametrina String-viitteen, tulostusvirran viitteen ja kokonaisluvun ja joka tulostaa Stringin sanat (tyhjämerkein erotellut osat) virtaan siten että jokainen rivi katkaistaan viimeisestä sanavälistä ennen kuin kokonaisluvun ilmoittama merkkimäärä on kirjoitettu. Jos sana on pidempi kuin kokonaisluvun ilmoittama määrä, kirjoitetaan rivin viimeiseksi merkiksi kenoviiva ja tulostus jatkuu keskeltä sanaa. Aiemmat rivinvaihdot on hukattava, mutta kappalejakojen on säilyttävä.

Esim jos alkuperäinen jono on:

Tekstiesim:

On aika pitkä teksti jos kauaskantoisin
tekstirivi saa olla vain 12 merkkiä.

Niin tuloste 12 merkin asetuksella on:

Tekstiesim:

On aika
pitkä teksti
jos
kauaskantoi\
sin
tekstirivi
saa olla
vain 12
merkkiä.

Bonussektori

Tehtävä 6: APIksi APIn paikalle?

Toteuta itse Integer.parseInt(String,int) -metodin toiminnallisuus lukujärjestelmille, joiden kantaluku on mikä tahansa väliltä 2-10.

Siis tee aliohjelma "int omaParseInt(String muunnettavaLuku, int kantaluku)", joka tutkii merkkijonon merkki kerrallaan ja yrittää muuntaa sen kokonaislukutyyppiseksi, riippuen lukujärjestelmän kantaluvusta. Samaan tapaan kuin tavoittelemasi toiminta, heitä soveltuva poikkeus, jos syötemerkit eivät ole mallillaan. Huomioi etumerkki.