Raamatute hinnavaatluse ülesande soovitusi ja nõuandeid
Alltoodud soovitused on esialgsed, oktoobri alguse jooksul nad oluliselt täienevad: loe regulaarselt üle, leiad uusi ideid!
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.
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 muutujaid ja funktsioone, sellega väldid objektindusest tulevaid komplikatsioone, mis selles praktikumitöös ei ole vajalikud.
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.
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.
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.