2. kodutöö: Regulaaravaldised
Selles kodutöös tutvume Java regulaaravaldise APIga. See on üsna praktiline ülesanne ja paljud tudengid leiavad, et see on üldse kõige kasulikum kodutöö meie aines. Mina ise ei arva nii, mistõttu eksamil me sellist ülesannet ei küsi. Meil on selles aines edaspidi palju lihtsamad regulaaravaldised, kuid laiendatud regulaaravaldised on mujal väga kasulikud. Kui tahad näiteks andmeid töödelda/korrastada, siis regexid on selleks parim töövahend. Seega, kui Sulle Java programmeerimise osa siin ei meeldi, siis keskendu rohkem regulaaravaldistele (kodutöö esimene osa) ja katseta neid regex101.com keskkonnas.
Eesmärk on defineerida klass week2.TextAnalyzer
, mille ülesanne on leida etteantud tekstist inimeste nimed ja nende telefoninumbrid. Klassi kasutamine näeks välja umbes nii:
package week2; public class TextAnalyzer { public static void main(String[] args) { String input = "Mina olen Kalle Kulbok ja mu telefoninumber on 5556 4272.\n" + "Mina olen Peeter Peet ja mu telefoninumber on 5234 567.\n" + "Mari Maasikas siin, mu number on 6723 3434. Tere, olen Jaan Jubin numbriga 45631643."; TextAnalyzer ta = new TextAnalyzer(input); HashMap<String, String> phoneBook = ta.getPhoneNumbers(); System.out.println(phoneBook.get("Peeter Peet")); // peab väljastama 5234567 System.out.println(phoneBook.get("Jaan Jubin")); // peab väljastama 45631643 System.out.println(ta.anonymize()); /* peab väljastama: Mina olen <nimi> ja mu telefoninumber on <telefoninumber>. Mina olen <nimi> ja mu telefoninumber on <telefoninumber>. <nimi> siin, mu number on <telefoninumber>. Tere, olen <nimi> numbriga <telefoninumber>. */ } }
Täpsustuseks:
- Võib eeldada, et ettentud tekstis esinevad inimeste nimed ja telefoninumbrid alati paarikaupa ja nimi on alati enne telefoninumbrit.
- Inimeste nimed kujutavad endast kahte järjestikust ladina tähtedest koosnevat sõna, kus kummagi sõna esimene täht on suurtäht ja ülejäänud väikesed. Seega võib eeldada, et inimesel on ainult üks eesnimi.
- Telefoninumbrid võivad koosneda ühest numbrigrupist või kahest omavahel tühiku või sidekriipsuga eraldatud numbrigrupist. (Soovitame kõigepealt keskenduda ainult tühikuga eraldatud juhtumitele ja üritada viimasest testist jagu saada, kui kõik muidu töötab.)
- Võib eeldada, et ühe joruna antud telefoninumbris on alati 4–8 numbrit ja kahe grupina antud numbri puhul on igas grupis 3–4 numbrit. Täispunktide saamiseks peate seda formaati täpselt jälgima, näiteks peaks telefoninumbri kätte saama lausest: "Juhan Juhanson on 72 aastat vana ja tema number on 737-8109".
- Võib eeldada, et iga nimi (st. eesnimi + perenimi) esineb vaid ühel korral, st. igale inimesele vastab vaid üks telefoninumber.
- NB! Selle kasutusnäide (ja testide põhjal) tuleb välja mõelda, kuidas telefoninumbrit korrastatakse!
Kui ülesanne vajab täpsustamist, siis küsige julgelt fleep-is. Testide komplekt on üsna korralik, aga testimine on ainult esimene samm. Peaksite ise olema veendunud, miks lahendus on korrektne iga võimaliku sisendi korral.
Soojendusülesanded (need moodustavad osa kodutöö hindest!)
Kuna me tahaks, et kasutaksite lahendamiseks mõistlikult regulaaravaldisi, siis on ülesande lahendamiseks kõigepealt vaja mõned regulaaravaldised kirja panna. Neist viimaseid võiks ka põhikodutöö lahendamisel kasutada. Kirjuta klassi alguses olevate staatiliste muutujate väärtusteks sellised regulaaravaldised, millega sobituvad täpselt need sõned, mis on ülesande juures kirjeldatud.
- RE1: sõne lõppeb tähtedega "ed" või need tähed esinevad eelviimases positsioonis, näiteks "Ted", "edx" ja "need!".
- RE2: Paaritu pikkusega sõned!
- RE3: Sõned, mille esimene ja viimane sümbol on samad. Selleks tuleb siis kasutada tagasiviiteid! Mõtle ka erijuhtudele.
- NAME: See peaks sobituma üleval välja toodud lihtsate inimeste nimede tingimustele ehk ainult Eesnimi ja Perekonnanimi, nagu Vesal Vojdani, kes on väga lihtne inimene.
- NUMBER: Meie telefoninumbrid. Selleks on kaks testi, millest üks natuke lihtsamal kujul, kus sobivad kõik numbrijadad, mis võivad sisaldada ühe korra sidekriipsu, aga täpsem regex peaks siis tuvastama ainult õige pikkusega jupid.
NB! Siin mõtleme sobitumise all Java matcheri find meetodi rakendamist: see sobitub alamsõnedega, mistõttu tuleb kasutada õigetes kohtades sõne alguse ja lõpu erisümboleid.
Tingimused
- Lahenduse esitamine: https://moodle.ut.ee/mod/vpl/view.php?id=230002.
- Automaatse testimissüsteemi tõttu on oluline, et klassinimi ja implementeeritud meetodite signatuurid oleksid sellised nagu näidetes viidatud.
- Kui esitatavad java failid sisaldavad täpitähti (või muid ASCII'sse mittekuuluvaid sümboleid), siis peavad need olema UTF-8 kodeeringus.
- Eclipse'is on soovitatav määrata failide kodeering korraga tervele projektile (paremklõps projektil -> Properties -> Resource : Text file encoding) või lausa tervele workspace-ile (Window -> Preferences -> General -> Workspace : Text file encoding).
- Oma koodi testides ei ole Eclipse'is vaja kodeeringu pärast muretseda. Käsureal kompileerides võib olla vajalik kodeering ette anda, nt:
javac -encoding UTF-8 MinuKlass.java
. Juba kompileeritud *.class failide käivitamisel võib jälle kodeeringu teema unustada, sest *.class failid ei ole tekstifailid.
Probleemid globaalsete muutujatega
TL;DR
Väldi globaalsete muutujate kasutamist!
Probleem
Eelmistel aastatel on olnud tihti probleeme, kus automaattest saab tudengite funktsioone testides teistsugused tulemused, kui nad ise enda arvutis samade argumentidega testides. Kõigil juhtudel oli kahe silma vahele jäänud see, et lisaks argumentide väärtustele sõltus nende funktsioonide töö veel klassiväljadest e. teisisõnu globaalsetest muutujatest. Lahenduse skeem oli midagi sellist
class Jama { private static DataStructure data = new DataStructure(); public static int getStuff(...) { ... data.update(...); // või data = new DataStructure(); ... return ...; } }
Ilmselt olid nad testinud oma funktsiooni vaid ühe korra ühe käivituskorra kohta ja seetõttu oligi igal korral funktsiooni käivituskeskkond sama ja probleem ei tulnud ilmsiks. Automaattest käivitas aga sama funktsiooni mitu korda järjest (erinevate argumentidega) ning eelmise käivituse jääk jäi järgmist käivitust segama.
Kui vähegi võimalik, katsuge oma funktsioonid kirjutada nii, et kogu vajaminev info tuleb funktsiooni parameetritest, vastus antakse välja tagastusväärtusena ja funktsiooni töö ei sõltu välistest muutujatest. Mõnikord on selle nimel vaja veidi võimelda, aga enamasti tasub selline võimlemine ära.
Neil, keda mul praegu ei õnnestunud veenda globaalseid muutujaid vältima panna, soovitan ma seda siiski mõnda aega kasvõi trenni mõttes teha -- midagi halba ei saa ju sellest juhtuda. Loodetavasti näete varsti, et ilma on tõepoolest parem. Otsige ka internetist selle kohta arvamusi -- nt. see leht tundub täitsa asjalik http://programmers.stackexchange.com/questions/148108/why-is-global-state-so-evil