vilniaus universitetas - mokytojas...leksika, sintaks÷, semantika 17 2.2. kalba ir jos gramatika 18...

174
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

Upload: others

Post on 18-Feb-2021

6 views

Category:

Documents


0 download

TRANSCRIPT

  • 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š