Regulaaravaldised
Paari sõnetöötlusfunktsiooniga on võimalik kõik tekstidega
ettevõetavad toimingud korda saata. Kui on võimalik sõna tähtedeks jagada
ning igaühe väärtust eraldi teada saada ning muuta, siis ei jää midagi
tegemata sellepärast et pole võimalik teha. Lihtsalt mõni ülesanne võtab
rohkem aega kui teine. Korduvate olukordade tarvis saab enesele abiks
kirjutada alamprogramme, mis võivad mõnikord edaspidist tööd palju kordi
lihtsustada. Selliseks tekstianalüüsi abiks on loodud objekt RegExp. Selle
kaudu õnnestub kontrollida, kas lause vastab etteantud mallile. Samuti annab
regulaaravaldisega eraldada edaspidiseks kasutamiseks mallis ettemääratud
lauseosi. Nõnda võib suhteliselt kerge vaevaga korjata lausest välja näiteks
numbrid või suure tähega algavad sõnad. Ega vastava alamprogrammigi
kirjutamine ületamatu vaev ei tohiks olla, kuid malli abil soovitud kuju ette
andes on meil kergemini võimalik vajadusel andmete kuju muuta ilma, et
peaksime selleks kümneid ja sadu koodiridu ümber kirjutama. Universaalse
malli järele lauset kontrolliva alamprogrammi kirjutamine oleks aga küllalt
suur töö ning võime rahul olla, et meie eest on selle keegi juba ära teinud.
Sõne vastavus mallile
Järgnevas näites kontrollitakse, kas nimes sisaldub täht a. Selle
tarvis luuaks kõigepealt regulaaravaldise tüüpi objekt nimega r, kuhu antakse
sulgudes ette mall, millele vastavust edaspidi kontrollida. Kuna mall
esitatakse sõnena, siis on sellel jutumärgid ümber. Mallis nurksulgude vahel
paiknev a tähendab, et kui hiljem tahaks mõni lause avaldisele vastata, siis
peab lauses leiduma a. Kus kohal lauses ning kas üks või mitu tükki, see pole
oluline. Küll aga peab tegemist olema väikese a-ga, sest suur- ja väiketähti
eristatakse, kui just pole avaldis tõstutundetuks määratud.
Regulaaravaldis
Avaldise ehituse kirjeldamiseks on mõniteist sümbolit/kombinatsiooni,
mõned neist:
Selgitus
Avaldis
Sobiv sõne
* tähistab 0 või enam korda eelnevat tähte
jä*r
jäääär
+ tähistab 0 või enam korda eelnevat tähte
jä+r
jäääär
? tähistab 0 või 1 korda eelnevat tähte
Kat?i
Kai
Tähe korduse määramine
jä{2,4}r
jäääär
Täpselt 4 ä-d
jä{4}r
jäääär
Vähemalt 2 ä-d
jä{2,}
jäääär
Sisaldab a-tähte
a
Mati
Sisaldab teksti "ala"
ala
jalake
Sisaldab "ala" või "eri"
ala|eri
herilane
Sisaldab n-i võid t-d
[nt]
Mati
Sisaldab vähemalt üht tähte vahemikust n-v
[n-v]
Mati
Sisaldab l-i või tähe vahemikust n-v
[n-vl]
Maali
Rida algab a-ga
^a
apelsin
Rida lõpeb t-ga
t$
salat
Kaks sõna
\\w+\\s\\w+
Sinine taevas
\d ehk [0-9] tähistab numbrit, \D ehk [^0-9] tähistab mittenumbrit.
Nurksulgude sees vastab rõhumärgile ^ eitus, mujal sõna algus. \w tähistab
suvalist sõnas leiduvat tähte, \s sõnavahet. Näites on sõna tarvis kirjutatud
\\w+ . Kaks langjoont on algul, kuna Javaskriptis on \ erisümbol teiste
märkide edasiandmiseks, \\ tähistab ühte langjoont. Kuna + näitas, et vastav
täht esineb vähemalt ühe korra, siis vastabki väjendile \\w+ sõna.
Asendamine
Tekstis ühe sõne asendamiseks teisega võib kasutada RegExp'i
võimalusi. Sõne käsk replace asendab lauses avaldisele vastanud lõigu
etteantud sõnega.
Regulaaravaldis
Töö tulemuseks oli
Üks koer läks üle silla
Kui asendatavaid lõike on rohkem kui üks, siis tuleb märkida, et
asendus toimuks kogu etteantud lause ulatuses. RegExp'i loomisel tuleb
konstruktorisse kirjutada lisaks "g" (globaalne). Kui soovitakse, et avaldises
ei eristataks suur- ja väiketähti, siis tuleks märkida parameetriks "gi"
(i=ignore case).
Regulaaravaldis
Koodilõik väljastas
Üks koer läks üle silla, see koer oli tume.
Jäänuks asenduses g märkimata
var r=new RegExp("kass");'
siis tulnuks asenduse tulemusel lause
Üks koer läks üle silla, see kass oli tume.
Soovides esialgset lauset põhjalikumalt ümber tõsta või sealt sootuks
üksikuid osi tarvitada, siis tuleb mallis meeldejäetav osa ümarsulgudesse
paigutada ning pärast asenduse juures saab dollari ja järjenumbriga vastava
lõigu kätte. Esimene sobinud lõik on $1, järgmine $2 jne. All näites jäetakse
meelde esimene sõnale järgnev number. \\w+ annab sõna, \\s sõnavahe ning
seejärel ([0-9]*) teatab et järgnevad 0 kuni mitu numbrit tuleb meelde jätta.
Regulaaravaldis
Sulgude vahel olev jäetakse meelde
Vastavuste kogumine massiivi
Järgnevale mallile "(\\d)[\\s\\w]*(\\d)" vastavad kaks ühekohalist
numbrit, mille vahel on suvaline hulk tähti või tühikuid. Näites etteantud
lause "1 ja 3" vastab sellele tingimusele täiesti. Numbrid on sulgude vahel
ning jäetakse seega meelde. Avaldise test kontrollib kõigepealt, kas etteantud
lause sobis malliga. Sobimatuse korral loobutakse analüüsist ning teatatakse
mittevastavusest kuna sel juhul nagunii ei õnnestu avaldisest soovitut kätte
saada ning parem on soovimatutest veateadetest hoiduda. RegExp'i käsk
exec võtab andmeteks parameetrina etteantud lause ning avaldises
ümarsulgudes paiknevatele märkidele vastavad sümbolid või lõigud
paigutatakse massiivi elementideks. Kõige esimesele kohale
(järjekorranumbriga 0) pannakse esialgsest lausest avaldisele vastanud lõik
(praegusel juhul kogu lause). Edaspidi iga elemendi väärtuseks saab
järgemööda ümarsulgudes paiknevatele malliosadele vastavad väärtused.
Regulaaravaldis
Sulgude vahel olev jäetakse meelde
Töö tulemus:
Sulgude vahel olev jäetakse meelde
Massiivis 3 elementi
0. 1 ja 3
1. 1
2. 3
Lauses võib avaldisele vastavaid lõike leiduda rohkem kui 1. Kui
soovida ka tagumistest vastavustest omale vajalikke lõike välja valida, siis
tuleb avaldis globaalseks märkida ning sealt sama lause kohta mitmel korral
vastavusi küsida. Avaldise väli lastIndex näitab, kui kaugele (mitmenda
märgini) lauset analüüsides jõuti. Järgmise analüüsi (exec) korral jätkatakse
uurimist sellest kohast edasi.
Klassi RegExp väljad leftContext ning rightContext näitavad,
milline osa algsest lausest jäi seekordsel uurimisel vaatluse alt välja. Kuna
siin määratakse, et avaldisele vastav osa peab algama numbriga, siis jääb
algul olev sõna "numbrid" uuringu alt välja ning omistatakse muutujale
leftContext. Kuna esimene vastavus lõpeb numbriga 3, siis kogu ülejäänud
tekst "ning 2 ja 4 meeldivad" jääb uuringust välja ning omistatakse muutujale
rightContext. Uue uuringu puhul näitab lastIndex kohta kust alustada ning
selleks tulebki parasjagu kogu tekst, mis eelmisel korral paremalt poolt välja
jäi. Nüüd algab mallile sobiv koht numbriga 2 ning lõpeb numbriga 4, see
jääb vaskult välja (ja omistatakse muutujale leftContext) sõna "ning" ja
paremale jääb vaatamata "meeldivad" ning sellest kohast muutuja lastIndex
kaasabil alustatakse järgmisel korral vastavuse otsimist. Siis enam avaldisele
vastavat lõiku algsest lausest ei leita, sest sõna "meeldivad" ei sisalda
numbreid. Uuringu (exec) väljastatavale massiivile antakse tühiväärtus null
ning nüüd on juba programmeerija mure hoolitseda, et olematust kohast
väärtusi ei hakataks võtma.
Regulaaravaldis
Numbripaaride eraldamine lausest
Töö tulemus:
Numbripaaride eraldamine lausest
Sisend: numbrid 1 ja 3 ning 2 ja 4 meeldivad.
Võrdluskujund: (\d)[\sa-zõäöü]*(\d)
Tõstutundetu: false
Globaalne otsing: true
Algus: 8
Ots: 14
Vasakult eraldi: numbrid
Paremalt eraldi: ning 2 ja 4 meeldivad.
Viimati leiti: 1 ja 3
Viimane kujund: 3
Massiivis 3 elementi:
0. 1 ja 3
1. 1
2. 3
Algus: 20
Ots: 26
Vasakult eraldi: ning
Paremalt eraldi: meeldivad.
Viimati leiti: 2 ja 4
Viimane kujund: 4
Massiivis 3 elementi:
0. 2 ja 4
1. 2
2. 4
Isendid
Andmetega hõlpsamalt toime tulemiseks neid grupeeritakse. Kui on
palju sarnaseid andmeid nagu laste nimesid või autode kiirusi, sellisel juhul
võib need panna massiivi ning sealt järjekorranumbri abil kätte saada. Kui
aga on vaja korraga vaja meeles pidada nii laste nimesid kui sünniaastaid,
sellisel juhul läheb nendega toimimine keerukamaks. On täiesti võimalik luua
nii nimede kui sünniaegade tarvis eraldi massiivid ning hoolitseda, et näiteks
viiendale vanusele vastaks viies sünniaeg. Kui soovida mõnda last nimekirja
lisada või sealt kõrvaldada, siis tuleb see muutus lihtsalt mõlemas massiivis
teha. Tunnuste lisandudes tuleb sellisel juhul ka massiive juurde.
Teiseks võimaluseks on ühe isendi (lapse) kohta käivad andmed
ühisesse kesta koguda. Sellisel juhul ei teki ohtu, et väikese eksimuse tõttu
võiksid nimi ning sünniaeg teineteisest lahku minna. Samuti on ühe lapse
ümber paigutamine kergem. Lihtsaim võimalus andmeid ühte kesta paigutada
on allpool. Kokku soovin paigutada andmed palli kohta: mustri ning
raadiuse. Et edaspidi saaksin nende talletatud andmete poole pöörduda,
selleks loon muutuja p1. Kui soovin loodud isendi ühe välja (näiteks
raadiuse) andmeid pärida, siis kirjutan esiteks muutuja nime, välja nime ning
nende vahele panen punkti. Kui tahan raadiuse väärtust muuta, siis võiksin
kirjutada p1.raadius=27.
Isendid
Alamprogramm isendi loomiseks
Eeltoodud kirjaviis on hea, kui mul on vaja teha üks või paar
isendit. Kui aga on neid vaja luua hulgem, siis tuleks teha alamprogramm
isendite loomiseks. See aitab hoolitseda, et isendid tuleksid samade
väljadega, mitte et kusagil kirjutan raadiuse kogemata ühe a-ga. Selliselt on
võimalik isendi loomisel tema väljade väärtusi kontrollida või välja arvutada,
samuti võib edaspidi niimoodi hakata ühe isendi põhjal teisi koostama.
Siin tehakse alamprogramm palli andmeid sisaldava isendi
loomiseks. Funktsiooni sees this'i külge pandud muutujad jäävad edaspidi
selle isendi omadeks, mis selle alamprogrammi abil loodi.
Isendid
Omaduste pärimine
Isendi loomisel võib aluseks võtta teise isendi ning oma tüübi juures
hakata siis sellele tunnuseid lisama. Käsklus Pall2.prototype=new Pall1();
tähendab, et Pall2 abil isendite loomisel võetakse aluseks Pall1 eksemplar
ning hakatakse selle koopiale uusi tunnuseid lisama. Alati ei pea lisama, neid
võib ka muuta või lihtsalt samaks jätta. Sellisel juhul on teine objekt esimese
koopia.
Koopia puhul on mõlema isendi väljade väärtused võrdsed. Lisaks on
olemas ka selline võimalus, et kaks muutujat osutavad ühele isendile. Sellisel
juhul on isendi väljade jaoks mälus vaid üks koht, kus neid hoitakse. Mõlema
muutuja (ehk osuti) kaudu loetakse neid ühest kohast ning kui ühe muutuja
kaudu raadiust muuta, siis selgub, et ka teise muutuja poolt vaadatav raadius
on oma väärtust vahetanud. Selline olukord tekiks näiteks juhul, kui
kirjutaksin
a=new Pall1();
b=a;
Kui nüüd määran, et
b.raadius=20;
siis käsuga
alert(a.raadius);
väljastataks mulle samuti 20, ehkki algul sai a oma raadiuse väärtuseks 32,
nii kui talle loomisel anti. Järgnevas näites aga kasutatakse Pall2 loomisel
Pall1 koopiat. See tähendab, et iga uue isendi tegemisel funktsioon Pall2 abil
kõigepealt luuaks mälus uus ruum, kuhu kopeeritakse Pall1 isendi väljade
andmed ning alles seejärel hakatakse sinna lisama välju ja väärtusi, mis
Pall2-le juurde tulevad. Sellisel juhul, kui ma Pall2 väljade väärtusi muudan,
ei mõju see kuidagi sellele objektile, mille põhjal ta on tehtud.
Isendid
Lisaks andmetele oskused
Pikka aega kasutati sellist kapseldamist vaid andmete
kompaktsemaks säilitamiseks ning töötlemise lihtsustamiseks. Siis aga
avastati, et isenditest võib enam kasu olla, kui talle lisaks andmetele veel ka
oskusi ehk alamprogramme külge panna. Kui isendid nõndamoodi targaks
teha, siis saab selle arvel ülejäänud programmi lihtsamaks teha. Sisuliselt on
võimalik kusagil isenditele mõeldud alamprogrammid valmis teha, neid
hoida ja vajadusel kasutada, kuid tunduvalt mugavam on ütelda aknale, et ta
end kinni paneks, kui et hakata selleks vastavat alamprogrammide teeki
otsima, kust siis soovitud alamprogrammile akna muutuja ette andes selle
ristküliku samuti saaks ekraanilt ära koristada. All näites lisatakse Pall1-he
loomisel alamprogramm raadiuse teatamiseks. Niimoodi võin paluda loodud
isendil raadius teatada, ilma et peaksin ise hoolitsema, kuidas see käiks.
Alamprogramm tuleb kõigepealt valmis teha ning siis saab määrata, millise
nimega see isendi külge panna. Nimed võivad kokku langeda, kuid võivad ka
erineda.
Isendid
Kui alamprogrammiga (objektorienteeritud terminoloogias meetodiga) isend
on teiste isendite loomisel prototüübiks, siis saavad need uued ka selle
meetodi omale kaasa. Siin kuigi Pall2 loomisel talle ühtegi meetodit külge ei
panda, võtab ta kaasa kõik oma prototüübi omadused ja oskused, koos sellega
ka meetodi raadiuse teatamiseks. Nii saangi loodud Pall2-lt raadiuse teatamist
küsida.
Isendid
Alamprogramme saab üksteise seest välja kutsuda. Meetod
tutvustaEnnast kutsub teataRaadius’e. Algses kontekstis oleks tegemist
veaga, sest kui tutvustaEnnast käima tõmmata, siis pole tal seda
teataRaadius’t kusagilt võtta. Kui aga nad pärast isendite loomist on oma
kohtadele asetunud, siis asi toimib. Kõigepealt paigutatakse tutvustaRaadius
Pall1-te teataRaadius’e nime all. Kui nüüd Pall1 isend määratakse Pall2-he
prototüübiks, siis loodavale Pall2-le tuleb teataRaadius automaatselt kaasa.
Pall2 meetodiks tutvusta võetakse tutvustaEnnast kood. Kui see käima
pannakse ning sealjuures selle sees teataRaadius välja kutsutakse, siis on see
täiesti võimalik, sest Pall2 isendi jaoks this.teataRaadius on täiesti olemas.
See on tema enda teataRaadius, mille ta on prototüübiks võetud Pall1 kaudu
saanud. Nii kutsutaksegi välja teataRaadius, mille sisuks tegelikult on ennist
loodud tutvustaRaadius ning mis oma töö tulemusena teatab, et raadius on
32.
All on väljakutse läinud lühemaks. Kui ennist kirjutasin
p2=new Pall2();
p2.teataRaadius();
siis kõigepealt loodi Pall2 isend ning temale juurdepääsuks muutuja p2.
Järgmise käsuga pöörduti loodud isendi poole muutuja kaudu ning paluti sel
pallil oma raadius teatada. Kui aga kirjutan
new Pall2().tutvusta();
, siis tähendab see, et funktsiooni Pall2 abil luuakse uus isend. Funktsioonist
väljunud osuti (ehk isendi mälus paiknemise andmete) abil saadetakse sinna
teade et käivitatagu meetod tutvusta. Et selle kood on sama, mis meetodil
tutvustaEnnast, siis näeme andmeid nii raadiuse, mustri kui tausta kohta.
Isendid
Prototüübi abil võib oskusi juurde and ka juba olemasolevatele
tüüpidele. Kui prototüübile lisada funktsioon, siis edaspidi vastavat tüüpi
isenditel on selline oskus juba sünnist saati kaasas olemas. Siin tehakse
funktsioon valjastaLoeteluna, mis eeldab, et ta on haagitud massiivi külge
ning saab küsida nii iseenese juurde kuuluvaid elemente kui nende üldarvu.
Enese vahendite juurde pöördumiseks on võtmesõna this. Algselt luuakse
funktsioon valjastaLoeteluna ning hiljem määratakse samad käsud massiivi
prototüübi külge sõnaga valjasta, mille nime alt siis edaspidi saab
konkreetsete loodud massiivide juures seda välja kutsuda. Alamprogramm
väljastab massiivi elemendid nummerdamata loeteluna.
Lisaoskustega massiiv
Lisaoskustega massiiv
Ülekate
Olgugi, et Pall2-le saabub teataRaadius Pall1 kaudu ning esimene ei
peaks selle oskuse üle muret tundma ja võiks rahus omandatud oskusega
piirduda, ei pruugi sellest talle piisata. Sellisel juhul võib Pall2 vastava
oskuse omal uuesti määrata ning siis ei kutsuta välja enam endist
tutvustaRaadius’t vaid hoopis palju suursugusem tutvustaRaadius2. Siin on ta
vaid näite pärast, kuidas saab meetodit üle katta (niisugune on termin selle
kohta), kuid objektorienteeritud andmete puhul on selline kasutamine
sagedane. Nii saab näiteks massiivi kokku panna hulga inimesi esindavaid
isendeid. Igaühel on oma nimi ja palk, kuid olles pärit eri riikidest, käib
nende tulumaksu arvutamine erinevalt. Nii võib teha alul valmis
alamprogrammi lihtsalt inimese loomiseks. Seejärel kasutades inimese isendit
prototüübina, saab luua nii Eesti, Soome kui Saksamaa kodaniku. Kui edasi
iga inimese andmed nimekirjast vastava kesta järgi massiivi panna, mille
kodanik ta on, siis tulumaksu teada saamiseks ei pea enam kodakondsust
uurima vaid saab usaldada vastava isendi poolt toodud andmeid.
Isendid
Et kirjutusvaeva vähendada, on Javaskripti loodud with-lause. Selle
kasutamisel ei pea isendi omaduste kasutamiseks muutuja nime ette
kirjutama vaid intepretaator oskab ise arvestada, millise isendi tunnuseid
nüüd kasutada tuleb.
Isendid