Raamatute hinnavaatluse ülesande soovitusi ja nõuandeid
Alltoodud soovitused on esialgsed, oktoobri alguse jooksul nad oluliselt täienevad: loe regulaarselt üle, leiad uusi ideid!
Sisukord
Kuidas andmeid hoida?
Hoia nii google base xml-st saadud olulisi andmeid (pealkiri, hind) kui panga csv-st saadud valuutakursse (valuuta, kurss) kahemõõtmeliste stringimassiividena. Rehkendamise ajal teisenda numbrid jooksvalt stringist numbriks.
NB! Toore XML-stringi või csv-stringi massiivis hoidmine ei ole mõttekas tegu. Massiivi pane ikka väljaeraldatud jupid neist stringidest.
Kuidas pihta hakata ja mis järjekorras programmeerida?
Põhimõte:
Hakka kirjutama valmis üksikuid juppe, alates lihtsamatest. Kui üks on valmis, testi ta läbi ja mine siis järgmise kallale. Testimiseks kirjuta trükkimisi vahele, hiljem kommenteeri mittevajalikud trükid välja või kustuta ära. Kirjuta suuremad osad eraldi funktsioonidena. Algul kasuta maini juppide testimiseks. Alles lõpuks pane kõik main-i sees kenasti kokku. Lõplik main on suhteliselt väike funktsioon, sest ta kutsub järjest välja põhioperatsioone tegevaid funktsioone, mis varem kirjutasid.
Väldi algul mistahes keerukusi ja lisavõimaluste tegemist! Ehita kõik algul valmis nii lihtsalt, kui saad, hiljem saad soovi korral koodi täiustada, kui aega jääb.
Soovitaksin teha tükid valmis näiteks sellises järjekorras:
- Tõmba endale üks näite-XML failina google basest ja näite-CSV panga valuutakurssidega, ning kasuta progemisel algul sisendina neid faile, mitte aga võrguühendust! See on mugavam ja kiirem. Võrguühenduse pidev kasutamine lisa siis, kui enamus asju failide peal juba funkavad.
- Programmeeri kõigepealt valmis valuutakursside csv faili massiivi lugemise funktsioon (tingimata tee selleks eraldi funktsioon!), katseta, et käiks ok.
- Seejärel programmeeri valmis google base XML-st massiivi lugemise funktsioon. Katseta, et käiks ok.
- Seejärel programmeeri valmis gbase massiivist csv tegemine nii, et hindadega esialgu ei tegelda, küll aga loetakse kokku raamatud. Katseta, et käiks ok.
- Seejärel lisa hindadega tegelemine.
Mis juhtub, kui sa kõiki asju ei oska või ei jõua teha? Kui mingi mõistlik hulk komponente on tehtud ja töötavad, saad oma ülesande siiski arvestatud, ainult et väiksema arvu punktidega (kui praktikumijuhendaja hindab, et tehtud on pool tööd, siis saadki 5 punkti). Kui praktikumijuhendajale tundub, et tehtud on ikka väga vähe (kolmandik või vähem) siis ta üldjuhul seda ülesannet ei arvesta.
Võrgust lugemine
Kõigepealt soovitan teha eraldi katse selle http://java.sun.com/docs/books/tutorial/networking/urls/readingURL.html näiteprogrammiga.
Kui ta ei suuda võrgust lugeda, siis on tõenäoline põhjus selles, et Sinu arvuti ei saa otse internetti ühendust, vaid peab töötama läbi nn proxy.
NB! On võimalik (kuid mitte kindel: proovi järgi!) et kõik AK arvutiklasside arvutid peavad töötama läbi proxy. Siis viidatud näiteprogramm arvutiklassides võrgust lugeda otse ei suuda. Loe TTÜ AK proxy kohta: mida TTÜ AK arvutiklassides töötades vaja teha, KUI proxy osutub vajalikuks.
Tõenäoliselt aga Sinu koduarvutis või kusagil mujal arvutis sellist piirangut ei ole ja näiteprogramm suudab otse võrgust lugeda.
Kui sa ülaltoodud näiteprogrammi järgi url-i avamise ja sealt lugemise oma programmi sisse paned, siis tuleb nende operatsioonide ümber tingimata panna try { ... } catch (..) { ... }, täpselt nagu failioperatsioonidegi ümber.
Kui oled ülaltoodud programmi proovinud, proovi siis ka sarnast programmi GBaseExample.java, mis teeb samamoodi päringu juba otse Google base.
Google base stringist data kättesaamine
Google Base annab vastuseks XML stringi (alternatiivina on võimalik küsida json formaadis faili, aga see ei tee siin töös elu lihtsamaks: kasuta kindlasti XML formaati). XML stringi saab lammutada tükkideks XML parseriga (neid on palju ja erinevaid), mis on hea mõte keerukamatel juhtudel, kuid kogemuse puudumisel ei ole see väga lihtne. Siin praktikumis ära kasuta XML parserit!
Selle asemel otsi stringist lihtsalt stringiotsinguga järjest vajalikke tükke ja salvesta neid massiivi. Võimalik meetod on näiteks selline:
- Otsi järjest tage kujul <entry>.
- Kui oled sellise leidnud, otsi edasi tagi <title type='text'>. Kohe peale seda algab pealkirja-string, ning lõpeb, kui näed esimest < tähte.
- Seejärel otsi samamoodi tagi <g:price type='floatUnit'>. Kohe peale seda algab hind, mis lõpeb, kui näed esimest tähte <.
- Seejärel mine uut <entry> tagi otsima, kuni rohkem ei leia.
Soovitan algul mitte leitud stringitükke kohe massiivi panna, vaid algul nad lihtsalt välja trükkida ja kontrollida, et kõik ok.
Kursside faili/stringi lõhkumine eraldaja järgi
Sinu programm peab lammutama kursside faili stringi tükkideks.
Kuidas seda paremini teha?
Esiteks, lammutamise tulemuseks tasub anda stringimassiivi, kus elementideks on järjest vajalikud stringid. Hea on, kui see stringimassiiv teha kohe õige pikkusega, siis saab mh lihtsamalt kontrollida, kui palju elemente temas on (massiiv.length annab massiivi pikkuse).
Valuutakursside rea lõhkumist saab teha eriti lihtsalt: saab eeldada, et reas on täpselt üks eraldaja, milleks on semikoolon.
Teiseks, üldiselt on mõistlik on kirjutada funktsioon, mis võtab ette lammutatava stringijupi, eraldaja-stringi (koma või semikooloni) ja annab tagasi stringimassiivi.
Stringi lammutamiseks on palju meetodeid, võid uurid näiteprogrammi splitproov.java ja kasutada seal olevaid ideid. NB! Selleks, et selle programmi lõike kasutada, on vaja, et sa temast aru saaksid. Vaata kindlasti ka massiivide kasutamise näiteprogrammi massiivproov.java. Samuti on see programm valuutakursside jaoks liigagi võimas: ta võimaldab lõhkuda stringe, milles võib olla kuitahes palju eraldajaid (valuutakurssidel teadupärast ainult üks).
Muid soovitusi
Muutujate ja funktsioonide tüübid
Kasuta alati public static funktsioone ja globaalseid muutujaid, sellega väldid objektindusest tulevaid komplikatsioone, mis selles praktikumitöös ei ole vajalikud. Lokaalsetel muutujatel funktsiooni sees ei ole public static aga enam vajalik ega kohane.
Globaalsed muutujad
Päris mõistlik on kasutada mingit hulka globaalseid muutujaid ja massiive, näiteks:
- gbdata: kahemõõtmeline massiiv, mis sisaldab google basest loetud infot
- forexdata: kahemõõtmeline massiiv, mis sisaldab valuutakursse
Funktsioonid võivad vajadusel vabalt otse nende globaalsete muutujatega manipuleerida.
Kasulikud stringioperatsioonid
Stringi seest juppide otsimiseks, stringi teisendamiseks jne on mugav kasutada Javas olemasolevaid stringioperatsioone.
Täieliku ülevaate saad näiteks siit: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/String.html
Eriti kasulikud operatsioonid on indexOf (alamstringi otsimiseks), substring, replace (viimane töötab ainult üksikute sümbolite asendamiseks!), replaceAll (tervete alamstringide asendamiseks),
NB! Loe kindlasti järgmist lõiku stringide võrdlemise kohta.
Stringide võrdlemine
NB! Kui x ja y on stringid, siis võrdlemine
x==y
ei anna oodatud tulemust! See võrdlus ainult kontrollib, kas x ja y on võrdsed pointerid, st viitavad samale stringile mälus. Aga, kui teeme nii:
String x="ab"; String y="ab"; if (x==y) ...
siis x ja y on erinevad pointerid (kuigi stringide sisud on samad!) ja neid ei peeta võrdseks.
Selleks, et kontrollida, kas stringide sisud on samad, on vaja kasutada sellist spetsiaalset stringivõrdlust:
x.equals(y)
(selle funktsiooni ja muud head stringifunktsioonid leiab nii Ecki õpikust kui ametliku manuaali lingilt http://java.sun.com/j2se/1.4.2/docs/api/java/lang/String.html)
Võrdlemaks, kas string x on suurem kui string y (vajalik sorteerimisel), kasuta järgmist funktsiooni:
x.compareTo(y)
Lisainfot (mitte väga olulist) leiad eelnevalt viidatud linkidest.
Mida compareTo välja annab? int-i. Mida see int näitab? Katseta ise: võrdle paari stringi (pane katse main funktsiooni) ja trüki tulemus-int välja.
Stringi teisendamine arvuks
Stringi (näiteks "21.53") mis kujutab endast arvu, saab arvuks teisendada järgmiste funktsioonide abil (iga liht-arvutüübi jaoks oma):
byte b = Byte.parseByte("123"); short s = Short.parseShort("123"); int i = Integer.parseInt("123"); long l = Long.parseLong("123"); float f = Float.parseFloat("123.4"); double d = Double.parseDouble("123.4e10");
NB! Kui teisendatav string ei ole tegelikult arv, viskavad need funktsioonid NumberFormatException vea. Seepärast on hea nende ümber panna try...catch, näiteks nii:
try { double d = Double.parseDouble("123.4"); } catch (NumberFormatException e) { System.out.println("Ei onnestunud stringi arvuks teisendada!"); System.exit(1); }
Selles praksis on vaja teisendada valuutakursse ja hindu arvudeks. Kui seejuures tekib viga, ei ole hea mõte programm seisma panna! Selle asemel peaks sinu programm vastavat rida (kurssi või raamatu-infot) pigem ignoreerima, st seda justkui polekski loetud. Seega pead catch osa sisse panema mingi muu tegevuse, kui veatrüki ja programmi seismapaneku!
NB! Kuna eesti panga valuutakursid tulevad tõenäoliselt arvudena, mis sisaldavad koma, mitte punkti, siis nendest arvudest teisendaja aru ei saa. Vaja on enne teisendamist koma punktiga asendada.
Kuidas teha õige suurusega massiiv, kui näiteks stringist või failist andmeid loed?
Failist lugemise kohta vaata kindlasti ka näiteprogrammi ioproov.java
Probleem selles, et meil on vaja panna stringist/failist loetud stringijupid massiivi, aga me ei tea ette, kui palju neid tuleb. Seega, kui suur massiiv tuleks teha?
Siin on, nagu alati mitu lahendust.
Kõige primitiivsem lahendus on kohe teha hästi suur massiiv, ja kui data sinna ei mahu, anda lihtsalt viga. Selles praktikumiülesandes datat väga palju lugema ei pea, seega see lahendus võiks töötada, kuid ta on ebaelegantne ja raskelt laiendatav.
Lihtne ja ebaefektiivne universaal-lahendus on kõigepealt lugeda data välja, aga mitte massiivi panna: selle asemel lugeda kokku ridade arv.
Siis teeme õige suurusega massiivi ja loeme seejärel uuesti kõik read, seekord pannes nad massiivi sisse.
Elegantsem lahendus on vältida mitmekordset lugemist. Aga: kui sa endas väga kindel ei ole, soovitan algul realiseerida lihtsama lahenduse, mida äsja kirjeldasin.
Elegantsema lahenduse saab kirjutada näiteks nii:
Teeme esialgse, ajutise massiivi näiteks suurusega 100 rida (ja õige arv tulpasid). Hakkame sinna lugema. Kui datal mahtus ära, teeme uue massiivi, seekord õige (lühema) pikkusega, ja kopeerime esialgse massiivi vajalikud read sinna massiivi. Vana massiiv visatakse mõne aja pärast prahikoristaja poolt automaatselt ära.
Kui data ei mahtunud esialgsesse massiivi, teeme uue ajutise massiivi, seekord näiteks 2 korda suurema kui eelmine. Kopeerime vanast ajutisest kõik uude ajutisse ja jätkame data uude massiivi lugemist. Võib juhtuda, et nüüd mahub ära. Siis lõpuks teeme õige pikkusega (lühema) massiivi.
Kui ikka ei mahtunud, kordame protseduuri: teeme jälle uue, viimasest massiivist jällegi kaks korda suurema ajutise masssiivi. Jne jne.
NB! Selliselt ajutisi massiive tehes pane tähele, et kuna tulpade arv on neis alati õige, ei ole tegelikult vaja kopeerida vanast massiivist uude üksikute stringide kaupa (st topelttsükliga). Kahemõõtmeline massiiv on ju massiiv mitmest ühemõõtmelisest! Kopeerimise käigus saame kopeerida korraga terve rea (käsitledes massiive kui ühemõõtmelisi) ja see on palju kiirem meetod. Siin on veel võimalik teha mitu täiendavat optimeeringut, aga liiale ei ole optimeerimis-pingutustega ka mõtet minna.
Kuidas koodi kommenteerida
Üldpõhimõte: funktsioone/kohti koodis, mis ei ole kohe pealevaatamisel arusaadavad, tasub kommenteerida. Liiga palju (või liiga pikki) kommentaare, vastupidi, ei ole mõtet panna: see hoopis häiriks koodist kiiret arusaamist. Reeglina kommenteeritakse funktsioone (kirjutades funktsiooni ette kommentaari) pluss lisatakse keerukamatesse kohtadesse funktsioonides lühikesi kommentaare.
Funktsioonidele kommentaari lisamisel (funktsiooni ette) on lisaks hea mõte kasutada javadoci konventsioone: vt http://math.hws.edu/javanotes/c4/s5.html peatüki lõpuosa. Sel juhul saab pärast kasutada javadoc programmi (olemas java toolkitis sealsamas kataloogis, kus javac), mis automaatselt genereerib sellistest kommentaaridest veeebilehe kujul dokumentatsiooni. Lisaks saavad mitmed javale orienteeritud redaktorid (näiteks Eclipse) sellistest kommentaaridest aru.
URL-ide tegutsemine
Selle praktikumi jaoks piisavad URL-ist lugemise õpetused ja näited on kirjas varasemas (pluss näitekood).
Kui tahad aga huvi pärast täielikku ülevaadet URL-i kasutamisest, tasub lugeda Suni java tutoriali vastavat peatükki: http://java.sun.com/docs/books/tutorial/networking/index.html