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
Avald
is
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