keskiviikko 21. tammikuuta 2009

Demo 1 - kommentit

Ajattelin tässä samalla kirjoittaa hieman kommentteja demoista. Eipä tuosta liene haittaakaan ole. Mikäli lukijaa sattuu kiinnostamaan itse tehtävät, ne löytyvät osoitteesta http://users.jyu.fi/~antkaij/opetus/okp/2009/demot/1.html . Kertaan kuitenkin kommentoimani tehtävät tässä, jotta ei tarvitse pitää kahta sivua samaan aikaan auki ja vilkuilla edestakaisin. Joten sitten kommentteihin ja omiin ratkaisuihin...

Tehtävä 1 alkoi lupaavasti seuraavalla kuvauksella "Kommentoi Java-kielen operaattoreiden presedenssijärjestystä. Onko siinä korjaamisen varaa ja jos on, minkälaista? Perustele.". En ole mikään Java-guru, todettakoon se nyt alkuun. Kuitenkin kuuklettamalla löytyi seuraava sivu: http://www.uni-bonn.de/~manfear/javaoperators.php . Aluksi touhu meni hieman ihmettelyn puolelle. Kuitenkin jossain vaiheessa välähti ja rupesin ihmettelemään boolean operaattoreiden ja yhtäsuuruusvertailujen välistä järjestystä. Tämä sattuikin sitten olemaan olennaisin havainto.

Kenties hieman yllättäen Javassa lauseke "A && B == C || D" arvioituu seuraavalla tavalla: "A && (B == C) || D". Tämä ei ole lähelläkään sitä, mitä voisi olettaa tapahtuvan eli toisin sanoen voisi luulla, että loogista sen sijaan olisi seuraava tulos: "(A && B) == (C || D)". Luennolla selvisi, että tämä ihmeellinen, harmaita hiuksia kasvattava tapa, periytyy vanhasta kunnon C:stä (näköjään päässyt hieman unohtumaan :) ). Joten mikäli Javassa (tai C:ssä) haluaa käyttää booleaneja ja yhtäsuuruusvertailua samassa lauseessa, kannattaa suluttaa itse.

Toinen tekemäni huomio liittyi >>> operaatioon, joka myös nimellä "unsigned bit shift right" tunnetaan. Hieman asiaa tongittuani näyttää siltä, että kyseessä on operaatio, joka vain hävittää etumerkin shiftaamisen lisäksi. Syy miksi operaatiota ei löydy toisinpäin (eli <<<) on luonnollinen. Luvullahan on etumerkki vain toisessa päässä (ellei käytä jotain ihmeellistä lukujärjestelmää). Toisessa tehtävässä oli seuraava tehtävänanto: "Hahmottele merkkijonojen käsittelyyn sopivien lausekkeiden abstrakti syntaksi.". Tehtävässä lähdin liikkeelle yksinkertaisista vaatimuksista. Eli tarkoituksena oli luoda syntaksi, jossa voi yhdistää sekä monistaa merkkijonoja sekä poimia tietyn tai tietyt merkit merkkijonosta annetun tai annettujen indeksien (alkaa nollasta) avulla. Eräs jossain määrin järkevä tapa tehdä tämä voisi olla seuraava:


E := C, missä C on jokin merkki tai kokonainen merkkijono
| E + E # tämä yhdistää merkkijonoja
| N * E # tämä monistaa merkkijonoa N (N e N (kuvittele tähän luonnollisia lukuja edustava hieno N)) kertaa
| C[N] # palauttaa tietyn merkin
| E[N:] # merkit N:stä eteenpäin
| E[:N] # merkit ennen N:ää
| E[N:N] # em. yhdistelmä Alun perin viimeisissä oli C E:n sijalla.


Osoittautui kuitenkin, että on käytännöllisempää hyödyntää yleisempää E määrittelyä, jolloin operaatioita voidaan linkittää vapaammin. Kolmas ja neljäs tehtävä olivat seuraavia: 3. Kirjoita ohjelma, joka ottaa syötteenä tavallisen aritmeettisen lausekkeen (infix-muodossa) ja tulostaa sen postfix-muodossa. 4. Kirjoita ohjelma, joka ottaa syötteenä postfix-muotoisen aritmeettisen lausekkeen ja laskee lausekkeen arvon. Pidä huoli, että ohjelmasi kykenee lukemaan edellisen tehtävän ohjelman tuotosta. Lähdin ratkaisemaan kumpaakin tehtävää yleisen abstraktiin syntaksipuuhun pohjautuvan ajatuksen kautta. En kuitenkaan saanut ratkaisuani aivan valmiiksi. :) Sinällään melko yksinkertaiset tehtävät osoittautuivat yllättävän työläiksi. Yleisellä tasolla ratkaisuni arkkitehtuuri oli seuraava:
  • Tehdasluokka abstraktien syntaksipuiden luomista varten. Tehdas voi luoda syntaksipuun esimerkiksi infix tai postfix -syötteen pohjalta.
  • Varsinainen abstraksi syntaksipuu. Kyseessä on binääripuu ja se sisältää viitteen juureensa. Lisäksi puuhun voidaan tehdä erinäisiä hakuja sekä sitä voidaan selata halutussa järjestyksessä.
  • Syntaksipuun solmu. Solmu sisältää mahdollisen viitteen vanhempaansa (juurella ei ole vanhempaa) sekä lapsiinsa (vasen ja oikea).
Pythonista löytyy luonnollisestikin toteutus abstrakteille syntaksipuille (http://docs.python.org/library/ast.html). Tuota käyttämällä olisi kuitenkin päässyt jo turhan helpolla. :)

Bonustehtävän tehtävänanto oli seuraava: "Kirjoita ohjelma, joka ottaa syötteenä aritmeettisen lausekkeen ja tuottaa ulos Javan .class-tiedoston, jossa on main-metodi, jonka suorittaminen laskee kyseisen lausekkeen arvon. Voit käyttää apuna esim. BCEL-kirjastoa.".

En ollut aiemmin käyttänyt BCELiä. Käytännössä ratkaisuni oli tyyliä "mennään sieltä mistä aita on matalin" eli periaatteessa oikein, mutta käytännössä väärin. :) Ohjelmani hoiti syötteen ottamisen ja .class-tiedoston tuottamisen oikein. Se, missä menin noin ajatuksen tasolla metsään oli lausekkeen arvon laskeminen. Hoidin laskennan jo varsinaisessa ohjelmassa hyödyntäen erästä Eval-kirjastoa (https://eval.dev.java.net/), koska en halunnut/jaksanut omaa kirjoittaa. Oikeampi ratkaisu olisi tehdä tämä hyödyntäen apuna suoraan BCELiä ja suorittaa lasku pinon avulla (tätä varten tehtävät 3. ja 4. olisi ehkä kannattanut kirjoittaa Javalla).

Mielenkiintoisena kuriositeettina ratkaistakseni bonustehtävän kokeilin ihan huvikseni Jythonia. Epäonnekseni törmäsin kuitenkin vaikeuksiin (mappaus Javan ja Pythonin funktioiden välillä ei liene aina täydellinen?), jotka ajoivat minut takaisin Javan pariin. Kuitenkin voi olla, että mikäli jälleen tarvitsee jotain Javalla tehdä, testailen sitä hieman lisää. Ainakin tällä hetkellä suhtaudun siihen varovaisen positiivisesti. :)

1 kommentti:

  1. Näin jälkikäteen ajatellen kurssin tulevissa demoissa olisi päässyt aavistuksen helpommalla, kunhan olisi heittänyt kunnon AST-toteutuksen kasaan (auttaa kummasti, kun toteuttaa kieliä). Tosin tätä varten löytynee jo paljon olemassa olevia ratkaisuja, joita kannattaisi hyödyntää.

    VastaaPoista