-
1
PROGRAMAVIMO KALBŲ TEORINIAI PAGRINDAI
Mokymo priemon÷ bakalauro studijų programos
„Matematikos ir informatikos mokymas“ studentams
Valentina Dagien÷
Gintautas Grigas
Vilniaus universitetas Matematikos ir informatikos
fakultetas
Matematikos ir informatikos metodikos katedra
El. paštas: [email protected]
[email protected]
Vilnius, 2007
-
2
Mokymo priemon÷je pateikiamos pagrindin÷s procedūrinio
programavimo konstrukcijos, parodoma, kaip jos realizuojamos
programavimo kalbose. Tikslas – pad÷ti studentams susiformuoti
sistemingą požiūrį į įvairias programavimo kalbas ir geriau
orientuotis jų įvairov÷je. Akcentuojamos esmin÷s semantin÷s kalbų
struktūros, išryškinami bendri įvairių kalbų semantikos bruožai,
kurie dažnai būna užmaskuoti skirtingomis sintaksin÷mis
struktūromis.
Mokymo priemon÷ skiriama informatikos ir su ja susijusių
specialybių pedagoginių studijų studentams. Medžiaga d÷stoma taip,
kad ją paj÷gtų suprasti skaitytojas, mokantys bent vieną
procedūrinio programavimo kalbą (pvz., Paskalį, C).
Ši mokymo priemon÷ daugelį metų taikoma Matematikos ir
informatikos fakulteto bakalauro studijų programos „Matematikos ir
informatikos mokymas“ III kurso studentams d÷stant informatikos
didaktiką. Priemon÷ numatyta kurso daliai.
-
3
Įvadas
Programavimo kalbų gyvavimo amžius gana solidus – įžengta į
penktąjį dešimtmetį: Džono Bekaus (John Backus) vadovaujama grup÷
1954 metais sukūr÷ pirmąją programavimo kalbą Fortraną (Fortran) ir
jos transliatorių. Nuo to laiko iki šiol pasaulyje sukurta keli
tūkstančiai programavimo kalbų bei jų transliatorių.
Vienos kalbos paplitusios daugelyje šalių, vartojamos
įvairiausiems uždaviniams spręsti, kitos egzistuoja tik vienoje ar
kitoje šalyje, ar net vienoje įstaigoje, dar kitos – taikomos tik
specialiems, kartais net labai siauros srities uždaviniams spręsti.
Tačiau daugelis programuotojų paprastai moka tik keletą
programavimo kalbų, o dažniausiai netgi vieną arba dvi. Tuomet
natūraliai kyla klausimas, ar verta nagrin÷ti, lyginti programavimo
kalbas, jeigu daugumai programuotojų su daugeliu kalbų praktiškai
neteks susidurti.
Jei programavimo kalbas aptartume paviršutiniškai, tik
apžvelgtume jų konstrukcijų įvairovę, tai iš tokios pažinties
nebūtų didel÷s naudos – nenaudojamų kalbų smulkius ypatumus greitai
pamirštume.
Visai kas kita, jeigu į programavimo kalbas žvelgtume
sistemingai, klasifikuotume jas, aptartume pagrindinius kiekvienos
paradigmos realizavimo kelius, išanalizuotume juos tose kalbose,
kurios turi konstrukcijas su būdingiausiomis savyb÷mis. Tuomet
įgautume gilią sampratą apie programavimo kalbos sandarą,
konstrukcijų savybes ir gal÷tume tai panaudoti visiškai
nepriklausomai nuo to, kurią kalbą mokame.
Pateiksime keletą priežasčių, kad programavimo kalbų analiz÷ yra
gana svarbi kiekvienam programuotojui.
Pirmiausia tokia analiz÷ pagerina kiekvienos konkrečiai
naudojamos programavimo kalbos sampratą. Daugelis programavimo
kalbų turi įvairiausių konstrukcijų, kurios yra tikrai naudingos
tuomet, jei sugebame jas tinkamai panaudoti. Netinkamas
konstrukcijų taikymas gali padaryti algoritmą sunkiai suprantamą,
tur÷ti neigiamos įtakos algoritmo efektyvumui ar net tapti įvairių
netikslumų ir klaidų šaltiniu. Kartais net profesionalūs
programuotojai, daugelį metų kurdami programas kuria nors kalba,
gali netur÷ti gilesnio kai kurių konstrukcijų supratimo. Priežastys
paprastai glūdi tame, kad dauguma programavimo kalbų aprašų yra
pernelyg paviršutiniški, nepadedantys suprasti esm÷s.
Paprastai programuotojas žino tam tikrą kiekį programavimo
konstrukcijų, su kuriomis jis susiduria programuodamas konkrečia
programavimo kalba. Programavimo kalbų analiz÷ suteikia galimybę
gerokai išpl÷sti suvokiamų konstrukcijų aibę.
Moksliniuose darbuose, kurie nagrin÷ja žmogaus mąstymą, dažnai
sutinkama mintis, kad kalba tiek padeda mąstymui, tiek jį riboja.
Iš tikrųjų, žmogus naudoja kalbą, kad išreikštų tai ką jis galvoja.
Tačiau iš kitos pus÷s teisinga ir tai, kad kalba struktūrizuoja,
nusako žmogaus mąstymą ir netgi iki tokio laipsnio, kad žmogui
sunku mąstyti be žodžių. Vienos programavimo kalbos žinojimas
sukelia tokį patį stiprų ribojimo efektą. Spręsdamas vieną ar kitą
uždavinį ir parinkdamas jam būtinas konstrukcijas, programuotojas
neišvengiamai mąsto tik tos vienintel÷s žinomos kalbos
sąvokomis.
Susipažinus su pagrindin÷mis programavimo kalbų koncepcijomis,
žymiai aiškiau nustatyti, kuri kalba tinkamiausia konkrečios klas÷s
uždaviniams spręsti. Teisingai pasirinkta ir taisyklingai vartojama
programavimo kalba gali gerokai paspartinti programų rašymą ir
pailginti jų gyvavimo trukmę.
-
4
Dar viena svarbių programavimo kalbų teorijos mokymosi
priežasčių būtų ta, kad žinant bendruosius kalbų darybos principus
galima kur kas greičiau išmokti naujų kalbų. Vis daugiau ir daugiau
atsiranda smulkių specializuotų kalbų, kurių prireikia įvairiems
profesiniams darbams atlikti. Paprastai tos smulkios kalbos turi
bendriausias programavimo kalbų sąvokas bei konstrukcijas. Be to,
jei žinosime bendriausius programavimo kalbų principus ir
koncepcijas, lengvai suvoksime ir naujas kalbas. Dar daugiau,
programuotojas, lengvai suvokdamas kalbų esmę, gali iš karto
matyti, kaip kiekvienoje situacijoje tinkamai panaudoti naujos
kalbos konstrukcijas.
Programavimo kalbų teorijos žinojimas ne tik padeda greitai ir
kokybiškai suvokti naujas kalbas, bet ir suteikia galimybę patiems
kurti naujas kalbas. Žinoma, nedaug programuotojų ketina kurti
naujas kalbas. Tačiau daugeliui tenka kurti užduočių valdymo kalbas
arba kalbų poaibius specialiems tikslams.
Pateiksime pagrindines procedūrinio programavimo sąvokas ir
aptarsime jas realizuojančias konstrukcijas. Dažniausiai min÷sime
tokias plačiai paplitusias kalbas, kurios yra vertingos
programavimo kalbų teorijos bei algoritmavimo ir programavimo
mokymo metodikos požiūriu. Pirmiausia tai būtų Paskalis (ir jo
pagrindu sukurtos kalbos Modula-2, Oberonas-2), Algolas-68 ir Ada.
Šiek tiek užuominų bus apie istoriniu požiūriu vertingas kalbas
Fortraną ir Algolą-60, o taip pat sisteminio programavimo kalbas C,
C++, bei šiuo metu itin populiarią Javos kalbą.
Daugiausia vietos skirsime įvairių programavimo kalbų
konstrukcijų semantikos analizei. Po to trumpai pailiustruosime,
kaip tos konstrukcijos realizuojamos programavimo kalbose. Min÷dami
kalbą visada tur÷sime omeny jos standartą. Kalb÷dami apie konkrečią
kalbos realizaciją, visada ją įvardinsime.
Skaitytojo netur÷tų gąsdinti daugyb÷ čia pamin÷tų kalbų.
Medžiaga d÷stoma taip, kad ją paj÷gtų suprasti studentas, mokantis
bent vieną šiuolaikinę procedūrinę kalbą (pvz., Paskalį arba
C).
-
5
Turinys
Įvadas 3
Turinys 5
1. PROGRAMAVIMO KALBŲ KLASIFIKACIJA IR RAIDA
1.1. Programavimo kalbų klasifikacija 8
1.2. Kalbų vertinimo kriterijai 10
1.3. Programavimo kalbų raida 13
2. FORMALŪS KALBŲ APRAŠYMO METODAI
2.1. Leksika, sintaks÷, semantika 17
2.2. Kalba ir jos gramatika 18
2.3. Formalios gramatikos 22
2.4. Gramatikų klasifikacija 27
2.5. Kalbų klasifikacija 28
2.6. Kontekstin÷s kalbos 30
2.7 Laisvojo konteksto kalbos 31
2.8. Reguliariosios kalbos 32
2.9. Baigtinio automato samprata 34
2.10. Gramatikų ir kalbų vienareikšmiškumas 40
2.11. Kaip praktiškai nustatyti gramatikos tipą 46
2.12. Bekaus ir Nauro forma 48
2.13. Bekaus ir Nauro formos modifikacijos 54
2.14. Sintaks÷s diagramos 56
3. LEKSIKA
3.1. Kompiuterio ab÷c÷l÷ 62
3.2. Programavimo kalbos ab÷c÷l÷ 62
3.3. Terminalinių simbolių pavaizdavimas kompiuterio ab÷c÷l÷s
simboliais 63
3.4. Vardai 66
3.4. Konstantos 68
3.5. Programos teksto išd÷stymas ir komentarai 71
-
6
4. DUOMENYS
4.1. Duomenų klasifikacija 73
4.2. Duomenų rūšys: konstantų, kintamųjų ir rodyklių modelis
78
4.3. Konstantos ir jų vardai 83
4.4. Kintamieji, programos būsena 84
4.5. Operacijos, reiškiniai ir reikšmių priskyrimas 86
5. DUOMENŲ TIPAI IR STRUKTŪROS
5.1. Paprastieji duomenų tipai 90
5.1.1. Vardiniai tipai 90
5.1.2. Loginis tipas 92
5.1.3. Simbolių tipas 94
5.1.4. Atkarpos tipai ir potipiai 95
5.1.5. Sveikieji skaičiai 96
5.1.6. Realieji skaičiai 98
5.1.7. Trivialūs duomenų tipai 100
5.2. Rodykl÷s 101
5.3. Struktūriniai duomenų tipai 101
5.3.1. Rinkinys 103
5.3.2. Variantinis rinkinys 106
5.3.3. Masyvas 108
5.3.4. Kiti duomenų tipai 111
5.3.5. Struktūrinių duomenų tipų suderinamumas 112
5.4. Objektai 113
5.4.1. Objektinio programavimo samprata 113
5.4.2. Realizacija procedūrin÷se programavimo kalbose 119
6. VALDYMO STRUKTŪROS
6.1. Valdymo struktūrų samprata 123
6.2. Struktūrin÷s schemos 124
6.3. Sakinių seka ir sud÷tinis sakinys 129
6.4. Išrinkimas 129
6.4.1. Išrinkimo sakinio samprata 129
6.4.2. Išrinkimas iš dviejų variantų 130
-
7
6.4.3. Išrinkimas išrinkime 133
6.4.4. Variantinis išrinkimas 134
6.4.5. Tiesioginis išrinkimas 136
6.5. Kartojimas 137
6.5.1. Kartojimo sakinio samprata 137
6.5.2. Nežinomo kartojimų skaičiaus ciklas 138
6.5.3. Žinomo kartojimų skaičiaus ciklas 141
6.6. Nukreipimo sakinys 144
7. PROCEDŪROS IR FUNKCIJOS
7.1. Valdymo struktūrų abstrakcijos samprata 147
7.2. Parametrai 149
7.2.1. Formalieji ir faktiniai parametrai 149
7.2.2. Perdavimas reikšme 152
7.2.3. Perdavimas rezultatu 152
7.2.4. Perdavimas reikšme ir rezultatu 154
7.2.5. Perdavimas adresu 155
7.2.6. Perdavimas vardu 157
7.2.7. Funkcijų ir procedūrų perdavimas parametrais 161
7.2.8. Parametrų perdavimo būdai programavimo kalbose 163
8. PROGRAMAVIMO KALBŲ STANDARTAI IR REALIZACIJOS
8.1. Oficialūs kalbų aprašymai 168
8.2. Kalbų dialektai 168
8.3. Interpretatoriai, transliatoriai ir kompiliatoriai 169
8.4. Programavimo terp÷ 170
8.5. Programavimo kalbų suderinamumas 171
Literatūra 172
-
8
1. PROGRAMAVIMO KALBŲ KLASIFIKACIJA IR RAIDA
1.1. Programavimo kalbų klasifikacija
Programavimo kalbas galima klasifikuoti taikant įvairius
kriterijus: pagal jų išraiškos priemones, taikymo sritis,
naudojamus metodus, sukūrimo laikotarpį ir pan. Kiekvienas
klasifikavimo būdas išryškina tam tikras kalbos savybes.
Pirmiausia panagrin÷sime kalbų klasifikaciją pagal jų taikymo
sritis. Tai elementariausia ir dažnai pateikiama klasifikacija.
Ryškiau skiriasi keturios kalbų grup÷s, tinkamos šiems
uždaviniams:
1) mokslinių tyrimų,
2) komercinių,
3) kompiuterin÷s intelektikos,
4) sisteminio programavimo.
Mokslinių tyrimų uždaviniams būdingas didelis operacijų kiekis
su nedideliu duomenų kiekiu. Pirmieji kompiuteriai buvo skirti
tokiems uždaviniams spręsti. Tod÷l ir programavimo kalbų
projektavimas prasid÷jo nuo šių uždavinių programavimo. Pirmoji
programavimo kalba Fortranas ir buvo skirta tokiems uždaviniams
programuoti. Kuriant Algolą-60 buvo galvojama taip pat apie jos
taikymą moksliniams skaičiavimams, tačiau ši kalba paplito ir
kitose srityse.
V÷liau at÷jo eil÷ komercin÷s informacijos apdorojimui. Šios
srities uždaviniams būdingi dideli duomenų kiekiai, o operacijų su
jais nedaug ir jos nesud÷tingos. Geriausiai šiam tikslui tinkama
buvo Kobolo (Cobol) kalba [ANSI74], sukurta 1960 metais.
Šiuo metu programavimo kalbos komercinei veiklai pasidar÷
neaktualios. Atsirado kitos kompiuterin÷s priemon÷s – duomenų
baz÷s, skaičiuokl÷s, o taip pat daugyb÷ programų paketų tam
tikriems uždaviniams arba jų grup÷ms. Šios priemon÷s pakeičia
programavimo kalbų poreikį komercin÷je veikloje, nes atlieka beveik
visus reikalingus veiksmus. Bet atsirado didesnis poreikis
sisteminio programavimo kalboms, kuriomis min÷tos priemon÷s
programuojamos.
Kompiuterine intelektika domimasi nuo pat pirmųjų kompiuterio
žingsnių. Bet ir dabar nepraranda aktualumo problema, kaip
suprojektuoti žmogaus mąstymą atitinkančią sistemą.
Šios srities uždaviniams būdingas simbolin÷s informacijos
apdorojimas ir sąrašin÷s struktūros. Operuojama ryšiais tarp
objektų. Reikia kalbos, kurioje būtų patogu aprašyti sud÷tingos ir
dinamiškai besikeičiančios struktūros duomenis bei veiksmus su tais
duomenimis. Daugelis įvairių valstybių mokslininkų ieškojo būdų,
kaip tokią informaciją apdoroti. 1959 metais šiam tikslui buvo
sukurta funkcin÷ kalba Lispas (Lisp). Ir iki šiol kompiuterin÷s
intelektikos srityje tebenaudojamos kalbos, artimos Lispo kalbai.
V÷liau, 8-ajame dešimtmetyje pasirod÷ loginio programavimo kalba –
Prologas (Prolog), kuris dar geriau tiko kompiuterin÷s intelektikos
srities uždaviniams spręsti.
Min÷tų trijų grupių uždaviniuose operuojama su žmogaus veiklai
būdingais duomenimis – skaičiais, tekstais, paveikslais ir pan.
Juos kompiuteris tik apdoroja. Jie atsiranda ir naudojami
kompiuterio išor÷je. Tod÷l kompiuterio požiūriu jie yra
išoriniai.
Kartu su kompiuteriu atsirado dar viena duomenų rūšis – vidiniai
kompiuterio duomenys, egzistuojantys tik kompiuterio viduje ir
būdingi tik pačiam kompiuteriui. Tai kodai – vidinis
-
9
išorinių duomenų pavaizdavimas, jų adresai, komandų kodai ir
pan. Su šiais duomenis tenka operuoti rašant programas, tvarkančias
vidinį kompiuterio ūkį, pavyzdžiui operacines sistemas. Tai
sisteminio programavimo uždaviniai. Šios rūšies uždaviniams
programuoti skiriamos sisteminio programavimo kalbos. Šios kalbos
yra labiau susijusios su kompiuterio, dažniausiai tam tikros jo
dalies, architektūra negu kitos aukšto lygio kalbos.Tod÷l jose yra
nemažai mašininių ar asemblerinių konstrukcijų, leidžiančių atlikti
veiksmus su kompiuterio atmintimi, registrais ir pan.
Viena populiariausių sisteminių kalbų yra C. Pavyzdžiui, gerai
žinoma operacin÷ sistema UNIX buvo parašyta C kalba. C kalbą galime
laikyti tarpine tarp aukšto ir žemo lygio kalbų.
Kartais išskiriamos ir daugiau specializuotos (siauresn÷s)
uždavinių klas÷s, kurioms sudaromos siauresn÷s specializacijos
programavimo kalbos.
Programavimas n÷ra vienalytis. Tą patį uždavinį galima
programuoti įvairiais metodais. Ir atvirkščiai – įvairius, netgi
skirtingų rūšių uždavinius programuoto tuo pačiu metodu.
Programavimo pažiūriu netgi natūraliau programavimo kalbas
klasifikuoti pagal programavimo metodiką – kalboje esančias
(dominuojančias) konstrukcijas. Dažniausiai kalbos skirstomos į
keturias grup÷s pagal toje kalboje realizuotą programavimo
paradigmą (gr. paradigma – kryptis, pavyzdys):
1) imperatyvios arba procedūrin÷s,
2) funkcin÷s,
3) objektin÷s,
4) login÷s.
Imperatyvių kalbų grupei priklauso kalbos, kurios uždavinio
sprendimas išreiškiamas veiksmais – komandomis (paliepimais)
kompiuteriui vykdyti vieną ar kitą veiksmą. Tai viena seniausių,
gausiausia ir dažniausiai naudojamų kalbų grup÷. Jai priklauso
Algolas-60, Algolas-68, Ada, C, Fortranas, Modula, Paskalis ir
daugelis kitų kalbų.
Imperatyvioji paradigma geriausia derinasi su tradicine
algoritmo samprata.
Funkcinių kalbų paradigmos pavadinimas sako, kad svarbiausia
konstrukcija yra funkcija. Veiksmai išreiškiami funkcijų aprašais
ir kreipiniais į funkcijas. Funkcijos aprašas tiesiogiai
(matematiškai) apibr÷žia funkcijos rezultato priklausomybę nuo
argumentų. Grynose funkcin÷se kalbose n÷ra nei kintamojo, nei
priskyrimo sąvokų, tod÷l rezultatų priklausomyb÷ nuo pradinių
duomenų išreiškiama tiesiogiai, nenaudojant tarpinių duomenų.
Funkcin÷ kalba pateikia primityvių funkcijų aibę, galimybę
aprašin÷ti sud÷tingesnes funkcijas, panaudojant primityviąsias, o
taip pat kai kurias struktūras duomenims laikyti. Tobulai sukurta
funkcin÷ kalba turi gana daug primityvių funkcijų. Aprašant
primityvias funkcijas, dažnai naudojama rekursija.
Funkcinių kalbų pavyzdžiai: Lispas, Scheme, Miranda.
Objektin÷s kalbos atsirado tik prieš gerą dešimtį metų. Jos
išaugo iš abstrakčiųjų duomenų tipų teorijos, perimdamos duomenų
abstrakcijos mechanizmą. Pagrindin÷ duomenų struktūra – objektas.
Vieni objektai gali būti kuriami iš kitų objektų pritaikant
paveld÷jimo mechanizmą. Operacijos su duomenimis yra įmontuotos į
objektus. Taigi operacijas atlieka objektai, o jie sąveikauja
siųsdami vienas kitam pranešimus.
Pirmoji objektin÷ kalba buvo Smalltalk. Nesenai tapo populiari
Eiffell. Kalbose Oberon-2 ir Java yra gerai suderintos procedūrinio
ir objektinio programavimo paradigmos. Dabar objektinis
programavimas yra labai populiarus. Daugelis procedūrinio
programavimo kalbų (Paskalis, C) papildomos objektinio programavimo
elementais.
-
10
Loginio programavimo kalbose aprašomi faktai ir jų sąryšiai.
Vieni jų žinomi prieš atliekant programą (pradiniai duomenys), kiti
gaunami ją atlikus (rezultatai). Geriausiai žinoma šios grup÷s
kalba yra Prologas.
Kartais programavimo kalbos klasifikuojamos pagal lygius. Kuo
žemesnis lygis, tuo kalbos duomenų tipai ir operacijos yra
artimesni kompiuterio kodams ir komandoms. Kuo kalbos lygis
aukštesnis, tuo kalba abstraktesn÷, t. y. daugiau nutolusi nuo
kompiuterio komandų ir priart÷jusi prie žmogaus kalbos. Įprasta
kalbas skirstyti į tris lygius:
1) žemo,
2) aukšto,
3) labai aukšto.
Suprantame, kad žemo lygio programavimo kalbomis laikomos visos
mašinin÷s ir joms artimos kalbos.
Aukšto lygio programavimo kalboms priklauso beveik visos mums
žinomos kalbos.
Kalbų lygis nuolat aukšt÷ja. Tam, kad galima būtų išskirti
kalbas, kurių lygis dar aukštesnis už tų kalbų lygį, kurios jau
buvo vadinamos aukšto lygio kalbomis, tokios kalbos imamos vadinti
labai aukšto lygio programavimo kalbomis. Jų pavyzdžiai – login÷
kalba Prologas, funkcin÷ kalba Miranda, aib÷mis operuojanti kalba
Setlas (Setl).
1.2. Kalbų vertinimo kriterijai
Norint apibūdinti programavimo kalbas, reikia iš anksto
susitarti d÷l jų vertinimo kriterijų. Suprantama, kad gali būti
labai daug savybių, kurių tik÷tum÷m÷s iš programavimo kalbų. Reikia
atsirinkti svarbiausias, ir dar, pasirenkant konkrečią programavimo
kalbą, pagalvoti apie sritį, kurioje bus taikoma ši kalba.
Pateiksime keturias pagrindines programavimo kalbų savybes,
kurias nurodo R. W. Sebesta [Sebesta93], laikytinas kalbų vertinimo
kriterijais:
1) programų skaitomumas,
2) programų rašymo patogumas,
3) patikimumas (programų, parašytų ta kalba),
4) išlaidos (programoms kurti, programuotojams mokytis ir t.
t.).
Tai išorin÷s (galutin÷s) kalbos savyb÷s. Kiekviena jų priklauso
nuo vidinių kalbos savybių, įvardijamų specialiais programavimo
terminais ir taip pat laikytinų (vidiniais) kalbos vertinimo
kriterijais. Ryšiai tarp įvairių savybių (vertinimo kriterijų)
pateikti 1.1 paveiksle.
-
11
1.1 pav. Programavimo kalbų vertinimo kriterijai
Svarbesnius kriterijus aptarsime išsamiau.
Programų skaitomumas. Kuo lengviau ir greičiau galima suvokti
programą, tuo geriau. Kuo programa ilgiau gyvuoja, tuo svarbesnis
jos skaitomumas, nes ją ilgiau reikia prižiūr÷ti, taisyti,
papildyti, pritaikyti prie naujų sąlygų, ir, vadinasi dažniau
skaityti. Programos skaitomumas tur÷tų būti gerų programų kūr÷jų
pagrindinis devizas – programą rašo vienas žmogus, o skaito
daugelis.
Programos skaitomumas glaudžiai susijęs su jos paprastumu. Jei
programavimo kalba turi pernelyg daug konstrukcijų, ją sunku
išmokti. Tod÷l vertingesn÷s paprastesn÷s kalbos, turinčios nedaug
motyvuotai parinktų konstrukcijų. Fortrano autorius Dž. Bekus
kažkada yra pasakęs, kad būtų laimingas, jeigu jam pavyktų sukurti
programavimo kalbą, kurios aprašymas tilptų ant vieno lapo!
Kalbą daro sud÷tinga taip pat savybių persidengimas, t. y. kai
kelios konstrukcijos turi tas pačias savybes. Pavyzdžiui, vienetu
padidinti kintamojo reikšmę C kalba galima vartojant net keturis
būdus:
kiek := kiek + 1
kiek += 1
++kiek
kiek++
Kalba pasidaro paini (sud÷tinga) ir priešingu atveju – kai viena
ir ta pati konstrukcija (operacijos ženklas, žodis ir pan.) gali
būti vartojama keliose skirtingų prasmių vietose. Sakoma, kad
vienai sąvokai žym÷ti turi būti vartojamas vienas simbolis,
skirtingoms – skirtingi.
Ortogonalumas – tai galimyb÷ gauti naujas kalbos konstrukcijas
visais duotais būdais kombinuojant turimas konstrukcijas. Kuo
didesnis kalbos ortogonalumas, tuo mažiau išimčių joje. Kai kalba
ortogonali, tai iš nedidelio elementarių (pradinių) konstrukcijų su
nedaugeliu kombinacijų galime gauti visas valdymo ir duomenų
struktūras. Kiekviena kombinacija yra teis÷ta ir prasminga.
-
12
Labiausiai ortogonali kalba yra Algolas-68. Joje, pavyzdžiui
n÷ra skirtumo tarp reiškinio ir sakinio. Jų funkcijas atlieka viena
ir ta pati konstrukcija, turinti reikšmę ir tipą (kaip sakinys) ir
galinti atlikti priskyrimo veiksmą arba kreiptis į procedūrą (kaip
sakinys).
Nors Paskalio kalba yra gana moderni ir aukšto lygio, tačiau ji
n÷ra ortogonali. Vos ne kiekvienai konstrukcijai esama ribojimų.
Pavyzdžiui, cikle „while“ negalima rašyti sakinių sekos, ją turime
paversti sud÷tiniu sakiniu, o cikle „repeat“ – galima, ne visi
faktiniai parametrai gali būti perduodami procedūrai reikšme (pvz.,
reikšme negalima perduoti bylų), funkcijos reikšm÷ gali būti tik
paprastojo tipo ir t. t.
Ortogonalumas turi būti gerai subalansuotas. Kuo ortogonalesn÷
kalba, tuo paprastesn÷. Iš kitos pus÷s, pernelyg didelis
ortogonalumas teikia didelę laisvę naujoms konstrukcijoms kurti, jų
gali atsirasti per daug, kai kurių paskirtis gali pasidaryti
miglota. Taip pat sumaž÷ja kalbos pertekliškumas ir atsiranda
didesnis klaidų pavojus (sumaž÷ja programų patikimumas).
Kalbos skaitomumui didelę reikšmę turi jos konstrukcijų forma
(sintaks÷). Pamin÷tini trys formos komponentai:
1) vardų forma,
2) bazinių žodžių prasmingumas, bendrumas, grupavimas,
patogumas,
3) konstrukcijų formos ryšys su jų semantika.
Kuo labiau žodžiai ir konstrukcijos išreiškia bei primena jų
semantiką, tuo lengviau išmokti kalbą, tuo ji paprastesn÷ ir ja
parašytos programos lengviau skaitomos.
Programos rašymo patogumas susijęs su kalbos paprastumu,
ortogonalumu, abstrakcija ir išraiškingumu.
Abstrakcija – tai galimyb÷ aprašyti ir valdyti sud÷tingas
struktūras arba operacijas taip, kad galima būtų ignoruoti daugelį
smulkmenų (detalių). Tai viena esminių programavimo kalbų savybių:
jos ir kuriamos tam, kad būtų galima atsiriboti nuo smulkmenų –
techninių detalių.
Išraiškingumas siejasi su daugeliu kalbos savybių. Pavyzdžiui,
Paskalio kalbos ciklas „for“ išraiškingesnis negu ciklas
„while“.
Programos patikimumas suprantamas kiek kitaip, negu aparatūros
patikimumas. Aparatūra fiziškai d÷visi, genda. Kuo patikimesn÷
aparatūra, tuo rečiau ji genda. Vienas tos pačios aparatūros
egzempliorius esant absoliučiai toms pačioms sąlygoms gali veikti
gerai, kitas – sugesti. Programa „nedyla“. Kiekvienas jos
egzempliorius esant toms pačioms sąlygoms veikia absoliučiai
vienodai. Programos patikimumas suprantamas kaip jos geb÷jimas
teisingai veikti esant bet kokioms ir retai pasitaikančioms
sąlygoms (pradinių duomenų kombinacijoms). Tai klaidos, o ne
atsitiktiniai gedimai. Kadangi čia turima omeny retai
pasitaikančios klaidos, tai čia daroma analogija su aparatūros
gedimais ir jos patikimumu.
Sakoma, kad tam tikra kalba parašytos programos yra
patikimesn÷s, jei kalboje yra priemon÷s, padedančios geriau
numatyti ir apriboti klaidas esant neįprastoms, dažniausiai
neleistinoms pradinių duomenų kombinacijoms. Vienas iš tokių
programų patikimumą didinančių mechanizmų yra duomenų tipų
kontrol÷.
Išlaidos susijusios su programavimo kalbos projektavimu,
platinimu, priežiūra nesunkiai suvokiamos iš 1.1 paveiksle pateiktų
sąsajų.
-
13
1.3. Programavimo kalbų raida
Trumpai peržvelgsime svarbesnes programavimo kalbas jų
atsiradimo chronologine tvarka. Daugiau d÷mesio kreipsime į tai,
kuo tos kalbos prisid÷jo kuriant teorinius programavimo kalbų
pagrindus, ką jos dav÷ programavimo kalbų teoriją.
1.2 paveiksle pavaizduota aukšto lygio programavimo kalbų
genealogija ir jų tarpusavio ryšiai: kurios kalbos kurias
įtakojo.
1.2 pav. Programavimo kalbų raida
-
14
Reik÷tų pamin÷ti pirmąją programavimo kalbą Plankalkül. Ją
sukūr÷ vokiečių mokslininkas Konradas Ciuz÷ (Konrad Zuse) 1945 m.
rašydamas daktaro disertaciją. Kalba tur÷jo pačius primityviausius
duomenų tipus ir nedaug valdymo struktūrų. Kalba nebuvo realizuota
– tais laikais dar tik prad÷jo atsirasti pirmieji, labai primityvūs
kompiuteriai. Tačiau kalba tur÷jo neabejotiną svarbą: parodytas
kelias, kaip kurti programavimo kalbas. Kaip istorinę reikšmę
turintis dokumentas, jos aprašas buvo išspausdintas tik 1972 m.
[Bauer72].
Programavime dažniausiai minimi 1954 metai. Tų metų lapkritį Dž.
Bekus ir jo grup÷ paruoš÷ projektą apie matematinių formulių
transliavimo sistemą Fortraną (angl. FORTRAN = FORmula TRANslating
System). Tai buvo pirmoji realizuota aukšto lygio (ne mašinin÷)
programavimo kalba.
Nors šioje kalboje duomenų ir valdymo struktūros buvo labai
menkos, tačiau kelios pagrindin÷s programavimo kalbų teorin÷s
id÷jos buvo perteiktos. Čia buvo vartojami loginiai kintamieji –
suprasta jų reikšm÷ programavime. Buvo įgyvendinta išrinkimo
sakinio id÷ja, nors dar gana netobulai, labai neaiškiai. Apskritai
pirmosios kalbos vis dar tebebuvo labiau skiriamos kompiuteriui, o
ne žmogui.
Fortranas jau tur÷jo žinomo kartojimų skaičiaus ciklą ir
masyvą.
Fortrano kalba buvo nuolat tobulinama, kuriamos vis naujos jos
versijos. Daugiausia žinomos – Fortranas-4, Fortranas-77,
Fortranas-90.
Funkcinių kalbų krypties pradininkai yra Džonas Makkartis (John
MacCarthy) ir Marvinas Minskis (Marvin Minsky), kurie 1958 m.
sukūr÷ Lispo (Lisp) kalbą.
Lispas turi labai paprastas, tačiau tvirtai teoriškai pagrįstas
duomenų struktūras – atomus ir sąrašus. Sąrašai apibr÷žiami labai
paprastai, jų gylis nurodomas skliaustais, pavyzdžiui,
(A B C D)
(A (BC) D (E (FG)))
Skaičiavimai atliekami taikant argumentams funkcines programas
(funkcijas). Iteratyvūs procesai modeliuojami rekursiniais
kreipiniais. Tai aiškiai išreikšta funkcinio programavimo
koncepcija.
Lispas dav÷ pradžią ir simbolin÷s informacijos apdorojimui.
1958 metais dvi didel÷s tarptautin÷s organizacijos ACM (angl.
Association for Computing Machinery) ir GAMM (vok. Gesellschaft für
Angewandte Mathematik und Mechanik) ÷m÷si projektuoti tokią
programavimo kalbą, kuri būtų tinkama sud÷tingiems uždaviniams
spręsti ir būtų teoriškai pagrįsta. Metų pradžioje šios abi grup÷s,
susitikusios Ciūriche, išk÷l÷ pagrindinius tikslus:
1. Kalba turi būti kiek galima artimesn÷ standartiniams
matematikos žymenims ir ja parašytos programos turi būti lengvai
skaitomos be papildomų paaiškinimų.
2. Kalba turi būti tokia, kad ja galima būtų aprašyti
skaičiavimus, publikuojamus spaudoje – būtų lengva suprasti
juos.
3. Šios kalbos programos turi būti mechaniškai išverčiamos į
mašininius kodus.
Buvo parengtas projektas – pirmoji Algolo-58 kalba (pavadinimas
sudarytas iš angliškų žodžių ALGOrithmic Language pirmųjų raidžių).
1960 m. buvo pateikta nauja, iš esm÷s patobulinta šios kalbos
versija Algolas-60 [Naur61, Naur63].
Svarbiausios konstrukcijos, suprojektuotos Algole-60 ir v÷liau
vartojamos daugelyje programavimo kalbų, buvo šios:
1. Blokin÷ programos struktūra. Tai leido programuotojams
apriboti vardų galiojimą. Pavyzdžiui,
-
15
begin
integer suma, kiekis;
...
end;
Čia žodiniais skliaustais begin ir end nurodoma bloko pradžia ir
pabaiga; bloke aprašyti vardai suma ir kiekis galioja (kitaip
sakant, matomi) tik bloko viduje, o išor÷je jie neegzistuoja, taigi
ir nežinomi.
2. Du procedūrų parametrų perdavimo būdai: reikšme ir vardu.
3. Rekursinių procesų samprata.
4. Pusiau dinaminiai masyvai. Tai masyvai, kurių r÷žiai nusakomi
kintamaisiais ir d÷l to masyvo dydis gali būti nustatomas programos
vykdymo metu, kai tik atliekama aprašų dalis. Pavyzdžiui,
procedure įterpk (ilgis, nauja);
value ilgis;
integer ilgis, nauja;
begin
integer array [1: ilgis] sąrašas;
...
end įterpk;
Šioje programoje sąrašas yra sveikųjų skaičių masyvas, kurio
viršutinis r÷žis ilgis yra procedūros parametras. Taigi, kol
procedūra neatliekama, n÷ra žinomas. Tačiau kai masyvas jau
sukurtas, jo r÷žių pakeisti nebegalima.
Algolas-60 tur÷jo įtakos daugelio programavimo kalbų
projektavimui. V÷lesn÷s kalbos arba per÷m÷ Algolo-60 konstrukcijas,
arba jas patobulino.
Su Algolu-60 glaudžiau susijusi Bekaus ir Nauro forma (BNF) –
formalus būdas programavimo kalbos sintaksei aprašyti. Būtent,
projektuojant Algolą-60 kalbą ir buvo iškeltas uždavinys: surasti
formalų būdą kalbos sintaksei aprašyti. Būdą pateik÷ Džonas Bekus,
o patobulino Peteris Nauras (Peter Naur).
Pasirodžius asmeniniams kompiuteriams buvo suprojektuota Beisiko
(Basic) kalba. Ji netur÷jo jokios įtakos kalbų moksliniams
tyrimams, mokslininkai kompiuterininkai ignoravo šią kalbą. Tačiau
daugumai programuotojų praktikų ši kalba tur÷jo nemažą reikšmę, –
buvo pirmoji jų kalba.
Pagrindinis reikalavimas, iškeltas projektuojant šią kalbą –
naudoti kuo mažiau kompiuterio atmintin÷s, nes tai buvo itin svarbu
pirmiesiems asmeniniams kompiuteriams.
Peržvelgiant šios programavimo kalbos iškeltus tikslus, vienas
jų buvo gana revoliucingas ir tur÷jęs įtakos tolesnių kalbų raidai:
žmogaus laikas, sugaištamas programos rašymui, yra svarbesnis
dalykas negu kompiuterio laikas, skirtas programos vykdymui.
1967 metais buvo suprojektuota kalba Simula-67 [Dahl68]. Ji
per÷m÷ Algolo-60 blokinę struktūrą bei valdymo sakinių
konstrukcijas, tačiau tur÷jo ir naujovių. Svarbiausia naujov÷ –
duomenų abstrakcijos samprata, išreikšta šios kalbos konstrukcija –
klase. Klas÷s id÷ja dav÷
-
16
impulsą abstraktiems duomenų tipams atsirasti, o v÷liau iš tų
pačių id÷jų išsirutuliojo objektinis programavimas.
1968 metais buvo suprojektuotas Algolas-68 [Wijngaarden75]. Tai
buvo ne vien tik Algolo-60 tobulinimas ar išpl÷timas, o iš esm÷s
nauja kalba, su daugeliu naujų konstrukcijų ir sampratų.
Viena įdomiausių Algolo-68 savybių yra jos ortogonalumas. Su
ortogonalumu susijusių kalbų raidai itin reikšmingas rezultatas –
programuotojo aprašomi duomenų tipai. Algolo-68 kalboje leidžiama
programuotojui pačiam, panaudojant standartinius tipus ir keletą jų
komponavimo būdų konstruoti naujus duomenų tipus. Šią savybę per÷m÷
v÷lesn÷s kalbos: Paskalis, C, Modula–2. Ada.
Algolas-68 jau tur÷jo dinaminius masyvus. Tokio masyvo dydis
gali kisti atliekant programą.
Algolas-68 tur÷jo didelę įtaką programavimo kalbų teorijai.
Tačiau praktikoje nepaplito, nes kalba gana sud÷tinga, pernelyg
formalus ir įkandamas jos aprašymas.
Kaip priešingyb÷ Algolo-68 sud÷tingumui 1970 metais buvo sukurta
Paskalio kalba. Jos devizas – paprastumas. Projektuojant Paskalį
buvo iškeltas tikslas: sukonstruoti tokią kalbą, kad ji tiktų
programavimui mokyti. Kalba buvo tiek paprasta ir išraiškinga, be
to tur÷jo tokias geras aukšto lygio duomenų ir valdymo struktūras,
kad greitai paplito tarp programuotojų ne tik kaip mokymosi
kalba.
Maždaug tuo pačiu metu buvo suprojektuota ir kita kalba – C. Jos
konstrukcijos buvo perimtos iš Paskalio, Algolo-68 ir netipizuotos
žemo lygio programavimo kalbos BCPL. Tod÷l C kalba turi nemažai
žemo lygio programavimo kalboms būdingų bruožų. Pavyzdžiui, beveik
n÷ra duomenų tipų kontrol÷s. Daugelis programuotojų m÷gsta kalbą C
d÷l jos lankstumo, didesnių galimybių tiesiogiai prieiti prie
kompiuterio vidaus. Tačiau visa tai yra klaidų šaltinis.
Kalbos C++ pagrindu buvo suprojektuota kalba Java. Tai galima
sakyti modernizuota C++ iš jos pašalinus daugelį nelabai tobulų
žemo lygio konstrukcijų. Tačiau Javos sintaks÷ yra labai artima C++
sintaksei. Taip padaryta populistiniais tikslais, kad kalba nebūtų
svetima dabar populiarios kalbos C++ programuotojams. Spartų Javos
plitimą lemia jos naudojimas internete.
1974 m. buvo prad÷ta Ados (ji pavadinta pirmosios pasaulyje
programuotojos, Bairono dukters Ados Lavelais vardu) kalbos
projektas. Jį finansavo JAV gynybos departamentas, nor÷damas tur÷ti
universalią programavimo kalbą procesorių, įmontuotų į karinius
įtaisus, programoms rašyti. Projektui buvo skiriama daug l÷šų.
Dirbo didel÷s žmonių grup÷s. Buvo parengti keli kalbos variantai. Į
kalbą buvo įtraukti svarbiausi tiek programų inžinerijos, tiek
programavimo kalbų projektavimo teoriniai principai – lygiagretūs
procesai, ypatingų situacijų valdymas, išsamūs realiųjų skaičių
aprašai, abstraktieji duomenų tipai ir pan. Tod÷l ši kalba dažnai
naudojama profesionaliam programavimui d÷styti.
-
17
2. FORMALŪS KALBŲ APRAŠYMO METODAI
2.1. Sintaks÷ ir semantika
Kalba yra bendravimo priemon÷. Žmon÷s bendrauja natūraliomis
kalbomis, pavyzdžiui, lietuvių, latvių, anglų, vokiečių.
Programavimo kalba skirta žmogaus ir kompiuterio bendravimui. Ja
bendravimui naudojasi ir žmon÷s, kai vienas programuotojas skaito
kito programuotojo parašytą programą.
Tam, kad bendravimas būtų s÷kmingas, reikia, kad visi tos pačios
kalbos vartotojai vienodai ją suprastų – visi programuotojai,
rašantys programas ta pačia kalba ir kompiuteriai, analizuojantys
ir atliekantys jų parašytas programas. Čia už kompiuterio v÷l slypi
žmogus – dabar jau programuotojas, parašęs transliatorių.
Aukšto lygio programavimo kalbos yra gana sud÷tingos. Retai kas
gali pasigirti, kad tobulai moka visą kurią nors programavimo kalbą
nuo pradžios iki galo. Tod÷l programavimo kalbų aprašymui skiriama
nemažai d÷mesio. Siekiama, kad programavimo kalbos aprašas būtų
trumpas, aiškus ir svarbiausia – visų vienareikšmiškai
suprantamas.
Pirmas žingsnis programavimo kalbos aprašymui susisteminti yra
jo skirstymas į lygius. Išskirtini du lygiai: sintaks÷ ir
semantika. Sintaks÷ apibr÷žia programos teksto sandarą. Semantika
nusako sintaksiškai teisingos programos prasmę. Pavyzdžiui,
Paskalio kalbos sąlyginio sakinio sintaks÷ gal÷tų būti aprašyta
šitaip:
Sąlyginis sakinys turi tokį pavidalą:
if b then S1 else S2
čia b – loginis reiškinys,
S1 ir S2 – sakiniai.
Sąlyginio sakinio dalis else S2 gali būti praleista.
Sąlyginio sakinio semantika gali būti aprašyta šitaip:
Apskaičiuojama loginio reiškinio b reikšm÷. Jeigu ji yra
„teisinga“ (true), tai atliekamas sakinys, S1, jei „neteisinga“
(false) – sakinys S2 . Jeigu sąlyginis sakinys neturi dalies else
S2 , tai antruoju atveju neatliekamas joks sakinys.
Čia sintaksę ir semantiką apraš÷me lietuvių kalba, panaudodami
matematikos žymenis. Natūraliomis kalbomis išreikšti aprašai dažnai
būna ilgoki, o svarbiausia – kai kurios vietos nevienareikšm÷s,
kitos išvis sunkiai paaiškinamos. Tai susiję su natūralių kalbų
sąvokų daugiareikšmiškumu, vartojimo laisve.
Abu lygiai – sintaks÷ ir semantika – vartojami ne tik dirbtin÷ms
kalboms aprašyti, bet ir lingvistikoje, kai nagrin÷jamos
natūraliosios (gyvosios) kalbos.
Ir sintaks÷, ir semantika gali būti aprašytos formaliai,
vartojant griežtą matematinį formalizmą. Pirmuosius darbus apie
natūralių kalbų sintaks÷s formalizavimą paraš÷ JAV kalbininkas N.
Chomskis (Naam Chomsky) 20 amž. 6–ojo dešimtmečio pabaigoje – 7–ojo
dešimtmečio pradžioje.
-
18
Tuoj pat po N. Chomskio paskelbtų darbų, Džonas Bekus ir Peteris
Nauras sukūr÷ formalizuotą formą algoritmin÷s kalbos Algolo-60
kalbos sintaksei aprašyti. Šis metodas pavadintas Bekaus ir Nauro
forma (sutrumpintai BNF).
Programavimo kalbų sintaks÷ n÷ra sud÷tinga (tokią stengiamasi
padaryti jau kuriant kalbą), tod÷l ją aprašyti formaliai nesunku ir
beveik visada vartojami formalūs būdai. Šie formalūs būdai,
aprašantys programavimo kalbų sintaksę, vadinami formaliosiomis
gramatikomis.
Programavimo kalbų semantika sud÷tingesn÷. N÷ra gero matematinio
formalizmo, įgalinančio trumpai ir aiškiai ją aprašyti.
Programavimo kalbų semantikai aprašyti daugiausiai žinomi trys
būdai: operacinis, aksiomatinis ir denotacinis. Tačiau bet kuriuo
jų parengti semantikos aprašai gana griozdiški ir sunkiai skaitomi.
Tod÷l programavimo kalbų semantika dažniausiai aprašoma
neformaliai, natūralia, kartais šiek tiek matematizuota kalba.
Riba tarp programavimo kalbos sintaks÷s ir semantikos ne visada
vienareikšmiškai nubr÷žiama. Nesutariama, pavyzdžiui, ar vardų
galiojimo bei duomenų tipų suderinimo taisykl÷s priklauso
sintaksei, ar semantikai. Kadangi šios taisykl÷s reglamentuoja
programos teksto taisyklingumą, tai jas tiktų priskirti kalbos
sintaksei. Tačiau beveik niekada šių dalykų neapibr÷žia kalbos
sintaks÷s taisykl÷s, nes beveik visada naudojami sintaks÷s aprašymo
metodai, kurie programavimo kalbos konstrukcijas aprašo
autonomiškai, t. y. neatsižvelgiant į kontekstą. Pavyzdžiui,
sakinio, kuriame panaudotas kintamasis, sintaks÷ nesiejama su to
paties kintamojo aprašo sintakse.
Nors sintaks÷ ir semantika dažniausiai tyrin÷jamos atskirai,
tačiau jos yra glaudžiai susijusios. Iš gerai aprašytos
programavimo kalbos sintaks÷s gal÷tų išplaukti ir semantikos
dalykų.
2.2. Kalba ir jos gramatika
Kas yra kalba ir iš ko ji sudaryta?
Prad÷sime nuo ab÷c÷l÷s. Kiekviena kalba turi savą ab÷c÷lę.
Apibr÷žtis. Terminalinių simbolių ab÷c÷l÷ – tai netuščia
simbolių aib÷. Ją žym÷sime T. Simboliai vadinami terminaliniais d÷l
to, kad juos būtų galima atskirti nuo kitų gramatikose vartojamų –
neterminalinių simbolių, apie kuriuos kalb÷sime toliau.
Ab÷c÷l÷s simbolius reikia suprasti abstrakčiau, negu mums
įprastas 32 lietuvių kalbos ab÷c÷l÷s raides. Ab÷c÷lę gali sudaryti
bet kokie simboliai, kurie rašte gali būti vaizduojami įvairiais
ženklais (pvz., skaitmenimis, skyrybos ženklais) arba išreiškiami
keliais spausdintais ženklais.
Sintaks÷ nagrin÷ja natūralios kalbos sakinį, sudarytą iš žodžių.
Taigi sintaks÷s požiūriu žodis yra pats mažiausias, toliau
nebeskaidomas sakinio elementas. Vadinasi, jį reikia laikyti
terminalin÷s ab÷c÷l÷s simboliu.
Jeigu kalbą tirtume morfologiniu požiūriu, tai tada reik÷tų
nagrin÷ti, kaip iš raidžių sudaromi žodžiai ar jų dalys.
Programavimo kalbos ab÷c÷lę sudaro skaitmenys, raid÷s, operacijų
ir skyrybos simboliai (kai kurie jų dažnai žymimi rašto ženklų
poromis, pvz., :=,
-
19
simbolius. Dažniausiai ab÷c÷lei naudosime pirmąsias mažąsias
lotyniškos ab÷c÷l÷s raides, pavyzdžiui,
T = {a, b},
T = {a, b, c}.
Apibr÷žtis. Seka s = t1t2...tn, sudaryta iš ab÷c÷l÷s T simbolių,
vadinama eilute. Eilut÷s ilgiu laikomas ją sudarančių simbolių
skaičius. Eilut÷s u ilgį žym÷sime |u|.
Apibr÷žtis. Eilut÷, kurios ilgis 0, vadinama tuščia eilute ir
žym÷sime ją ε; |ε| = 0.
Apibr÷žtis. Dviejų eilučių u ir v sąjunga vadinama operacija,
kurios rezultatas uv gaunamas vieną eilutę v = b1b2 ...bm prirašius
po kitos u = a1a2...an:
uv = a1a2...anb1...bm.
|uv| = |u| + |v|.
Tuščia eilut÷ turi savybę:
uε = u = εu
Apibr÷žtis. T* vadinsime aibę visų eilučių, sudarytų iš ab÷c÷l÷s
T simbolių, įskaitant ir tuščią eilutę. Visų ab÷c÷l÷s T eilučių,
išskyrus tuščią, aibę žym÷sime T+.
Vadinasi, T+ + {ε} = T*.
3 pavyzdys.
T = a, b
T* = ε, a, b, aa, ab, ba, bb, aaa, ...
T+ = a, b, aa, ab, ba, bb, aaa, ...
Apibr÷žtis. Kalba L (turinti ab÷c÷lę T) yra eilučių aib÷s T*
poaibis.
Kalbą sudaro eilučių, sudarytų iš jos ab÷c÷l÷s simbolių,
rinkinys, t. y. kalba yra eilučių aib÷. Tačiau ne visų galimų
eilučių, o tik tam tikrų. Pavyzdžiui, ne visi lietuvių kalbos
ab÷c÷l÷s raidžių rinkiniai yra lietuviški žodžiai.
Taigi kalba L yra eilučių aib÷s T* poaibis: L ⊆ T*.
4 pavyzdys.
Tegu T = {a, b}.
Panaudodami šią ab÷c÷lę galime apibr÷žti daugybę kalbų:
L1 = T* = {ε, a, b, aa, ab, ba, bb, aaa ...}
L2 = {aa, ab, ba, bb}
L3 = {bap p – pirminis skaičius}
L4 = {anbn n ≥ 1}
Čia vertikaliu brūkšniu atskiriami ribojimai kalbos eilut÷ms.
Žym÷jimu an suprantame n ilgio eilutę, sudarytą iš a simbolių, a0 –
tuščia eilut÷.
Kalbą L1 sudaro ab÷c÷l÷s {a, b} bet kokios eilut÷s: ε, a, b, aa,
ab, ba, bb, aaa ...
Kalbą L2 sudaro keturios eilut÷s.
-
20
Kalbai L3 priklauso eilut÷s, prasidedančios raide b, o po jos
einančių raidžių a skaičius, lygus bet kuriam pirminiam skaičiui:
baa, baaa, baaaaa.
Kalbai L4 priklauso eilut÷s, sudarytos iš vienodo raidžių a ir b
skaičiaus, be to raid÷s b eina po raidžių a: ab, aabb, aaabbb.
Kalba L2 yra baigtin÷, kitos trys – nebaigtin÷s
Kalbą apibr÷žti kaip aibę nesunku. Tačiau šitaip apibr÷žti
galima tik labai paprastas kalbas, kurias patogu naudoti tyrin÷jant
gramatikas – kitokius kalbų apibr÷žimo būdus. Deja, praktiškai
vartojamoms kalboms, net ir paprasčiausioms programavimo kalboms,
tokios aibių teorijos apibr÷žimo priemon÷s yra per silpnos.
Naudojami sud÷tingesni apibr÷žimo būdai, vadinami formaliomis
gramatikomis. Apie jas kalb÷sime kitame skyrelyje.
Gramatikos būna dviejų rūšių: generuojančios ir atpažįstančios.
Generuojančios gramatikos turi taisykles, pagal kurias kuriamos
(generuojamos) kalbai priklausančios simbolių eilut÷s.
Atpažįstančios gramatikos turi taisykles, nustatančias, ar duotoji
eilut÷ priklauso kalbai.
Programavimo kalbų aprašuose įprasta pateikti generuojančias
gramatikas. Jos pritaikytos programuotojui, rašančiam (kuriančiam)
programą. Tuo tarpu kompiuteriui tenka analizuoti žmogaus parašytą
programą, t. y. tikrinti, ar ji taisyklinga. Tod÷l čia geriau tinka
atpažįstančios gramatikos.
Iš karto sugeneruoti (apibr÷žti) visą sud÷tingesn÷s kalbos
eilutę (pvz., programą) arba ją atpažinti (išnagrin÷ti) būtų sunku.
D÷l to darbas atliekamas palaipsniui, dalimis. Natūralios kalbos
sakinys skaidomas į sakinio dalis: veiksnį, tarinį, papildinį ir
pan. (2.1 pav.). Programa skaidoma į programavimo kalbos
konstrukcijas: aprašus, sakinius ir t. t. tol, kol nusileidžiama
iki kalbai priklausančios terminalinių simbolių eilut÷s (2.2
pav.).
2.1 pav. Lietuvių kalbos sakinio „Žmogus eina plačiu keliu“
struktūra, išreikšta medžiu
-
21
2.2 pav. Supaprastinta priskyrimo sakinio a := b+2 struktūra,
išreikšta medžiu
Medžiu pavaizduotas eilut÷s generavimas atspindi eilut÷s
struktūrą. Tai svarbi gramatikos savyb÷. Gramatika, kurioje
naudojami neterminaliniai simboliai ir eilut÷s generavimą galima
išreikšti medžiu, vadinama struktūrine gramatika.
Gramatikos sąvokų vardai (veiksnys, tarinys ir t. t.) yra
vadinami neterminaliniais simboliais ir jie sudaro neterminalinių
simbolių ab÷c÷lę N. Šiuo atveju
N = veiksnys, tarinys, ...
Ši ab÷c÷l÷ skiriasi nuo anksčiau apibr÷žtos terminalinių
simbolių ab÷c÷l÷s T. Neterminaliniai simboliai yra vartojami pačiai
kalbai nagrin÷ti ir jie priklauso gramatikai arba metakalbai, kuria
kalbama apie kitą kalbą. Pati kalba sudaroma tik iš terminalinių
simbolių ab÷c÷l÷s. Taigi N ∩ T = ∅.
2.1 paveiksle pateiktas simbolių (žodžių) skirstymas į
terminalinius ir neterminalinius gali pasirodyti dirbtinas, kadangi
neterminaliniai simboliai (veiksnys, tarinys... ) taip pat yra
lietuviški žodžiai. Skirstymas būtų aiškesnis, jeigu lietuviškai
aprašytume kitą, ne lietuvių kalbą. Tada aiškiai matytųsi, kad
lietuviški sakinio dalių pavadinimai ir nagrin÷jamas sakinys
priklauso skirtingoms kalboms (2.3 pav.). Tokiu atveju lietuvių
kalbą ir ja parašytą gramatiką būtų galima laikyti metakalba tos,
kitos, kalbos atžvilgiu.
-
22
2.3 pav. Latvių kalbos sakinio „Cilvēk iet platu ceĜu“
struktūra, išreikšta medžiu
Skirtumas tarp terminalinių ir neterminalinių simbolių yra
aiškesnis programavimo kalbose (2.2 pav.). Akivaizdu, kad sakinys a
:= b + 2 priklauso programavimo kalbai. Tuo tarpu tos kalbos
konstrukcijų pavadinimai (kintamasis, priskyrimo ženklas ir t. t.)
yra neterminaliniai simboliai ir tarnauja pagalbiniams tikslams –
pačios kalbos aprašymui.
Gramatika, kurioje kalba apibr÷žiama naudojant tarpinius
simbolius, o konkrečios eilut÷s išreiškiamos medžiu, vadinama
struktūrine kalba.
2.3. Formalios gramatikos
Formaliomis gramatikomis apie 20 amž. 6–ąjį dešimtmetį
susidom÷jo kalbotyrininkai, kai atsirado realios technin÷s
galimyb÷s automatiškai, kompiuteriu, versti tekstus iš vienos
kalbos į kitą. Buvo bandoma sudaryti matematinį kalbos modelį.
Matematika nagrin÷ja griežtai apibr÷žtus objektus. Taigi reik÷jo
mechanizmo formaliam kalbos apibr÷žimui. Taip gim÷ formalios
gramatikos id÷ja.
Pirmuosius darbus atliko JAV Masačūsetso universiteto
lingvistas, profesorius N. Chomskis, kuris pateik÷ gramatikų
klasifikaciją.
Gramatikų prireik÷ ir dirbtinių kalbų – programavimo kalbų
kūr÷jams.
Pirmą kartą praktiškai formalių gramatikų teorija buvo
pritaikyta Algolo-60 kalbai apibr÷žti. Programuotojas Dž. Bekus ir
lingvistas P. Nauras sukūr÷ specialiai šiam tikslui pritaikytą
gramatiką, kuri buvo pavadinta jų vardu – Bekaus ir Nauro forma
(BNF).
Kompiuterijoje imta dom÷tis gramatikomis netgi daugiau, negu
lingvistikoje. Mat lingvistikoje tekstą iš vienos kalbos į kitą
s÷kmingai verčia žmogus, nesinaudodamas formaliu matematiniu
modeliu. Pasaulis nesugrius, jeigu šis darbas bus neautomatizuotas
arba nepakankamai automatizuotas. Tuo tarpu programavimo kalba,
kurios kompiuteris negali išversti į savą kompiuterinę kalbą, yra
bevert÷. Ir dar – formalizmą lengviau taikyti dirbtiniam daiktui
(t. y. programavimo kalbai), nes jį galima kurti pagal gramatikos
taisykles. Matyt šios priežastys ir l÷m÷, kad dabar formalių kalbų
teorija beveik visai persik÷l÷ iš lingvistikos į kompiuteriją.
-
23
Paprasčiausias būdas apibr÷žti kalbą, kaip aibę, yra išvardyti
visus jos elementus, pvz., sakinius. Bet realios kalbos yra
nebaigtin÷s aib÷s. Tod÷l einama kitu keliu – sudaromos taisykl÷s,
kurių pagalba galima generuoti kalbos elementus. Taisykles galima
sudaryti taip, kad baigtinis jų skaičius gal÷tų generuoti
nebaigtinį kalbos elementų skaičių.
Kadangi kalbos elementai yra eilut÷s, tai jų generavimui
natūralu taikyti eilučių operacijas. Programuotojams gerai žinomos
eilučių (ar jų dalių) keitimo komandos, vartojamos tekstų tvarkymo
sistemose. Operacija
u → v
reiškia, kad simbolių seką u, rastą pradin÷je eilut÷je, reikia
pakeisti seka v. Šitaip gaunama nauja eilut÷.
Pavyzdys. Tarkime, kad turime eilutę aab.
Taikydami operaciją ab → bac iš šios eilut÷s galime gauti dvi
naujas eilutes:
aab → abac → bacac
Taikydami operaciją b → bb iš pradin÷s eilut÷s galime gauti be
galo daug naujų eilučių:
aab → aabb → aabbb → aabbbb…
Formaliųjų gramatikų taisykl÷ms ir buvo pasirinkta ši
operacija.
Apibr÷žtis. Formalią gramatiką sudaro keturios dalys:
1) neterminalinių simbolių baigtin÷ aib÷ N;
2) terminalinių simbolių baigtin÷ aib÷ (kalbos ab÷c÷l÷) T;
3) gramatikos taisyklių baigtin÷ aib÷ P; bendru atveju taisykl÷
išreiškiama u → v, čia u ∈ (N∪T)+, v ∈ (N∪T)*;
4) pradinis neterminalinis simbolis S (S ∈ N), nuo kurio
pradedamas eilučių generavimas.
Sutrumpintai gramatika G gali būti užrašoma:
G = (N, T, P, S).
Ankstesniuose pavyzdžiuose pradiniai simboliai buvo sakinys (2.1
ir 2.3 pav.) ir priskyrimo sakinys (2.2 pav.).
Taigi, pradžioje turime vienintelę eilutę, sudarytą tik iš vieno
neterminalinio simbolio S. Taikydami generavimo taisykles gauname
naujas eilutes. Šitaip taisyklių pagalba iš pradinio simbolio S
gaunamos visos kalbai priklausančios eilut÷s.
1 pavyzdys. Sudarysime kalbos L1
L1 = anbn | n ≥ 1
gramatiką (ją galima žym÷ti G(L1)):
G(L1) = (S, a, b, S → aSb, S → ab, S)
Gramatika G(L1) turi dvi taisykles. Taisykl÷ S → aSb sako, kad
eilutę (arba jos dalį) S galima keisti eilute aSb, o taisykl÷ S →
ab sako, kad simbolį S galima pakeisti eilute ab. Taikydami šias
taisykles gausime eilutes:
S → ab
-
24
S → aSb → aabb
S→ aSb → aaSbb → aaabbb
S→ aSb → aaSbb → aaaSbbb → aaaabbbb
...
Šitaip galima gauti bet kurią kalbai L1 priklausančią eilutę ir
tik jas – jokių kitų eilučių. Kurią iš dviejų taisyklių taikyti,
pasirenkame patys. Jeigu taikome taisyklę S → aSb, tai eilutę
praplečiame vienu simboliu į abi puses, o viduryje paliekame
simbolį S – tolesnio pl÷timo galimybę. Kai norime užbaigti eilut÷s
generavimą, taikome taisyklę S → ab. Tada eilut÷je nebelieka
neterminalinio simbolio S. Ji tampa sudaryta vien iš terminalinių
simbolių.
Apibr÷žtis. Eilut÷, sudaryta vien iš terminalinių simbolių,
vadinama terminaline eilute.
Terminalin÷ eilut÷ priklauso kalbai. Tuo tarpu eilut÷, kurioje
dar yra neterminalinių simbolių, kalbai nepriklauso, nes
neterminaliniai simboliai n÷ra kalbos simboliai (jie yra gramatikos
simboliai).
2 pavyzdys.
Tarkime, kad duota L2 kalbos gramatika:
G(L2) = (B, C, S, a, b, c, P, S)
Generavimo taisyklių yra keletas, tod÷l jas užrašome
atskirai:
P = S → BC
B → aBb
B → abb
C → Ccc
C → c
Pateikiame keletą eilučių, kurios priklauso kalbai L2:
abbc
aabbbc
aaabbbbc
abbccc
abbccccc
aabbbccccc
Kalbos L2 eilučių aibę galima išreikšti šitokia formule:
L2 = anbn+1 c m , n > 0, m > 0, m \ 2 = 1
Ženklu / žym÷sime sveikųjų skaičių dalybą, o ženklu \ dalybos
liekaną. Taigi sąlyga m \ 2 = 1 reiškia, kad skaičius m yra
nelyginis.
Taisykles, kurių kair÷s pus÷s vienodos, apjungsime panaudodami
alternatyvos simbolį | (vadinamą arba):
B → aBb | abb
C → Ccc | c
-
25
3 pavyzdys.
G(L3) = ({D, E}, {a}, P, D}
Čia taisykl÷s P aprašomos:
D → a aE
E → aD
Nor÷dami nustatyti, kokios eilut÷s priklauso gramatika G(L3)
aprašytai kalbai, imkime nuosekliai taikyti taisykles:
D ⇒ a, taigi a ∈ L3.
D ⇒ aE ⇒ aaD ⇒ aaa, taigi aaa ∈ L3.
D ⇒ aE ⇒ aaD ⇒ aaaE ⇒ aaaaD ⇒ aaaaa, taigi aaaaa ∈ L3.
...
Toliau galime įžvelgti d÷sningumą, tod÷l užrašome kalbai L3
priklausančių eilučių bendrą pavidalą:
L3 = {a2n+1 n ≥ 0}.
Seka
u1 ⇒ u2 ⇒ u3 ⇒ ... ⇒ un
vadinama generavimo seka.
4 pavyzdys.
Pateiksime sud÷tingesn÷s gramatikos pavyzdį:
G(L4) = ({A, B, C}, {a, b, c}, P, A).
Jos taisyklių aibę P sudaro
A → abc | aBbc
Bb → bB
Bc → Cbcc
bC → Cb
aC → aaB | aa
Norint suvokti kalbą L4, reikia pabandyti sugeneruoti pagal
duotas taisykles bent keletą jai priklausančių eilučių. Kadangi
taisyklių nemažai ir jos painokos, užrašysime ne tik generavimo
seką, bet ir taisykles, kurias taik÷me. Štai kokios eilut÷s
priklauso kalbai L4:
1) abc ∈ L4, nes
A ⇒ abc
2) aabbcc ∈ L4, nes A ⇒ aBbc Bb → bB
⇒ abBc Bc → Cbcc ⇒ abCbcc bC → Cb ⇒ aCbbcc aC → aa ⇒ aabbcc
-
26
3) aaabbbccc ∈ L4, nes A ⇒ aBbc Bb → bB ⇒ abBc Bc → Cbcc ⇒
abCbcc bC → Cb ⇒ aCbbcc aC → aaB ⇒ aaBbbcc Bb → bB ⇒ aabBbcc Bb →
bB ⇒ aabbBcc Bc → Cbcc ⇒ aabbCbccc bC → Cb ⇒ aabCbbccc bC → Cb ⇒
aaCbbbccc aC → aa ⇒ aaabbbccc
Dabar jau galime užrašyti ir kalbai priklausančių eilučių bendrą
pavidalą:
L4 = {anbncn | n ≥ 1}
Iš tikrųjų, šitą teiginį tur÷tume pirmiausia įrodyti. Įrodymas
susid÷tų iš dviejų dalių: reik÷tų įrodyti, kad
{anbncn | n ≥ 1} ⊆ L4
ir kad
{anbncn | n ≥ 1} ⊇ L4
Įrodymas remiasi matematiniais samprotavimais ir indukcija. Jį
galima rasti knygoje [Backhouse79??].
Formalias gramatikas ir kalbos eilučių generavimą galima
sugretinti su login÷mis teoremų įrodymo sistemomis ir teoremų
įrodymu. Pradžioje turime aksiomų sistemą ir teoremų įrodymo
taisykles. Taikydami taisykles iš aksiomų, gauname naujus teiginius
– teoremas, kurių skaičius gali būti nebaigtinis. Panašiu keliu
einama ir formalių gramatikų teorijoje. Čia aksioma laikomas
pradinis neterminalinis simbolis S. Taikydami gramatikos taisykles
iš jo gauname naujas eilutes, prilygstančias naujoms teoremoms.
Pratimas
2.1 lentel÷s eilut÷s įvardintos simbolių eilut÷mis, o stulpeliai
– gramatikomis (tiksliau jų taisykl÷mis). Pažym÷kite pliusais tuos
lentel÷s langelius, kurių eilut÷se parašytas simbolių eilutes
galima sugeneruoti panaudojant jų stulpelių gramatikas.
-
27
2.1 lentel÷
Eilut÷ S → aSb S → aBb B → bB B → b
S → aSb S → aBb B → bB B → bb
S → aaS S → aT S → aaB B → bbB B → bb T → bB
S → aaS S → aaB B → bbB B → bbb
S → aaS S → aB B → bB B → bbB B → bb
aaaaaabbb
aaaabbbb
aaabbbb
abbbbbbbbb
2.4. Gramatikų klasifikacija
Bendru atveju gramatikos taisykl÷s pavidalas yra šitoks:
u→ v
u∈ (N∪ T)+; v ∈ (N ∪ T)*
u vadinama kairiąja taisykl÷s puse, v – dešiniąja.
Šitokiomis taisykl÷mis galima aprašyti bet kokią programavimo
kalbą. Tačiau taisyklių pavidalas yra toks bendras, kad iš jų maža
naudos. Naudojant šitokias bendro pavidalo taisykles, galima
apibr÷žti labai daug pačių įvairiausių kalbų. Tačiau toks
universalumas sukuria tokią didelę kalbų aibę, kad sunku rasti
naudingų (ypač realizacijai) savybių, kurias tur÷tų visos kalbos.
Kalbų aibes galima sumažinti įvedant ribojimus taisykl÷ms. Tokius
ribojimus pasiūl÷ N. Chomskis ir pagal juos suklasifikavo
gramatikas ir kalbas. Jo pateikta klasifikacija tapo visuotinai
priimta. Klasifikacija remiasi šitokiais gramatikos taisyklių
ribojimais:
1. uAv → uwv
2. A→ w
3. A→ a | A → aB
čia u, v, w ∈ (N ∪ T)*
a ∈ T
A, B ∈ N
Jeigu gramatikos taisykl÷ms netaikomi jokie ribojimai, tai
tokios gramatikos tipas yra 0. Jei tik pirmasis ribojimas – tipas
1, jei antrasis – tipas 2, jei trečiasis – tipas 3.
Kiekvienas ribojimas su didesniu numeriu apima ribojimus su
mažesniais numeriais. Pavyzdžiui, jeigu pirmąjį ribojimą
papildysime sąlyga u = v = ε, tai gausime antrąjį ribojimą. Taip
pat akivaizdu, kad trečiojo ribojimo dešin÷je rodykl÷s → pus÷je
esančios dalys yra atskiri antrojo ribojimo taisykl÷s dešin÷s pus÷s
atvejai.
-
28
Nulinio tipo gramatikos yra pačios bendriausios. Jos eilučių
keitimo galia ekvivalenti Tiuringo mašinai (2.1 lentel÷).
Pirmojo tipo gramatikos vadinamos kontekstin÷mis gramatikomis.
Tap vadinama d÷l to, kad eilut÷s pakeitimą A → w galima atlikti
tiktai tada, kai keičiama dalis A yra tam tikrame kontekste: jos
kair÷je yra eilut÷ u, o dešin÷je v. Pats kontekstas lieka toks pat
ir po taisykl÷s taikymo, tiktai jis gaubia jau naują eilutę w.
Antrojo tipo gramatikos vadinamos laisvojo konteksto
gramatikomis. Kadangi neterminalinį simbolį A galima keisti
dešiniąja taisykl÷s dalimi nepriklausomai nuo to, kurioje eilut÷s
vietoje jis bebūtų, t. y. nepriklausomai nuo kaimyninių jo
simbolių.
Beveik visos programavimo kalbos yra aprašomos laisvojo
konteksto gramatikomis.
Trečiojo tipo gramatikos vadinamos reguliariosiomis arba
automatin÷mis, kadangi jos yra ekvivalenčios reguliariesiems
reiškiniams, vartojamiems aibių teorijoje bei baigtiniams
automatams. Jeigu į gramatikas žiūr÷sime kaip į jų taisyklių aibes,
tai galios tokia priklausomyb÷:
G0 ⊇ G1 ⊇ G2 ⊇ G3
2.2 lentel÷ Gramatikų tipai
Tipas Pavadinimas Generavimo taisyklių
pavidalas
Atitikmuo algoritmų
teorijoje
0
Rekursin÷ Tiuringo mašina
1
Kontekstin÷ uAv → uwv
2
Laisvojo konteksto A → w D÷klas
3
Reguliarioji A → t | tB arba
A → t | Bt
Baigtinis automatas
2.5. Kalbų klasifikacija
Kalbos skirstomos į tokius pat tipus, kaip ir jas aprašančios
gramatikos. Taigi turime rekursines, kontekstines, laisvo konteksto
ir reguliariąsias kalbas. Tačiau klasifikuojant kalbas esama ir
šiokių tokių neaiškumų. Jų atsiranda d÷l to, kad tai pačiai kalbai
apibr÷žti galima parašyti daug skirtingų gramatikų.
Apibr÷žtis. Dvi gramatikos, apibr÷žiančios tą pačią kalbą,
vadinamos ekvivalenčiomis gramatikomis.
Pavyzdys. Duotos dvi gramatikos, turinčias tas pačias simbolių
aibes, bet skirtingas generavimo taisykles:
G1 = ({S}, {a, b}, P1, S), G2 = ({S}, {a, b}, P2, S),
P1 = {S → aS, S → b} P2 = {S → Ab, A → aA | a}
-
29
Paanalizavę gautas eilutes, galime nustatyti, kad abi gramatikos
G1 ir G2 apibr÷žia tą pačią kalbą:
L = anb | n > 0
Tai pačiai kalbai galima parašyti daug ekvivalenčių gramatikų.
Gali būti ekvivalenčios ir skirtingų tipų gramatikos. Tokiu atveju
kalbos tipu laikomas ją aprašančios gramatikos su didžiausiu
numeriu tipas.
Jeigu mokame parašyti kalbos L gramatiką i tipo, tai galime
tvirtinti, kad kalbos tipas yra nemažesnis kaip i.
Norint pasakyti, kad kalba yra i tipo, reikia įrodyti, kad
neegzistuoja jos gramatika, kurios tipo numeris yra didesnis už i.
O tai padaryti sunkiau.
Formalių kalbų tyrin÷tojai yra įrodę, kokio tipo yra tam tikro
pavidalo eilučių, išreikštų aibių žymenimis, kalbos. Jeigu tokias
eilučių formas (trafaretus) atrasime ir nagrin÷jamoje kalboje, tada
gal÷sime pasiremti įrodymais iš formalių kalbų teorijos.
Nustatyti kalbos tipą yra svarbus praktinis uždavinys, kadangi
kuo didesnis tipo numeris, tuo paprastesnius tos kalbos
transliatorių rašymo metodus galima taikyti.
Tarp skirtingų tipų kalbų aibių galioja tokios pat
priklausomyb÷s, kaip ir tarp jų gramatikų:
L0 ⊇ L1 ⊇ L2 ⊇ L3.
Tiek šios priklausomyb÷s, tiek anksčiau pateiktos priklausomyb÷s
tarp įvairių tipų gramatikų aibių, išplaukia iš pačių gramatikų
tipų apibr÷žimų (ribojimų augimo). Tačiau įdomu, ar galioja
griežtesn÷s priklausomyb÷s:
G0 ⊃ G1 ⊃ G2 ⊃ G3
L0 ⊃ L1 ⊃ L2 ⊃ L3
Tam reikia įrodyti, kad egzistuoja bent viena 0 tipo kalba (t.
y. neegzistuoja tos kalbos 1 tipo, o tuo pačiu 2 ir 3 tipo
gramatika), egzistuoja bent viena 1 tipo ir bent viena 2 tipo
kalba. Kiekvienam atvejui yra suformuluota ir įrodyta teorema.
Taigi egzistuoja ir griežtos priklausomyb÷s tarp gramatikų ir kalbų
tipų aibių.
2.4 pav. Kalbų tipų hierarchija
-
30
2.6. Kontekstin÷s kalbos
Kontekstin÷s kalbos programavimo kalboms apibr÷žti beveik
nenaudojamos, kadangi jų realizacija sud÷tinga. Tod÷l apie jas daug
nekalb÷sime, tik pateiksime pavyzdžių, kad mok÷tume šias kalbas
atpažinti ir taip gal÷tume jų išvengti.
Formalių gramatikų teorijoje įrodyta, kad kalbos
L1 = anbncn | n > 0
L2 = anbncj | n > 0, n ≤ j ≤ 2n
L3 = aibjaibj | i, j > 0
L4 = anbnan | n > 0
yra kontekstin÷s. Pirmosios kalbos L1 gramatikos taisykles
nagrin÷jome 2.3 skyr. 4 pavyzdyje. Ten pateik÷me šios kalbos
nulinio tipo gramatiką. Tačiau egzistuoja ir jai ekvivalenti
pirmojo tipo gramatika. Pateiksime jos taisykles.
S → aSBC | aBC
CB → BC
aB → ab
bB→ bb
bC→ bc
cC → cc
Taisykl÷ CB → BC netenkina pirmojo ribojimo. Tod÷l ji yra
nulinio tipo. Tačiau šią taisyklę galima išreikšti keletu pirmojo
tipo taisyklių. Pabandykime jas parašyti. Du neterminalinius
simbolius reikia sukeisti vietomis. Kadangi pirmojo tipo gramatikos
taisykl÷je galima keisti nauja eilute tik vieną kair÷je pus÷je
esantį neterminalinį simbolį (jį supantys simboliai tarnauja tik
kaip kontekstas ir n÷ra keičiami), tod÷l kiekvieno simbolio keitimą
užrašysime atskiromis taisykl÷mis.
CB → DB
DB → DE
DE → BE
BE → BC.
Iš plačiau žinomų programavimo kalbų tik Algolo-68 sintaks÷ buvo
aprašyta dviejų pakopų gramatika, kuri yra ekvivalenti kontekstinei
gramatikai. Ši gramatika vadinama Vijngardeno gramatika – jos
autoriaus A. van Wijngaarden vardu.
Pratimai
1. Naudodamiesi šiame skyrelyje pateikta kalbos anbncn pirmojo
tipo gramatika užrašykite eilut÷s a2b2c2 generavimą.
2. Nulinio tipo gramatikos taisyklę AB → BA pakeiskite trimis
pirmojo tipo gramatikos taisykl÷mis.
-
31
2.7. Laisvojo konteksto kalbos
Beveik visų programavimo kalbų sintaks÷ yra aprašyta laisvojo
konteksto (2 tipo) gramatikomis. Taigi beveik visos programavimo
kalbos yra laisvojo konteksto.
Būdingas laisvojo konteksto kalbos pavyzdys yra
L = anbn | n > 0
Šios kalbos gramatikos taisykles jau aptar÷me 2.3 skyr. 1
pavyzdyje:
S = aSb | ab
2.5 paveiksle pavaizduotas šios kalbos eilut÷s aaabbb generavimo
medis. Galime pasteb÷ti, kad bet kokios šiai kalbai priklausančios
eilut÷s medis visuomet bus simetrinis: kiek yra raidžių a, tiek pat
ir raidžių b.
2.5 pav. Eilut÷s aaabbb gavimas, pavaizduotas medžiu
Laisvo konteksto gramatikos taisyklių kair÷je pus÷je gali būti
tik vienas neterminalinis simbolis. Taigi, kiekvieną laisvo
konteksto gramatikos generuojamą eilutę galima pavaizduoti medžiu.
Iš to galima daryti išvadą, kad laisvo konteksto gramatika yra
struktūrin÷ gramatika.
1 pavyzdys.
Panagrin÷sime kalbą, kurios gramatiką aprašo taisykl÷s:
S → AB
A → aA | a
B → bB | b
Šiai kalbai priklausančios eilut÷s a3b2 generavimo medis
pateiktas 2.6 paveiksle.
-
32
2.6 pav. Eilut÷s aaabb gavimas, pavaizduotas medžiu
Verta įsid÷m÷ti, kad jeigu dviejų terminalinių simbolių skaičius
yra susietas nebaigtine priklausomybe, tai kalba yra antrojo tipo,
jeigu trijų – pirmojo tipo.
Jeigu priklausomyb÷ baigtin÷, pavyzdžiui,
L1 = anbn | 0 < n < 5
L1 = ambn | m > 0, n > 0, m \ 2 = n \ 2
tai, kaip v÷liau matysime, kalba yra trečiojo tipo.
Pratimas
1. Parašykite laisvo konteksto gramatikas eilut÷ms, sudarytoms
iš 0 ir 1, t. y. T = 0, 1, tokias, kad:
a) kiekvieno 0 dešin÷je būtų 1;
b) atvirkščiai užrašyta eilut÷ sutaptų su originalia;
c) simbolių 0 būtų dvigubai daugiau, negu 1.
2.8. Reguliariosios kalbos
Trečiojo tipo gramatikos ir kalbos vadinamos reguliariosiomis
d÷l to, kad jų kalbas galima užrašyti reguliariaisiais reiškiniais.
Reguliariųjų reiškinių nenagrin÷sime.
Reguliariąsias kalbas taip pat galima apibr÷žti kaip baigtinius
automatus, kurie gali atlikti ir kalbos generatorių (nedeterminuoti
baigtiniai automatai) ir analizatorių (determinuoti baigtiniai
automatai) vaidmenį. D÷l to kartais trečiojo tipo gramatikos ir
kalbos dar vadinamos automatin÷mis.
Baigtinius automatus įprasta užrašyti grafiškai. Tai gana
vaizdus kalbos aprašymo būdas. Apie baigtinius automatus kalb÷sime
kitame skyrelyje.
Reguliarioji gramatika, kurios taisykl÷s užrašytos aukščiau
pateiktu pavidalu:
-
33
A → a
A → aB
yra vadinama kairine gramatika, kadangi eilut÷ generuojama iš
kair÷s į dešinę. Pavyzdžiui, jeigu turime taisykles:
S → b
S → aS
tai eilut÷ aaaab gaunama šitaip:
S → aS → aaS → aaaS → aaaaS → aaaab.
Reguliarioji gramatika, kurios taisykl÷s užrašytos šitokiu
pavidalu:
A → a
A → Ba
yra vadinama dešinine gramatika, kadangi eilut÷ generuojama iš
dešin÷s į kairę. Tik ką nagrin÷tos kalbos dešinin÷s gramatikos
taisykl÷s būtų užrašomos šitaip:
S → Ab
A → Aa
A → a
Nesunku įrodyti, kad abiejų pavidalų A → aB ir A → Ba taisykl÷s
generuoja tas pačias kalbų aibes.
Mes skaitome ir rašome iš kair÷s į dešinę. Tod÷l mums
įprastesn÷s yra kairin÷s gramatikos. Jas toliau ir naudosime.
Tai pat nesunku įrodyti, kad taisykles:
A → x
A → xB
A → Bx
čia x ∈ T+
galima išreikšti bendro pavidalo reguliariųjų gramatikų
taisykl÷mis. Pabandykite įrodyti.
Trečiojo tipo kalbos yra pačios paprasčiausios. Jose negali būti
sąsajų tarp bet kurių dviejų simbolių skaičiaus, išreikšto
nebaigtine priklausomybe. Pavyzdžiui, d÷l tos priežasties
kalbos
anbn | n > 0
ambn | n > 0, m > 0, m = n + 1
ambn | n > 0, m > 0, m = n / 5
ambn | n > 0, m > 0, n > m
negali būti apibr÷žiamos reguliariosiomis gramatikomis.
Bet kuri kalba, turinti baigtinį eilučių skaičių, gali būti
aprašyta reguliariąja gramatika. Įrodymas paprastas. Taisykl÷,
turinti pavidalą A→ x, yra 3 tipo. Taigi kiekvienai terminalinei
eilutei galima parašyti po taisyklę ir tur÷sime pačią
paprasčiausią, nors ir nepatogią – su ilgu (bet baigtiniu)
taisyklių sąrašu, gramatiką. Pavyzdžiui, kalba
-
34
anbn | 0 < n ≤ 100
yra trečiojo tipo. Tačiau tokios kalbos gramatika būtų labai
griozdiška – tur÷tų 100 taisyklių:
S → ab
| aabb
| aaabbb
| aaaabbbb
. .
Trečiojo tipo gramatikomis galima aprašyti tik pačias
paprasčiausias programavimo kalbos konstrukcijas: vardus, skaičius
ir pan. Tačiau ir tai svarbu, nes trečiojo tipo kalbų analiz÷ labai
paprasta. Transliatoriuose ji netgi atskiriama nuo sintaks÷s
analiz÷s ir vadinama leksikos analize (žr. 3 skyr).
Pratimas
1. Gramatiką, kurios taisykl÷s aprašytos
A → aA | A → aB
B → bB | B → bC
C → cC | C → c
pakeiskite jai ekvivalenčia gramatika, tokia, kad visos
taisykl÷s būtų pavidalo
S → a | S → Sa
2.9. Baigtinio automato samprata
Kiekviena reguliari gramatika gali būti pavaizduota orientuotu
grafu, kurio viršūn÷s pažym÷tos neterminaliniais simboliais, o
rodykl÷s – terminaliniais. Taisykl÷s vaizduojamos rodykl÷mis. Jeigu
gramatika turi taisyklę A→ aB, tai iš viršūn÷s A eina rodykl÷,
pažym÷ta terminaliniu simboliu a, į viršūnę B. Jeigu gramatika turi
taisyklę A→ a, tai iš viršūn÷s A eina rodykl÷ a į viršūnę,
nebeturinčią sąsajos su neterminaliniu simboliu. Tai vienintel÷
viršūn÷, iš kurios neišeina jokia rodykl÷. Tai pabaigos viršūn÷. Ją
žym÷sime simboliu Z, o jos figūrą piešime storesne linija.
Eilut÷ pradedama generuoti nuo viršūn÷s, pažym÷tos pradiniu
neterminaliniu simboliu S (ir ateinančia rodykle). Einama rodyklių
kryptimi, kol pasiekiama viršūn÷ Z. Eilut÷ gaunama iš kelyje
pasitaikiusių rodykles žyminčių terminalinių simbolių.
Pavyzdys. 2.7 paveiksle pateiktas grafas gramatikos:
S → aB | aC
B → bB | b
C → cC | c
-
35
2.7 pav. Nedeterminuotas automatas
Vaikščiojimas grafu prilygsta gramatikos taisyklių taikymui.
Vienas žingsnis atitinka vieną taisykl÷s taikymą.
Tokio pat pavidalo grafu vaizduojamas ir baigtinis automatas.
Tai matematin÷s baigtinių automatų teorijos objektas. Ši teorija
taikoma kompiuterio schemų (procesorių) projektavime. Jos
rezultatais galima pasinaudoti ir nagrin÷jant trečiojo tipo
gramatikas.
Baigtinis automatas turi tam tikrą, baigtinį, būsenų skaičių
(d÷l to ir vadinamas baigtiniu). Būsenos vaizduojamos grafo
viršūn÷mis. Kiekvienu diskretaus laiko momentu automatas yra
vienoje būsenoje. Su kiekvienu nauju laiko momentu (taktu)
automatas pereina į kitą būseną. Jis pradeda darbą nuo pradin÷s
būsenos. Automato elgsena priklauso nuo to, kokioje būsenoje jis
yra: gali pereiti tik į tą būseną, į kurią eina rodykl÷ ir prie
kalbos eilut÷s prid÷ti tą simbolį, kuriuo pažym÷ta rodykl÷.
Pavyzdžiui, jei 2.7 paveiksle pavaizduotas automatas yra būsenoje
B, tai jis gali pereiti tik į tą pačią būseną arba į galinę būseną
Z. Abi rodykl÷s pažym÷tos ta pačia raide b. Vadinasi.abiem atvejais
automatas generuojamos eilut÷s pabaigoje prirašo raidę b, bet vienu
atveju eilut÷s generavimo jis dar nebaigia, o kitu baigia.
Baigtiniai automatai skirstomi į determinuotus ir
nedeterminuotus. Jeigu visos rodykl÷s, išeinančios iš kiekvienos
grafo viršūn÷s yra pažym÷tos skirtingomis raid÷mis, tai toks
automatas vadinamas determinuotu, o priešingu atveju –
nedeterminuotu. Automatas, pavaizduotas 2.7 paveiksle yra
nedeterminuotas. Tą pačią kalbą atitinkantis kitas automatas,
pavaizduotas 2.8 paveiksle, yra determinuotas.
-
36
2.8 pav. Determinuotas automatas, aprašantis tą pačią kalbą,
kaip ir 2.7 pav. pateiktas nedeterminuotas automatas
Determinuotas automatas gali ne tik generuoti kalbai
priklausančias eilutes, bet ir atpažinti, ar nagrin÷jama eilut÷
priklauso duotai kalbai. T. y., determinuotas automatas gali
atlikti atpažįstančios gramatikos vaidmenį. O tai labai svarbi jo
savyb÷, lemianti jo panaudojimą formalių kalbų teorijoje.
Dabar įsivaizduokime eilut÷s generavimui atvirkščią veiksmą –
eilut÷s atpažinimą. Automatas skaito simbolių eilutę po vieną
simbolį per laiko vienetą (taktą) ir priklausomai nuo perskaityto
simbolio bei būsenos, kurioje jis yra tuo laiko momentu, pereina į
naują būseną (t. y. pakeičia savo esamą būseną).
Tur÷dami bet kokią eilutę galime lengvai patikrinti, ar ji
priklauso kalbai, kurią apibr÷žia duotas automatas. Reikia paeiliui
imti eilut÷s simbolius ir jais vadovaujantis vaikščioti automato
būsenomis. Jeigu automatas, perskaitęs paskutinį eilut÷s simbolį,
atsiras galin÷je būsenoje, tai reikš, kad eilut÷ priklauso kalbai.
Jeigu paskutinis eilut÷s simbolis neatves į galinę būseną arba
vaikščiojimas būsenomis „užluš“ dar nebaigus analizuoti eilut÷s
(automatas atsidurs būsenoje, iš kurios neišeina rodykl÷, pažym÷ta
reikiamu simboliu), tai reikš, kad eilut÷ kalbai nepriklauso.
Baigtinio determinuoto automato galin÷ būsena turi kiek kitokį
statusą, negu nedeterminuoto: iš jos gali eiti rodykl÷s į kitas
būsenas. Tai galima (bet nebūtina) automato darbo, o kartu ir
eilut÷s analiz÷s pabaiga. Jeigu pasibaig÷ eilut÷s analiz÷ galin÷je
būsenoje, tai eilut÷je neb÷ra daugiau simbolių ir tada savaime
neb÷ra kur toliau eiti. Be to determinuotas automatas gali tur÷ti
kelias galines būsenas.
Automato darbą paaiškinsime pavyzdžiu, pateiktu 2.9
paveiksle.
-
37
2.9 pav.
Pradin÷ automato būsena pažym÷ta b1.
Pateiktas automatas atpažįsta šitokias eilutes:
b ab bc abc aabc aabcc aaabcc ir t. t.
Pateiksime automato būsenų kaitą, kai jam pateikiama eilut÷
aaabcc. Perskaitytą eilut÷s dalį nuo neperskaitytos skirsime
tarpu
aaabcc b1 a aabcc b1 aa abcc b1 aaa bcc b1 aaab cc b2 aaabc c b2
aaabcc b2
Nesunku įsitikinti, kad kalbą, kuriai priklauso ši eilut÷,
galima užrašyti aibe {ambcn | m ≥ 0, n ≥ 0}.
Jeigu automatui pateiksime eilutę, nepriklausančią kalbai, tai
kurioje nors būsenoje jis „užstrigs“ – nebus rodykl÷s, pažym÷tos
perskaitytu simboliu, išeinančios iš tos būsenos. Pavyzdžiui, jeigu
nagrin÷tam automatui pateiksime eilutę
aacc
tai jis, būdamas b1 būsenoje, perskaitys simbolį c ir nebežinos,
ką toliau daryti (kur eiti), nes n÷ra rodykl÷s, pažym÷tos simboliu
c, išeinančios iš būsenos b1.
Taigi šitaip pavaizduotas baigtinis automatas atitinka dalinai
apibr÷žtą funkciją. Norint, kad automatas atitiktų visur apibr÷žtą
funkciją, reik÷tų papildyti jį dar viena būsena b’, į kurią jis
patektų visais atvejais, kai aptinka, jog eilut÷ nepriklauso
kalbai. Į būseną b’ turi būti rodykl÷s iš visų būsenų, iš kurių
neišeina rodykl÷s pažym÷tos visais terminaliniais simboliais. Iš
būsenos b’
-
38
neturi eiti rodykl÷s į kitas būsenas. Tokio automato pavyzdys
pateiktas 2.10 paveiksle. Raide b’ pažym÷jome būseną, į kurią
automatas patektų perskaitęs eilutę, nepriklausančią kalbai.
2.10 pav.
Skyrelio pradžioje apraš÷me, kaip iš trečiojo tipo gramatikos
gauti baigtinį nedeterminuotą automatą. Vadinasi, galime tvirtinti,
kad kiekvienai trečiojo tipo gramatikai egzistuoja nedeterminuotas
automatas. Ar tas pats galioja ir determinuotam, t. y. ar galima
tvirtinti, kad kiekvienai trečiojo tipo gramatikai egzistuoja
determinuotas automatas?
Taip, egzistuoja. Automatų teorijoje įrodoma, kad kiekvienam
nedeterminuotam automatui egzistuoja jį atitinkantis determinuotas
automatas.
Įrodymo id÷ja labai paprasta. Nedeterminuotumo priežastis yra
kelios tuo pačiu simboliu pažym÷tos rodykl÷s, einančios iš tos
pačios būsenos į kelias skirtingas būsenas. Nedeterminuotumą galima
pašalinti automato būsenas pakeitus visomis galimomis būsenų
aib÷mis. Jeigu nedeterminuotas turi n būsenų, tai gausime 2n būsenų
aibių, o atmetus tuščią aibę bus 2n-1. Tada kelios rodykl÷s,
pažym÷tos ta pačia raide ir einančios į kelias skirtingas būsenas,
pakeičiamos viena rodykle, einančia į naują būseną, vaizduojančią
tų būsenų aibę. Pailiustruosime tai ankstesniu pavyzdžiu,
nedeterminuotą automatą (2.7 pav.) pakeisdami determinuotu (2.8
pav.).
Nedeterminuotas automatas (2.7 pav.) turi 4 būsenas. Vadinasi,
determinuotas automatas tur÷s 24-1= 15 būsenų. Belieka sutvarkyti
rodykles. Pradedame nuo pradin÷s būsenos S. Iš jos išeinančias dvi
rodykles, pažym÷tas raide a, einančias į būsenas B ir C, pakeičiame
viena rodykle, einančia į būseną BC. Būsena BC tapo bendra dviejų
būsenų būsena. Iš buvusios būsenos B, dabar jau priklausančios
jungtinei būsenai BC, išeina dvi rodykl÷s b į būsenas B ir Z.
Vadinasi, rodykl÷ b turi eiti iš BC į BZ. Analogiškai rodykl÷ c
vedama iš BC į CZ.
Nepanaudotas būsenas galima pašalinti ir gauname determinuotą
automatą, pavaizduotą 2.11 paveiksle, analogišką pavaizduotam 2.5
paveiksle. Skiriasi tik būsenų pavadinimai, kuriuos galima
pakeisti.
-
39
2.11 pav. Determinuotas, gautas iš nedeterminuoto automato,
pavaizduoto 2.7 pav.
Einant tokiu keliu kiekvienai trečiojo tipo gramatikai galima
sukonstruoti baigtinį determinuotą automatą. Tačiau šis kelias
ilgas ir dažnai automatas konstruojamas nesudarius gramatikos
taisyklių.
Baigtinį automatą nesunku modeliuoti programa ir tuo pačiu gauti
trečiojo tipo kalbos analizatorių.
Pratimai
1. Nubraižykite schemą baigtinio automato, atpažįstančio
šitokios gramatikos generuojamas eilutes:
a) S → aS | aB | aC B → bB | b C → cC | c
b) S → Ab | Ac | Bb | Cc A → Aa | a B → Bb | Aa |a C → Cc | Aa |
a
2. Nubraižykite schemą baigtinio automato, atpažįstančio
Paskalio kalbos slankaus kablelio skaičius.
3. Nubraižykite schemą baigtinio automato, atpažįstančio visas
eilutes, sudarytas iš nulių ir vienetų, kuriose
a) n÷ra greta nei trijų nulių, nei trijų vienetų;
b) greta esančių nulių skaičius gali būti tik lyginis.
4 Nubraižykite schemą baigtinio automato, atpažįstančio šių
kalbų eilutes:
a) L1 = {anbm | n > 0, m > 0, n \ 2 = m \ 2},
-
40
b) L2 = {ab2n+1c | n ≥ 0},
c) L3 = {(ab)* ∪ {bc, a}* b}.
2.10. Gramatikų ir kalbų vienareikšmiškumas
Labai svarbus reikalavimas programavimo kalboms – kad jos būtų
vienareikšm÷s arba, jei kitaip neįmanoma, bet kuris
nevienareikšmiškumas būtų aiškus ir lengvai išvengiamas.
Pirmiausia išsiaiškinsime vienareikšmiškumo sąvoką. Tam atidžiau
panagrin÷kime laisvo konteksto gramatikos pavyzdį (2.7 skyr. 1
pavyzdys):
S → AB A → aA | a B → bB | b
Imkime eilutę a3b2 ir užrašykime jos generavimo seką:
S ⇒ AB ⇒ aAB ⇒ aaAB ⇒ aaaB ⇒ aaabB ⇒ aaabb
Šią eilutę gavome taikydami taisykles sistemingai: visada
keisdami pirmąjį iš kair÷s neterminalinį simbolį. Kai eilut÷je yra
daugiau negu vienas neterminalinis simbolis, tai galima pasirinkti,
kurį keisti. Jeigu eilut÷je AB keitimui pasirinktume antrąjį
simbolį B ir toliau keistume pirmąjį iš dešin÷s simbolį, tai
gautume šitokią generavimo seką:
S ⇒ AB ⇒ AbB ⇒ Abb ⇒ aAbb ⇒ aaAbb ⇒ aaabb.
Kaitaliodami pasirenkamus neterminalinius simbolius, galime
gauti daugiau generavimo sekų.
Tačiau jei kiekvienai šiai sekų konstruotume generavimo medį,
tai kaskart gautume tą patį medį (žr. 2.7 skyr. 2.5 pav.). Skirtųsi
tik medžio gavimo kelias: kurią šaką: kairiąją ar dešiniąją pirmiau
piešiame.
Apibr÷žtis. Gramatika, kurios kiekvienai generuojamai eilutei
galima sudaryti tik vieną generavimo medį, vadinama vienareikšme.
Priešingu atveju (t. y, jeigu generuojama bent viena eilut÷, kuriai
galima sudaryti du ar daugiau medžių) – nevienareikšme.
1 pavyzdys.
Kalbos L ={an | n ≥ 1} gramatika
G1(L) = ({S}, {a}, {S → a | aS}, {S})
yra vienareikšm÷, o gramatika
G2(L) = ({S}, {a}, {S → a | aS | Sa}, {S})
nevienareikšm÷.
Jos eilučių generavimo medžių pavyzdžiai pateikti 2.12 ir 2.13
paveiksluose.
-
41
2.12 pav. Kalbos L = {an | n ≥ 1} eilučių a, aa ir aaa
vienareikšm÷s gramatikos G1(L) = ({S}, {a}, {S → a | aS}, {S})
generavimo medžiai
2.13 pav. Kalbos L = {an | n ≥ 1} eilučių aa ir aaa generavimo
medžiai, gauti taikant nevienareikšmę gramatiką G2(L) = ({S}, {a},
{S → a | aS | Sa}, {S})
Kai gramatika vienareikšm÷, tai kiekvienai eilutei galima
nubraižyti vienintelį generavimo medį (2.12 pav.), o kai
nevienareikšm÷, tai yra eilučių, kurioms galima nubraižyti daugiau
medžių. Šiuo atveju eilutei aa – du medžius (2.13a pav.), o eilutei
aaa– keturis (2.13b pav.).
Vienareikšmiškumas yra labai svarbi gramatikų savyb÷ rašantiems
transliatorius ir apskritai bet kokius kalbos analizatorius. Mat
transliatorius iš programos teksto atkuria jo generavimo medį ir su
kiekvienu medžio mazgu (t. y. gramatikos taisykl÷s taikymu) susieja
tam tikrus veiksmus (pvz., generuoja kompiuterio komandas). Jeigu
gramatika nevienareikšm÷, tai tie veiksmai priklausys nuo to, kokį
medį pasirinks transliatorius (kokiu keliu jis nueis analizuodamas
eilutę). D÷l to
-
42
stengiamasi išvengti nevienareikšmių gramatikų. Kaip iš
ankstesnio pavyzdžio galima pasteb÷ti, nevienareikšmiškumas
atsiranda, kai gramatika turi per daug taisyklių, t. y., kai tą
patį darbą atlieka ne viena taisykl÷. Taigi sudarant gramatiką
reikia taisykles rašyti sistemingai ir taip, kad jų būtų kuo
mažiau.
Kaip nustatyti, ar gramatika vienareikšm÷?
Nevienareikšmiškumą galima įrodyti konstruktyviai – surasti bent
vieną eilutę, gaunamą dviem skirtingais generavimo medžiais.
Nustatyti, ar gramatika vienareikšm÷, sunkiau – reikia įrodyti, kad
neegzistuoja eilut÷s, turinčios daugiau negu vieną generavimo medį.
Bendru atveju ši problema algoritmiškai neišsprendžiama, t. y.
neegzistuoja algoritmas, kuris nustatytų, ar duota gramatika
vienareikšm÷. Tačiau atskiroms gramatikų grup÷ms, o juo labiau
atskiroms gramatikoms ši problema išsprendžiama – vienareikšmiškumą
galima įrodyti.
Nevienareikšmiškumų pasitaiko ir klasikinių programavimo kalbų
gramatikose. Buvo daug kalbama apie aptiktą nevienareikšmiškumą
Algolo-60 gramatikoje. Jo priežastis yra viena iš taip vadinamos
simbolių atvirosios eilut÷s E gavimo taisyklių:
E → E E
Beje, šis nevienareikšmiškumas neturi praktin÷s reikšm÷s (matyt
tod÷l jis ir „prasmuko“ į kalbą). Mat Algole-60 simbolių eilut÷s
tik spausdinamos. Jokie kitokie veiksmai su jomis neatliekami. D÷l
to ne taip svarbu kokia tvarka sudedami simboliai į spausdinimui
ruošiamą eilutę. Tačiau yra atvejų, kur vienareikšmiškumas vaidina
svarbų vaidmenį. Panagrin÷sime aritmetinio reiškinio sintaksę.
2 pavyzdys.
Pateiksime aritmetinio reiškinio, kuriame vartojamos keturios
aritmetin÷s operacijos su kintamaisiais a, b, c, d, vienareikšmę
gramatiką G1:
G1 = ({R, T, D}, {a, b, c, d, +, –, *, /, (, )}, P, {R}), čia P
sudaro taisykl÷s
R → T | R + T | R – T
T → D | T * D | T / D
D → a | b | c | d | (R)
Neterminaliniai simboliai taisykl÷se turi šitokią prasmę:
R – reiškinys,
T – termas,
D – daugiklis.
Reiškinio a + b * c / a – d generavimo medis pateiktas 2.14
paveiksle.
-
43
2.14 pav. Reiškinio a + b * c / a – d generavimo medis, gautas
taikant 2 pavyzdžio vienareikšmę gramatiką
Transliatorius formuotų šio reiškinio reikšmę skaičiuojančias
operacijas kildamas medžiu iš apačios į viršų, t. y. pirmiausiai
suformuotų daugybos b * c operaciją, po to formuotų dalybos,
sud÷ties ir atimties operacijas. Atidžiau panagrin÷ję gramatiką G1,
galime įsitikinti, kad pagal ją bus sudaromas medis,
vienareikšmiškai nusakantis operacijų atlikimo tvarką. Pirmiau
atliekamos operacijos skliaustuose. Jeigu skliaustų n÷ra, tai
pirmiau atliekama daugyba ir dalyba, po to – sud÷tis ir atimtis.
Greta esančios vienodo prioriteto operacijos atliekamos iš kair÷s į
dešinę.
Tokiems patiems reiškiniams apibr÷žti galima sudaryti
nevienareikšmę gramatiką. Pateiksime tokios gramatikos (G2)
pavyzdį.
3 pavyzdys.
G2 = ({R}, {a, b, c, d, +, –, *, /, (, )}, P, {R}), čia P yra
taisykl÷
R → a | b | c | d | (R) | R + R | R – R | R * R | R / R
Naudojantis šia gramatika aukščiau nagrin÷tam reiškiniui a + b *
c / a – d galima sudaryti daugelį medžių.
Pateikiame du skirtingus medžius (2.15 ir 2.16 pav.).
2.15 pav. Reiškinio a + b * c / a – d generavimo medis, gautas
taikant 3 pavyzdžio nevienareikšmę gramatiką
-
44
2.16 pav. Kitas reiškinio a + b * c / a – d generavimo medis,
gautas taikant 3 pavyzdžio nevienareikšmę gramatiką
Štai d÷l ko programavimo kalbų sintaks÷s aprašuose pateikiamos
reiškinių gramatikos iš pirmo žvilgsnio atrodo sud÷tingos ir
kartais norisi jas suprastinti. Mat sud÷tingumas ir papildomi
neterminaliniai simboliai (termas, daugiklis) reikalingi tam, kad
gramatika vienareikšmiškai nustatytų operacijų atlikimo tvarką ir
tokią, kokios mes norime.
Iki šiol nevienareikšmiškumo sąvoką taik÷me tik gramatikoms. Tai
pačiai kalbai raš÷me ir vienareikšmes, ir daugiareikšmes
gramatikas.
Paprastai kalbą apibūdina ją aprašančios gramatikos savyb÷s.
Jeigu kalbą aprašanti gramatika yra vienareikšm÷, tai sakoma, kad
ir kalba vienareikšm÷, priešingu atveju – nevienareikšm÷.
Gal yra tokių kalbų, kurių visos gramatikos nevienareikšm÷s, t.
y. kurioms negalima parašyti vienareikšmių gramatikų?
Taip, yra.
Apibr÷žtis. Kalbos, kurioms neegzistuoja vienareikšm÷ gramatika,
vadinamos esminiai nevienareikšm÷mis kalbomis.
Pateiksime tokios kalbos pavyzdį.
{aibi cj | i ≥ 1, j ≥ 1} ∪ {aibjcj | i, j ≥ 1}
Šią kalbą sudaro dviejų aibių sąjunga.
Pratimai
1. Duotos gramatikos taisykl÷s:
R → S R → S – R S → T S → T + S S → T * S T → U T → U / T U → 0
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
-
45
Aritmetinių operacijų semantika įprasta (+ sud÷tis, – atimtis, *
daugyba, / dalyba). Jų atlikimo tvarką apibr÷žia sintaks÷.
Apskaičiuokite šių reiškinių reikšmes:
a) 8 * 4 / 2 / 2
b) 8 – 7 – 4 + 3 * 2 + 6 – 5 – 4
c) 8 – 7 – 4 + 3 * 2 + 6 – 7 – 4
d) 12 * 4 / 2 / 2
e) 18 * 4 / 2 / 2
f) 8 – 6 – 4 + 3 * 2 + 6 – 5 – 4
g) 16 * 4 / 4 / 2
h) 10 – 7 – 4 + 3 * 2 + 6 – 5 – 4
2. Aritmetinį reiškinį apibr÷žia gramatikos, kurių taisykl÷s
pateiktos žemiau:
a) R → T | R + T | R – T | R * T | R / T T → a | b | c
b) R → T | T + R | T – R | T * R | T / R T→ a | b | c
c) R → T | R F T T→ a | b | c F → + | – | * | /
Nustatykite, kurios gramatikos vienareikšm÷s.
Ar šios gramatikos ekvivalenčios? Jei taip, kurios?
3. Parašykite vienareikšm÷s antrojo tipo gramatikos taisykles
reiškiniui generuoti. Generavimo medis turi atspind÷ti operacijų
atlikimo tvarką. Reiš