You are on page 1of 174

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: valentina.dagiene@mif.vu.lt
grigas@ktl.mii.lt

Vilnius, 2007

1
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.

2
Į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ę.

3
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).

4
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

5
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

6
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

7
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

8
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.

9
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.

10
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.

11
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ų.

12
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

13
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,

14
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÷

15
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.

16
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.

17
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., :=, <=), baziniai žodžiai (pvz., begin, if).
Gramatikų nagrin÷jimui natūralios kalbos, o taip pat ir programavimo kalbos, yra perdaug
sud÷tingos. Tod÷l šiam tikslui imsime labai paprastas kalbas, kurių ab÷c÷l÷s turi vos kelis

18
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.

19
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

20
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.

21
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ą.

22
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

23
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

24
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

25
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.

26
2.1 lentel÷
Eilut÷ S → aSb S → aSb S → aaS S → aaS S → aaS
S → aBb S → aBb S → aT S → aaB S → aB
B → bB B → bB S → aaB B → bbB B → bB
B→b B → bb B → bbB B → bbb B → bbB
B → bb B → bb
T → 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.

27
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ų Atitikmuo algoritmų
pavidalas teorijoje
Rekursin÷ Tiuringo mašina
0
Kontekstin÷ uAv → uwv
1
Laisvojo konteksto A→w D÷klas
2
Reguliarioji A → t | tB arba Baigtinis automatas
3
A → t | Bt

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}

28
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

29
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.

30
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.

31
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:

32
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

33
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

34
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.

35
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.

36
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’

37
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
4
2 -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.

38
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},

39
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.

40
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

41
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→EE
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.

42
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ą

43
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

44
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škinyje vartojamos dvinar÷s
operacijos žymimos šiais ženklais:
* /
+

Čia operacijos išd÷stytos pagal prioritetus. Vienoje eilut÷je – vienodo prioriteto operacijos.
Aukščiausią prioritetą (pirmiausia atliekama) turi viršutin÷s eilut÷s operacija, žemiausią apatin÷s.
Daugybos ir dalybos operacijos yra vienodo prioriteto, o sud÷ties prioritetas yra aukštesnis (ji
atliekama pirmiau) negu atimties. Vienodo prioriteto operacijos atliekamos iš kair÷s į dešinę.
Visus kintamuosius žym÷kite raide a.
4. Parašykite vienareikšmę gramatiką reiškinio, kuriame operacijų prioritetai yra tokie:
*/
+–
<>
Greta esančios operacijos * ir (arba) / atliekamos iš dešin÷s į kairę. Kitos vienodo prioriteto –
iš kair÷s į dešinę. Kitokia operacijų atlikimo tvarka nurodoma skliaustais ( ).

45
Visus kintamuosius žym÷kite raide a.
5. Nubraižykite reiškinio a < a * a * a / a / a generavimo medį pagal 4 uždavinyje sudarytą
gramatiką.
6. Parašykite vienareikšmę gramatiką, ekvivalenčią šiai gramatikai
S → ABC;
A → aA | a | Aa;
B → bBb | bC | Cb | bb;
C → Cc | c;
7. Vienareikšmę reiškinio gramatiką G1 (žr. 2 pavyzdį) papildykite k÷limo laipsniu operacija,
žymima **. K÷limo laipsniu operacija yra aukščiausio prioriteto (ji atliekama pirmiausiai), o kelios
greta esančios k÷limo laipsniu operacijos atliekamos iš dešin÷s į kairę, t. y. reiškinys
a** b** c
skaičiuojamos taip, lyg būtų šitaip sud÷ti skliaustai
a** (b** c)

2.11. Kaip praktiškai nustatyti kalbos tipą

Jeigu kalbai galime parašyti i tipo gramatiką, tai galime tvirtinti, kad kalbos tipo numeris yra
nemažesnis už i. O norint tvirtinti, kad kalbos tipo numeris lygus i, reikia įsitikinti, kad negalima
sudaryti gramatikos, kurios tipo numeris didesnis už i. Taigi reikia prad÷ti bandyti nuo didžiausio i.
Trečio tipo kalboms nagrin÷jome bent dvi gramatikas – generuojančią (klasikinę) ir
atpažįstančią (baigtinį automatą). Dažnai lengviau konstruoti automatą, nes jis vaizdesnis.
Pailiustruosime pavyzdžiais.
1 pavyzdys. Tegu turime kalbą
L1= {ambn | m ≥ 1, n ≥ 1}.
Raidžių a ir b skaičiai vienas nuo kito nepriklauso. Svarbu, kad visos raid÷s a eitų prieš raides
b. Taigi čia lengva sukonstruoti baigtinį automatą, kadangi būsenų skaičius nepriklauso nuo m ir n.
Vadinasi, kalba L1 yra trečiojo tipo. Ją atpažįstančio automato schema pateikta 2.17 paveiksle.

2.17 pav.

46
2 pavyzdys. Turime kalbą
L2 = {anbn | 1 ≤ n ≤ 4}.
Raidžių a turi būti tiek pat, kiek ir raidžių b. Eilučių skaičius baigtinis, vadinasi tokiai kalbai
galima sukonstruoti baigtinį automatą. Reikia įsiminti raidžių a skaičių n. Kiekvienam skaičiui n
reikia automato būsenos (2.18 pav.). Vadinasi, kalba L2 taip pat trečiojo tipo.

2.18 pav.

3 pavyzdys. Turime kalbą


L3 = {anbn | n ≥ 1}.
Skaičius n neribojamas. Jis gali būti kiek norima didelis. Jeigu nubraižysime automatą, turintį
N būsenų skaičiui n įsiminti (N = n), tai visada galima parašyti eilutę, turinčią n = N + 1 raidžių, ir
jos automatas neatpažins. Taigi negalima sukonstruoti automato, atpažįstančio visas kalbos L3
eilutes. Vadinasi, kalba L3 n÷ra 3 tipo.
Dabar reikia bandyti kalbai L3 sudaryti antrojo tipo gramatiką. Tai lengvai galima padaryti,
kadangi pagal antrojo tipo gramatikos taisykles eilutę galima generuoti nuo vidurio plečiant į abi
puses ir tuo pačiu išlaikant vienodą raidžių a ir b skaičių.
Užrašome antrojo tipo gramatikos taisykles:
S → aSb | ab
4 pavyzdys. Nagrin÷kime kalbą L4 = {anbncn | n ≥1}.
Trečio tipo gramatikos n÷ra ko ir bandyti rašyti, kadangi žinome, kad jau ir eilut÷s daliai,
pavyzdžiui, anbn, tokios gramatikos negalima parašyti.
Antrojo tipo gramatikos kalbai L4 taip pat nepavyks parašyti, kadangi čia turi būti vienodas
trijų raidžių skaičius, o eilut÷ yra tiesin÷ „figūra“, ir pagal antrojo tipo gramatikų taisykles ją galima
pl÷sti tik į dvi puses, o trečiai raidei (pusei) neb÷ra priemonių.Taigi kalba L4 n÷ra antrojo tipo.
Belieka bandyti parašyti jai pirmojo tipo gramatiką. Tą jau esame padarę anksčiau (2.3 skyr. 4
pavyzdys, 2.6 skyr.).

Pratimas

47
1. Nustatykite šių kalbų tipą (visur m, n, p, s, k – natūralieji skaičiai).
1) ambncm;
2) abcn;
3) abcn, n < 100;
4) anbm, n \ 2 = 1, m \ 2 = 1;
5) anbm, n \ 2 = m \ 2;
6) anbn+1cn-1;
7) ambncpdsek;
8) akbmcndp k ≠ m, m ≠ n, n ≠ p;
9) akbmcndp k = m = n = p;
10) akbmcndp k<m<n<p
11) akbmcndp k + m + n + p < 100;
12) anbncndn n \ 10 = 1.

2.12. Bekaus ir Nauro forma

Tuo metu, kai N. Chomskis klasifikavo gramatikas, Dž. Bekus, dirbdamas grup÷je,
kuriančioje kalbą Algolą-58 (tai buvo Algolo-60 pirmtakas), gavo užduotį aprašyti šią kalbą.
Tarptautin÷je konferencijoje 1959 m. Dž. Bekus perskait÷ pranešimą, kuriame pristat÷ formalizuotą
žymenų sistemą Algolo-58 kalbos sintaksei aprašyti.
1960 metais buvo parengtas naujas Algolo variantas – Algolas-60. Jo sintaksę apraš÷ P.
Nauras, šiek tiek modifikavęs Dž. Bekaus pateiktąją schemą. Nuo to laiko šis metodas
programavimo kalbų sintaksei aprašyti vadinamas Bekaus ir Nauro forma, sutrumpintai – BNF.
BNF yra laisvo konteksto gramatikos taisyklių rinkinys. Taisykl÷s pateikiamos šiek tiek
kitokiu pavidalu, negu priimta gramatikose:
* vietoj rodykl÷s → rašomas simbolis: := (panašus į priskyrimo simbolį),
* neterminaliniai simboliai rašomi tarp ženklų < > (kartais vadinamų kampiniais
skliaustais).
Pavyzdžiui, antrojo tipo gramatikos taisykl÷
S → aSb | ab
būtų užrašoma šitaip:
<S>: := a <S> b | a b
Kampiniai skliaustai parodo pavadinimo pradžią ir pabaigą. Jais apskliaustas pavadinimas,
nežiūrint iš kiek raidžių arba žodžių būtų sudarytas, laikomas vienu neterminaliniu simboliu,
pavyzdžiui,
<kintamojo vardas>

48
Tai patogu aprašant programavimo kalbas, nes neterminalinius simbolius galima vadinti
prasmingais jais žymimų sąvokų vardais.
1 pavyzdys. Neneigiamas skaičius.
<skaitmuo>: := 0  1  2  3  4  5  6  7  8  9
<neneigiamas skaičius>: := <skaitmuo> | <neneigiamas skaičius> <skaitmuo>
2 pavyzdys. Vardas. Sudaromas iš raidžių ir skaitmenų, prasideda raide.
<vardas>: := <raid÷>
 <vardas> <raid÷>
 <vardas> <skaitmuo>
Kampiniai skliaustai padeda atskirti terminalinius simbolius nuo neterminalinių. Pavyzdžiui,
užrašas
<funkcija>
yra terminalinis simbolis, o tas pats nesuskliaustas žodis
funkcija
būtų suprantamas kaip terminalinis simbolis (pvz., bazinis žodis), arba terminalinių simbolių
seka (pvz., raidžių seka, sudaranti vardą).
D÷l to nereikia atskirai išvardyti neterminalinių ir terminalinių simbolių ab÷c÷lių – prireikus
juos galima išrinkti iš taisyklių. Pradinį simbolį taip pat galima vienareikšmiškai nustatyti iš
taisyklių susitarus, kad jis nebus panaudotas n÷ vienos taisykl÷s dešin÷je pus÷je. Taigi kalbos
gramatikai apibr÷žti pakanka vien BNF taisyklių (nereikia nurodyti pradinio neterminalinio
simbolio, neterminalinių simbolių ab÷c÷l÷s ir terminalinių simbolių ab÷c÷l÷s).
Pačią BNF taip pat galima laikyti kalba. Kadangi ji vartojama kitai – programavimo – kalbai
apibr÷žti, tai ją reik÷tų vadinti metakalba. Taisykl÷se vartojami simboliai
: := | < >
priklauso metakalbai, tod÷l jie vadinami metasimboliais. Metasimboliais taip pat laikomi
neterminaliniai simboliai, nes jie taip pat nepriklauso BNF apibr÷žiamai kalbai.
3 pavyzdys. 1 lentel÷je pateiktas nedidel÷s programavimo kalbos (pavadinkime ją LP – labai
paprasta) sintaks÷s aprašas Bekaus ir Nauro forma.

2.3 lentel÷. LP kalbos sintaks÷

1. <programa> : := program <vardas> ;


<aprašas> ;
begin
<sakinių seka>
end.
2. <aprašas>: := var <vardų sąrašas>: integer
3. <vardų sąrašas> := <vardas>
| <vardų sąrašas> , <vardas>

49
4. <sakinių seka>: := <sakinys>
| <sakinių seka> ;
<sakinys>
5. <sakinys> : := <priskyrimo sakinys>
| <sąlyginis sakinys>
| <ciklo sakinys>
| <skaitymo sakinys>
| <rašymo sakinys>
| <sud÷tinis sakinys>
6. <priskyrimo sakinys> : := <kintamojo vardas> := <reiškinys>
7. <kintamojo vardas> : := <vardas>
8. <sąlyginis sakinys> := if <sąlyga>
then <sakinys>
| if <sąlyga>
then <sakinys>
else <sakinys>
9. <ciklas sakinys> : := while <sąlyga> do
<sakinys>
10. <skaitymo sakinys>: := read (<kintamojo vardas>)
11. <rašymo sakinys>: := write (<reiškinys>)
12. <sud÷tinis sakinys> : := begin
<sakinių seka>
end
13. <sąlyga>: := <operandas> <lyginimo operatorius> <operandas>
14. <reiškinys> : := <termas>
| <reiškinys> + <termas>
| <reiškinys> - <termas>
15. <termas> : := <operandas>
| <termas> * <operandas>
16. <operandas>: := <skaičius>
| <kintamojo vardas>
| (<reiškinys>)
17. <lyginimo operacijos ženklas>: := < | <= | = | <> | > | >=
18. <vardas> : := <raid÷>
| <vardas> <raid÷>

50
| <vardas> <skaitmuo>
19. <skaičius> : := <skaitmuo>
| <skaičius> <skaitmuo>
20. <raid÷> : := a | ą | b | c | d | e | ę | ÷ | f | g | h | i | į | y| j | k | l
|m|n|o|p|q|r|s|š|t|u|ų|ū|v|w|x|z|ž
21. <skaitmuo>: := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Pastaba.Taisyklių numeriai nepriklauso BNF. Sunumeravome tam, kad taisykles būtų


patogiau įvardyti.
Tai labai paprasta kalba, turinti tik 21 taisyklę. Realių programavimo kalbų (pvz., Paskalio,
Modulos) sintaksei aprašyti reikia apie šimtą – du šimtus taisyklių.
Programavimo kalbos konstrukcijų, kurias priimta išd÷styti į kelias eilutes (visos programos,
sud÷tinio sakinio, ciklo sakinio, sąlyginio sakinio) taisyklių dešiniąsias puses d÷st÷me į eilutes ir
lygiavome taip, kaip priimta programose.
7 taisykl÷ yra neproduktyvi, kadangi jos dešinę pusę sudaro tik vienas neterminalinis
simbolis, o vieno neterminalinio simbolio keitimas kitu nieko naujo neduoda. Jeigu 6, 10 ir 16
taisykl÷se vietoj simbolio <kintamojo vardas> įrašytume simbolį <vardas>, 7 taisyklę būtų galima
pašalinti. Sintaks÷s požiūriu neterminaliniai simboliai <kintamojo vardas> ir <vardas> yra
sinonimai. Tačiau jie turi skirtingą prasmę (semantiką). D÷l to jie duoda papildomos informacijos
skaitytojui: tuo pasakoma, kad, pavyzdžiui, priskyrimo sakinio kair÷je pus÷je gali būti tik
kintamojo, o ne kokio nors kito objekto (pvz., programos) vardas. Ši informacija naudinga ir rašant
transliatorių (žr. … skyr).
Apraše (2 ir 3 taisykl÷s) aprašomi kintamieji. Tačiau čia vartojome neterminalinį simbolį
<vardas>, o ne <kintamojo vardas>. Kod÷l?
Taip dar÷me d÷l to, kad kai <vardas> yra ką tik „pagamintas“, bet dar neaprašytas, tai dar
negalima pasakyti, kokį objektą jis žym÷s. Aprašas jam suteikia kintamojo vardo statusą. Tod÷l
vardą aprašančių taisyklių konstrukcijose logiška vartoti vardo sąvoką bendrąja prasme. Kitose
konstrukcijose vartojami jau aprašyti vardai. Taigi ten tikslinga vartoti konkretesnę sąvoką.
BNF yra generuojanti gramatika. Tod÷l ja patogu naudotis rašant programą. Tačiau ja galima
pasinaudoti ir tikrinant jau parašytą programą. Tokiu atveju reikia atrasti kelią (taisyklių taikymo
tvarką) kuriuo einant buvo gauta programa. Paaiškinsime, kaip tai galima padaryti. Tarkime, kad
turime šitokią LP kalbos programą:
program p;
var a, b: integer;
begin
read (a);
read (b);
if (a < b) then a := a+b;
write (a)
end.

51
Visą LP kalbos programą apibr÷žia 1 taisykl÷ (žr. 1 lentelę). Tod÷l norint patikrinti, ar šis
tekstas iš tikrųjų yra kalbos LP programa, reikia imti pirmąją taisyklę, apibr÷žiančią pagrindinį
neterminalinį simbolį <programa>, ir bandyti jos dešin÷je pus÷je esančius neterminalinius simbolius
pakeisti jų apibr÷žtimis. Keisti reikia tol, kol neliks neterminalinių simbolių. Jeigu rasime pakeitimų
(eilučių generavimo) kelią tokį, kad gautum tikrinamą programą, tai tada gal÷sime tvirtinti, kad
duotas tekstas yra kalbos LP programa (priklauso kalbai LP). Taigi, imame pirmąją taisyklę:
<programa> ⇒
program <vardas> ;
<aprašas> ;
begin
<sakinių seka>
end. ⇒
Toliau galime „išskleisti“ bet kurį neterminalinį simbolį. Prad÷kime nuo pirmosios eilut÷s,
kurioje yra tik vienas neterminalinis simbolis <vardas>. Vardo taisykl÷ turi dvi alternatyvas. Kurią
pasirinkti?
Rezultatas bus absoliučiai patikimas, jeigu sistemingai išbandysime visus kelius, taip, kaip
daroma pilno perrinkimo algoritmuose. Taigi imkime pirmąją alternatyvą. Jei v÷liau pasirodytų, kad
ji netinkama, reik÷s grįžti atgal ir bandyti antrą alternatyvą. Jeigu ir ši pasirodys netinkama, tai tai
tada reikia bandyti trečią ir t. t. Jeigu pasirodys, kad netinka n÷ viena alternatyva, reik÷s grįžti
medžiu aukštyn. Jeigu ir čia buvo paskutin÷ alternatyva, v÷l kilti į viršų. Jeigu nebebus kur kilti, tai
reikš, kad gavome neigiamą rezultatą – eilut÷ nepriklauso kalbai.
program <raid÷>;
<aprašas>
begin
… ⇒
Raid÷s taisykl÷ turi daugybę alternatyvų. Tačiau kiekviena alternatyva – terminalinis
simbolis. Taigi, tuo pačiu ir kelio pabaiga. Tod÷l iš karto galime pasirinkti tinkamą alternatyvą –
šiuo atveju raidę p.
program p;
<aprašas> ;
begin… ⇒
Dabar eil÷ neterminaliniam simboliui <aprašas>.
program p;
var <vardų sąrašas>: integer; ⇒

Vardų sąrašo apibr÷žtyje yra dvi alternatyvos. Imame pirmąją.

program p;
var <vardas>: integer; ⇒

52
program p;
var <raid÷>: integer; ⇒

program p;
var a: integer; ⇒

Antroje eilut÷je neterminalinių simbolių neb÷ra. Bet ji nesutampa su tikrinamos programos


eilute. Kadangi pirmose dviejose eilut÷se neterminalinių simbolių neb÷ra, tai ką toliau bedarytume,
šios eilut÷s (t. y., programos pradžia) nebesikeis. Vadinasi, arba neteisinga programa arba
pasirinkdami alternatyvas, nu÷jome neteisingu keliu.
Kai nagrin÷jome neterminalinį simbolį <vardų sąrašas>, liko neišbandyta antroji alternatyva.
Gal ji tiks. Reikia grįžti ir bandyti ją. Paliekame tai padaryti skaitytojui, primindami, kad kelias iki
s÷kmingos pabaigos dar ne taip arti...
Šiuo atveju pabaiga išties bus s÷kminga ir iš to bus galima padaryti išvadą, kad tikrinamoji
programa yra teisinga.
Jeigu išbandžius visas alternatyvas negautume tinkamo atsakymo, tada būtų galima tvirtinti,
kad programa neteisinga. Tai ilgas darbas. Stengiamasi jį suprastinti.
Programuotojui – kalbos vartotojui retai kada tenka tikrinti programos teisingumą su BNF
arba kitokiais sintaks÷s aprašais, nes šį darbą puikiai atlieka transliatorius. Sintaks÷s aprašo tenka
griebtis tada, kai reikia išsiaiškinti painesnes programavimo kalbos vietas arba tikrinti paties
transliatoriaus teisingumą. Dažniau prireikia tikrinti ne visos programos, o tam tikrų jos
konstrukcijų teisingumą.
Programavimo kalbų bei transliatorių kūr÷jams su formaliu programų tikrinimu tenka
susidurti nuolat.
Pasteb÷sime, kad BNF aprašo laisvo konteksto gramatiką. Tod÷l pagal ją negalima nustatyti,
ar visi vardai aprašyti, ar tas pats vardas n÷ra aprašytas du kartus, ar reiškiniuose vartojamos
kintamųjų reikšm÷s yra apibr÷žtos ir jų tipai suderinti ir pan. Tod÷l, pavyzdžiui, sintaksiškai bus
teisinga ir žemiau pateikta LP programa, nors joje ir yra keletas min÷tos rūšies klaidų.
program x;
var x, y, x: integer;
begin
write(a);
write(y);
read(alfa)
end.
Tokias klaidas galima rasti formaliai nagrin÷jant tik programos tekstą ir neatliekant joje
aprašytų veiksmų. D÷l to jas der÷tų laikyti sintaks÷s klaidomis. Tačiau jų negalima rasti
pasinaudojant formaliu mechanizmu – BNF. O negalima rasti d÷l to, kad šis mechanizmas per
silpnas. Čia reik÷tų vartoti kontekstinę (1 tipo) gramatiką. Bet ji per sud÷tinga ir programavime
beveik nevartojama. Transliatorius šias klaidas randa. Tačiau kitais, mažiau formalizuotais,

53
metodais. Tod÷l ir n÷ra vieningos nuomon÷s, kam priskirti vardų aprašų, jų galiojimo sričių,
duomenų tipų suderinamumo taisykles bei jų kontrolę: sintaksei ar semantikai.

2.13. Bekaus ir Nauro formos modifikacijos

BNF metakalba užrašytų taisyklių struktūra yra tokia pati, kaip ir matematiniai gramatikos
taisyklių užrašai. Skiriasi tik žymenys, kurie yra daugiau priartinti prie programavimo praktikos.
V÷liau atsirado BNF modifikacijų, kuriose šiek tiek pakeista ir taisyklių struktūra. Naujomis
išraiškos priemon÷mis papildyta BNF dažnai vadinama išpl÷stąja BNF. Joje yra numatytas taisykl÷s
dešin÷s pus÷s simbolių grupavimas panaudojant skliaustus.
Simbolių praleidimas. Laužtiniuose skliaustuose [ ] esanti simbolių seka gali būti praleista.
Tod÷l, pavyzdžiui, LP sąlyginį sakinį galima apibr÷žti trumpiau:
<sąlyginis sakinys> : := if <sąlyga>
then <sakinys>
[else <sakinys>]
Šitaip alternatyvos, besiskiriančios tuo, kad vienoje jų kuri nors simbolių seka yra, o kitoje jos
n÷ra, sujungiamos į vieną šaką.
Toks užrašas ne tik vaizdesnis, bet ir palengvina apibr÷žiamos konstrukcijos analizę: bendrą
kelioms alternatyvoms priklausiusią dalį dabar pakanka analizuoti vieną kartą.
Pasteb÷sime, kad nebūtinos dalies suskliaudimas į laužtinius skliaustus yra tapęs visuotinai
priimta norma programavime. Pavyzdžiui, šitaip operacin÷s sistemos komandų aprašuose žymimi
nebūtini parametrai.
Simbolių grupavimas. Simbolių grup÷ suskliaudžiama į paprastuosius skliaustus ( ). Šitaip
galima alternatyviai nurodyti ne tik visą dešinę taisykl÷s pusę, bet ir jos dalį. Pavyzdžiui, skaičius,
būtinai turintis ženklą, gali būti apibr÷žiamas šitaip:
<skaičius su ženklu>: := (+  -) <skaičius>
Simbolių grupių kartojimas. Simbolių seka, kurią galima kartoti, rašoma į figūrinius
skliaustus { }. Kartojimų skaičius traktuojamas dvejopai:
• laikoma, kad gali būti kartojama nulį arba daugiau kartų;
• laikoma, kad gali būti kartojama vieną arba daugiau kartų.
Tarkime, kad laikom÷s pirmojo susitarimo. Tada, pavyzdžiui LP kalbos vardą ir vardų sąrašą
gal÷tume apibr÷žti šitaip:
<vardas>: := <raid÷> {<raid÷>  <skaitmuo>}
<vardų sąrašas>: := <vardas> {, <vardas>}
Jeigu laikytum÷s antrojo susitarimo, tai šias konstrukcijas reik÷tų apibr÷žti šitaip:
<vardas>: := <raid÷> [{<raid÷>  <skaitmuo>}]
<vardų sąrašas>: := <vardas> [{, <vardas>}]

54
Kartojimas programavime išreiškiamas ciklu arba rekursija. BNF turi tik rekursiją. Išpl÷stin÷
BNF turi ir ciklams analogišką išraiškos priemonę, kurią duoda figūriniais skliaustai. Tur÷dami dvi
skirtingas išraiškos priemones galime visada pasirinkti konkrečiam atvejui tinkamesnę.
2.4 lentel÷je pateikta LP kalbos sintaks÷, užrašyta išpl÷stine BNF. Joje neb÷ra taisyklių
<vardų sąrašas> ir <sakinių seką>. Jomis išreiškiamas apibr÷žtis tiesiogiai įraš÷me į taisykles
<aprašas> ir <sud÷tinis sakinys>. Taip buvo galima padaryti d÷l to, kad panaudojus grupavimo
skliaustus, buvo pašalinta rekursija iš taisyklių <vardų sąrašas> ir <sakinių seka>.

2.4 lentel÷. LP kalbos sintaks÷, užrašyta išpl÷stine BNF

1. <programa> : := program <vardas> ;


<aprašas> ;
begin
<sakinių seka>
end.
2. <aprašas> : := var <vardas> {, <vardas>}: integer
5. <sakinys> : := <priskyrimo sakinys>
| <sąlyginis sakinys>
| <ciklo sakinys>
| <skaitymo sakinys>
| <rašymo sakinys>
| <sud÷tinis sakinys>
6. <priskyrimo sakinys> : := <kintamojo vardas> := <reiškinys>
7. <kintamojo vardas> : := <vardas>
8. <sąlyginis sakinys> : := if <sąlyga>
then <sakinys>
[else <sakinys>]
9 <ciklas sakinys> : := while <sąlyga> do
<sakinys>
10. <skaitymo sakinys> : := read (<kintamojo vardas>)
11. <rašymo sakinys> : := write (<reiškinys>)
12 <sud÷tinis sakinys> := begin
<sakinys>
{; <sakinys>}
end
13. <sąlyga> : := <operandas> <lyginimo operatorius> <operandas>
14. <reiškinys> : := <termas> {(+ | -) <termas>}

55
15. <termas> : := <operandas> {* <operandas>}
16. <operandas> : := <skaičius>
|<kintamojo vardas>
|(<reiškinys>)
17. <lyginimo operacijos ženklas>: := < | <= | = | <> | > | >=
18. <vardas> : := <raid÷> {<raid÷> | <skaitmuo>}
19. <skaičius> : := <skaitmuo> {<skaitmuo>}
20. <raid÷> : := a | ą | b | c | d | e | ę | ÷ | f | g | h | i | į | y| j | k | l
|m | n | o | p | q | r | s | š | t | u | ų | ū | v | w | x | z | ž
21. <skaitmuo>: := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Taisykl÷s numeruojamos tokiais pat numeriais, kaip ir ankstesn÷je lentel÷je. Tod÷l numeriai
taisyklių, kurių neb÷ra išpl÷stin÷je BNF, yra praleisti.
Išpl÷stin÷je BNF vartojami trijų rūšių skliaustai yra metasimboliai. Programavimo kalbose
skliaustai vartojami ir kaip terminaliniai simboliai. Kaip juos atskirti?
Paprasčiausias būdas – vartoti skirtingus šriftus. Čia mes taip, ir dar÷me, skliaustus –
metasimbolius pateikdami pusjuodžiu šriftu.
Yra ir kitų BNF išpl÷stin÷s formos modifikacijų. Pavyzdžiui, neterminaliniai simboliai į
kampinius skliaustus neskliaudžiami, o jeigu jų pavadinimai sudaryti iš kelių žodžių, tai tie žodžiai
sujungiami brūkšneliais, pavyzdžiui,
vardų-sąrašas
Tokiu atveju reikia kaip nors atskirti terminalinius simbolius, išreiškiamus žodžiais (bazinius
žodžius) nuo neterminalinių. D÷l to baziniai žodžiai rašomi pusjuodžiu šriftu arba visi terminaliniai
simboliai rašomi tarp kabučių. Antrasis būdas, nors ir netoks vaizdus, yra universalesnis, nes tinka
ir tada, kai n÷ra galimyb÷s vartoti skirtingus šriftus. Pastarasis būdas dažniausiai vartojamas
oficialiuose dokumentuose, pavyzdžiui standartuose, kur skirtingos simbolių interpretacijos gali
tur÷ti labai neigiamas pasekmes.

2.14. Sintaks÷s diagramos

Dar vaizdesnis būdas programavimo kalbų sintaksei aprašyti – sintaks÷s diagramos.


Kiekviena sintaks÷s taisykl÷ vaizduojama diagrama. Diagramos viršuje rašomas jos pavadinimas –
taisykl÷s kairioji pus÷. Dešin÷s pus÷s simboliai rašomi į geometrines figūras: neterminaliniai į
stačiakampius, o terminaliniai į apskritimus arba ovalus. Figūros sujungiamos rodykl÷mis taip, kad
figūrų ap÷jimas rodyklių kryptimi duotų visas galimas simbolių sekas, kurias galima gauti iš
apibr÷žiamo simbolio.
Kaip diagramoje vaizduojami atskiri išpl÷stin÷s BNF komponentai, parodyta 2.16 paveiksle.
Ženklas | reiškia alternatyvą, tod÷l diagramoje pakeičiamas išsišakojimu, ženklai { } –
kartojimą, tod÷l pakeičiami atgal grįžtančiomis rodykl÷mis (sudarančiomis ciklą), ženklai [ ] –
nebūtiną simbolių seką, tod÷l pakeičiami rodykle, aplenkiančia konstrukciją „tiesiu keliu“.

56
2.19 pav. Išpl÷stin÷s BNF konstrukcijų, pavaizdavimas sintaks÷s diagramomis

Kalbos LP (4 lentel÷) sintaks÷s diagramos pateiktos 2.20 paveiksle.


Čia diagramų yra mažiau, negu taisyklių, nes kai kurias taisykles sujung÷me ir pavaizdavome
viena diagrama.
Sintaks÷s diagramos vaizdesn÷s, negu rašytin÷s BNF taisykl÷s. Tod÷l jas m÷gstama vartoti
programavimo kalbų vadov÷liuose.
Diagramoje panaudoti sinonimai: vardas, kintamojo vardas.

57
58
59
2.20 pav. Kalbos LP sintaks÷s diagramos

Pratimai

1. Raskite sintaks÷s klaidas šiose LP programose


program;
var a, b: integer;
c: integer;
begin
read (a, b);
if a < b + 1

60
then c := -a*b;
write (c-1);
end;

program kita;
var z: integer;
begin
z := z + 1;
write ((z));
while z < 5 do
read (a);
write (a)
end.

2. Kurios iš čia pateiktų Paskalio kalbos programų neatitinka standartinių Paskalio sintaks÷s
diagramų:
a) program p1 (input, output);
var r: real;
i: integer;
begin
r := i;
c := i + r
end.
b) program p2 (input, output);
var b: boolean;
i: integer;
begin
i := 0;
b := not i
end.
c) program p3 (input, output);
var m: array[1..10] of file of 5...2;
begin end.
Nurodykite sintaksiškai klaidingas vietas tekste.

61
3. LEKSIKA

3.1. Kompiuterio ab÷c÷l÷

Programavimo kalbose susiduriame su dviem ab÷c÷l÷mis: kompiuterio ab÷c÷le ir


programavimo kalbos ab÷c÷le.
Kompiuterių elektronin÷s schemos yra dvejetain÷s – vienas elementas turi dvi būsenas.
Taigi tikroji kompiuterio aparatin÷s dalies ab÷c÷l÷ turi tik du ženklus: 0 ir 1.
Žmogus operuoja su kur kas didesniu ženklų skaičiumi – raid÷mis, skaitmenimis ir daugybe
kitokių ženklų.
Skirtingas ženklų aibes suderina operacin÷ sistema. Žmogaus vartojami ženklai koduojami
dvejetainiais ženklais. Dabar daugiausia vartojami kompiuteriai, kuriuose vienam ženklui skiriamas
vienas baitas, t. y. 8 bitai. Šitaip vienu baitu galima užkoduoti 28 = 256 ženklus, t. y. ab÷c÷lę,
turinčią 256 ženklus. Naujesniuose kompiuteriuose vienam ženklui skiriami du baitai ir jie
koduojami Unikodu. Tada kompiuterio ab÷c÷lę sudaro 216 = 65536 ženklai.Žmogaus vartojami
ženklai koduojami keliais dvejetainiais ženklais.
Reikia skirti vidinę ir išorinę kompiuterio ab÷c÷lę. Vidinę kompiuterio ab÷c÷lę sudaro visos
galimos kodų, skirtų vienam ženklui, kombinacijos. Išorin÷ kompiuterio ab÷c÷l÷ yra vidin÷s
ab÷c÷l÷s poaibis. Į jį įeina tik tie vidin÷s ab÷c÷l÷s ženklai, kuriuos galima pavaizduoti kompiuterio
išor÷je – išspausdinti, parodyti ekrane. Kiti ženklai yra valdymo ženklai. Jie skirti kompiuterio
valdymui ir neturi grafinio pavaizdavimo.
Tarpas, tabuliavimo ženklas, eilučių skirtukai taip pat neturi grafinio pavaizdavimo. Tačiau
jų vieta tekste yra matoma. Tod÷l jie laikomi išorin÷s ab÷c÷l÷s (teksto) ženklais.
Programose kompiuterio ab÷c÷l÷s ženklai vartojami duomenims (tekstams skaičiams)
užrašyti. Programose, kuriose sprendžiami įprasti taikomieji uždaviniai paprastai operoperuojama
tik su išorin÷s ab÷c÷l÷s ženklais.
Programose, tvarkančiose paties kompiuterio veikimą (pvz., operacin÷je sistemoje, įtaisų
tvarkykl÷se), dažnai vadinamomis sistemin÷mis programomis, tenka operuoti ir su valdymo
ženklais, t. y. su išorine kompiuterio ab÷c÷le. Tod÷l tokiems uždaviniams skirtose programose
reikia kokiu nors būdu užrašyti ir pavaizdavimo neturinčius valdymo ženklus. Jie paprastai
užrašomi sutartu būdu keliais išorin÷s kompiuterio ab÷c÷l÷s ženklais. Pavyzdžiui, programavimo
kalboje C bet kurį vidin÷s kompiuterio ab÷c÷l÷s ženklą galima pavaizduoti kairiniu brūkšniu \ ir po
jo einančiu aštuonetainiu (pvz., '\013') arba šešioliktainiu (pvz., '\xb') to ženklo kodu.

3.2. Programavimo kalbos ab÷c÷l÷

Programavimo kalbos ab÷c÷lę sudaro tos kalbos terminaliniai simboliai. Kadangi jais
užrašyta programa turi būti pateikiama kompiuteriui, tai reikia, kad kiekvieną terminalinį simbolį
būtų galima išreikšti išorin÷s kompiuterio ab÷c÷l÷s ženklais.
Šiuolaikiniai kompiuteriai turi turtingą ab÷c÷lę. Tačiau programavimo kalbose stengiamasi
naudoti tik nedidelę jos dalį, nes daugelis programavimo kalbų buvo sukurtos dar tais laikais, kai

62
kompiuteriuose buvo naudojamos 7 ar 6 bitų koduot÷s su labai skurdžia ab÷c÷le. Be to, kuriant
naujas programavimo buvo stengiamasi, kad jos tiktų kuo įvairesniems kompiuteriams, tarp jų ir
anksčiau pagamintiems.
Norint didesnę terminalinių simbolių aibę pavaizduoti mažesne kompiuterio ženklų aibe,
dalis terminalinių simbolių užrašomi keliais kompiuterio ab÷c÷l÷s simboliais. Tai baziniai žodžiai,
sudaryti iš raidžių (pvz., begin, end) arba ženklų poros (pvz., :=, <>).
Abstrakti terminalinių simbolių aib÷. Vieno terminalinio simbolio pavaizdavimas keliais
kompiuterio ab÷c÷l÷s simboliais sukelia nepatogumų aprašant kalbą. Nepatogumų būna ir kai tas
pats kompiuterio ab÷c÷l÷s simbolis naudojamas vietoj kelių terminalinių simbolių.
Pavyzdžiui, Paskalio kalboje taškas atlieka tris funkcijas: 1) skiria skaičiaus trupmeninę dalį
nuo sveikosios, 2) skiria sud÷tinio vardo komponentus ir 3) yra programos pabaigos ženklas.
Daugelyje primityvesnių kalbų tuo pačiu lygyb÷s ženklu žymima lygyb÷s operacija ir priskyrimo
operacija. Tokiais atvejais tas pats ženklas turi skirtingą semantiką, tod÷l kalbos gramatikoje būtų
geriau tur÷ti po skirtingą terminalinį simbolį. D÷l to siekiant didesnio griežtumo gali būti apibr÷žta
abstrakti, t. y. nuo pavaizdavimo nepriklausanti terminalinių simbolių aib÷. Toks modelis yra
pateiktas Algolo-68 oficialiame aprašyme. Ten terminaliniai simboliai vadinami vardais.
Pavyzdžiui, vietoj min÷to Paskalio kalbos taško būtų trys abstraktūs terminaliniai simboliai:
<Trupmenin÷s skaičiaus dalies skirtuko simbolis>
<Sud÷tinio vardo skirtuko simbolis>
<Programos pabaigos simbolis>
Tam, kad terminaliniai simboliai skirtųsi nuo analogiškai vadinamų ir žymimų
neterminalinių simbolių, reikia, kad terminalinių simbolių pavadinimai tur÷tų tam tikrą požymį,
pavyzdžiui, baigtųsi vienodu žodžiu – pavyzdžiui, žodžiu „simbolis“.
Kaip vaizduojamas kiekvienas neterminalinis simbolis pateikiama atskiroje kalbos
apibr÷žimo dalyje, kuri yra priklausoma nuo kalbos realizacijos. O tai suteikia papildomą lankstumą
kalbai. Pavyzdžiui, simbolis <Trupmenin÷s skaičiaus dalies skirtuko simbolis> realizacijoje,
skirtoje JAV, gali būti žymimas tašku, o realizacijoje, skirtoje Europai – kableliu (,).
Tuo pačiu konkrečiu simboliu vaizduoti kelis abstrakčius terminalinius simbolius galima tik
tuo atveju, jeigu pagal kontekstą galima vienareikšmiškai atstatyti abstrakčius simbolius iš
konkrečių simbolių.

3.3. Terminalinių simbolių pavaizdavimas kompiuterio ab÷c÷l÷s ženklais

Yra keletas kompiuterio ab÷c÷l÷s simbolių, kurie turi tą pačią prasmę, kaip ir programavime
(matematikoje). Tai aritmetinių operacijų ženklai ( + – / < > = ), skliaustai ( [ ] ( ) { } ),
skyrybos ženklai ( ⋅ , ; : ). Jie vartojami praktiškai visose programavimo kalbose. Daugybos ženklo
× nebuvo visuotinai paplitusioje 7 bitų koduot÷je ASCII. Tod÷l beveik visose programavimo
kalbose daugyba žymima žvaigždute.
Operacijos, kurių ženklų n÷ra ASCII kodų lentel÷je, dažniausiai žymimos ženklų poromis
arba baziniais žodžiais (3.1 lentel÷). N÷ra visuotino susitarimo, kokiais ženklais kokią operaciją
žym÷ti ir matome didelę įvairovę.

63
3.1 lentel÷
Simbolis Fortranas Algolas- Algolas- Paskalis Ada C++ Java
60 68
× * × × * * * *
← = := := := := = =
≤ .LE. ≤ ≤ <= <= >= >=
≥ .GE. ≥ ≥ >= >= >= >=
= .EQ. = = = = == ==
≠ .NE. ≠ ≠ <> /= != !=
¬ .NOT. ¬ ¬ not not
∧ .AND. ∧ ∧ and and & &
∨ .OR. ∨ ∨ or or | |

Baziniai žodžiai vartojami operacijoms bei kitoms programų konstrukcijoms žym÷ti.


Baziniai žodžiai gali būti paprasti ir rezervuoti. Žodžio rezervavimas reiškia, kad žodis
visada turi tą pačią, programavimo kalboje nustatytą prasmę, nežiūrint kurioje programos teksto
vietoje jis būtų parašytas. Kitiems tikslams jo vartoti negalima. Taigi negalima vartoti vardų,
sutampančių su rezervuotais baziniais žodžiais, nes tada toks žodis visada bus laikomas baziniu.
Kai baziniai žodžiai nerezervuoti, jų paskirtis nustatoma iš konteksto: jeigu žodis yra tokioje
programos teksto vietoje, kur tur÷tų būti bazinis žodis, tai jis ir laikomas baziniu žodžiu, o jeigu jis
yra tokioje vietoje, kur tur÷tų būti vardas, tai laikomas vardu. Pavyzdžiui, Fortrano kalbos baziniai
žodžiai nerezervuoti. Tod÷l jeigu parašysime
IF A < B ...
tai žodis IF bus laikomas baziniu žodžiu, nes tokioje programos vietoje vardo netur÷tų būti.
Bet jeigu parašysime
IF = 20
tai žodis IF bus laikomas vardu, nes priskyrimo ženklo kair÷je (Fortrane priskyrimas žymimas
lygybe) gali būti tik vardas.
Programavimo kalbas su nerezervuotais žodžiais projektuoti sunkiau, nes reikia numatyti
visas galimas situacijas, kad vienareikšmiškai būtų galima nustatyti, kur tas pats žodis atlieka
bazinio žodžio ir kur vardo funkcijas.
Yra dar viena galimyb÷ baziniams žodžiams atpažinti: rašyti prieš jų vardus specialų
kompiuterio ab÷c÷l÷s ženklą, pavyzdžiui, # . Bet tai n÷ra patogu ir programas, primargintas vienodų
specialių ženklų, skaityti sunkiau. D÷l to šis metodas nepaplito.
Jeigu baziniai žodžiai rezervuoti, tai programą lengviau skaityti, nes n÷ra pavojaus
supainioti bazinius žodžius su taip pat pavadintais kintamaisiais. Pavyzdžiui, PL/I kalboje,
turinčioje bazinius žodžius IF, THEN ir ELSE, galimas šitoks sakinys:
IF IF = THEN THEN THEN = ELSE;

64
Netaip lengva nustatyti, kuris iš čia parašytų žodžių atlieka bazinio žodžio funkciją ir kuris
kintamojo vardo. Vietoj atsakymo tą patį sakinį perrašysime paryškindami bazinius (nerezervuotus)
žodžius:
IF IF = THEN THEN THEN = ELSE;
Tačiau kai baziniai žodžiai nerezervuoti, programą lengviau rašyti: nebūtina prisiminti visus
bazinius (ir tuos, kurie retai vartojami).
Be to kai norima patobulinti egzistuojančią kalbą išplečiant jos galimybes papildant ją
naujais baziniais žodžiais, susiduriame su problemomis. Jei prid÷sime naujų rezervuotų žodžių,
anksčiau sudarytos programos gali nebetikti, nes jose gali būti pavartotų vardų, kurie sutampa su
naujais rezervuotais žodžiais.
Taigi, yra argumentų už ir prieš bazinių žodžių rezervavimą. Vienose programavimo
kalbose daro vienaip, kitose – kitaip. Pavyzdžiui, Ada ir Paskalis turi rezervuotus bazinius žodžius,
o PL/I – ne.
Rezervuotų žodžių programavimo kalboje būna keletas dešimčių. Pavyzdžiui, Paskalio
kalboje jų yra 35: and, array, begin, case, const, div, do, downto, else, end, file, for, function, goto,
if, in, label, mod, nil, not, of, or, packed, procedure, program, record, repeat, set, then, type, to,
until, var, while, with.
Norint pabr÷žti išskirtinį bazinių žodžių statusą, programų tekstuose jie dažniausiai rašomi
pusjuodžiu šriftu (Algole-68, Paskalyje). Tačiau tas paryškinimas galioja tik žmogui – programos
skaitytojui. Transliatorius paryškinimnus ignoruoja.
Baziniai žodžiai dažniausiai būna angliški arba angliškų žodžių santrumpos. Profesionalių
programavimo kalbų baziniai žodžiai į kitas kalbas dažniausiai neverčiami. Kai buvo kuriamos
pirmosios programavimo kalbos, buvo manoma, kad vienodi angliški baziniai žodžiai, žymintis
tuos pačius objektus įvairiose programavimo kalbose, palengvins keitimasi informacija tarp įvairių
tautų programuotojų, t. y. atliks analogišką vaidmenį, kaip lotynų kalba medicinoje arba biologijoje.
Deja kartu su naujomis kalbomis atsirasdavo ir nauji žodžiai, ir tik keletas žodžių (do, else, false,
for, if, true, while) išsilaik÷ vienodi daugelyje kalbų [Grigas 2000]
Baziniai žodžiai verčiami, kai programavimo kalbą ima vartoti ne vien programuotojai ir jos
vartotojų skaičius išauga (pvz., Kobolas) arba kai programavimo kalba skirta mokymo tikslams
(pvz., Logo, Paskalis).
Daugelis valstybių mokymo tikslams vartoja savas programavimo kalbas, kuriose originalūs
baziniai žodžiai yra rašomi tų valstybių kalbomis.
Teigiama, paprastai neverčiami, išskyrus atvejus, kai toks mechanizmas numatytas
programavimo kalboje (pvz., Algole-68).
Leksikos analiz÷. Kai programavimo kalba turi terminalinių simbolių, koduojamų keliais
kompiuterio ab÷c÷l÷s simboliais (o taip būna kiekvienoje kalboje), prieš atliekant programos
transliavimą (konkrečiau – sintaks÷s analizę), reikia kompiuterio simbolių sekas paversti
terminaliniais simboliais. Šis keitimas vadinamas leksikos analize. Tada programa, pavaizduota
kompiuterio ab÷c÷l÷s simbolių seka pakeičiama programa, pavaizduota programavimo kalbos
terminalinių simbolių seką. Terminaliniai simboliai dar vadinami leksemomis.
Vardų, skaičių ir kitų konstantų sandara panaši terminalinių simbolių, išreikštų keliais
kompiuterio ab÷c÷l÷s simboliais, sandarą. Panašus ir jų atpažinimas. Visi jie apibr÷žiami trečiojo
tipo gramatikomis. Tod÷l leksikos analizatoriai beveik visada „susidoroja“ ir su vardais, skaičiais ir
kitomis konstantomis ir jie visi vadinami leksemomis. Visi vardai paprastai laikomi ta pačia (taip

65
pat pavadinta) leksema, pavyzdžiui, „vardas“, visi sveikieji skaičiai – kita leksema ir t. t.
Konkrečias leksemų reikšmes (vardus, skaičius) analizatorius įsimena atskirai.
Leksin÷s analiz÷s programoje kalbos leksemų sąrašas aprašomas vardiniu duomenų tipu,
kurio reikšm÷s yra leksemų pavadinimai.
3 pavyzdys. LP kalbos leksemų duomenų tipas gali būti aprašytas taip:
type leksema = (programsim, beginsim, endsim, varsim, integersim, ifsim, thensim, elsesim,
whilesim, dosim, readsim, writesim, pliusas, minusas, daugyba, dalyba, kskl, dskl, mažiau,
mažiaulygu, lygu, nelygu, daugiau, daugiaulygu, priskyrimas, kbl, tšk, kbltšk, dvitšk);
Baziniais žodžiais žymimu leksemų vardus sudar÷me prie bazinių žodžių prid÷dami galūnę -
sim tam, kad konstantų (leksemų) vardai skirtųsi nuo bazinių žodžių, kuriuos kalboje, aprašančioje
šį duomenų tipą, laikome rezervuotais.

Pratimai
1. Užrašykite leksemų sąrašu šitokią LP kalbos programą
program programa;
var pirmas, antras, rezultatas: integer;
begin
read(pirmas);
if pirmas <= 0 then rezultatas := 0
else rezultatas := duomuo - 1;
write(rezultats)
end.

3.4. Vardai

Vardais vienareikšmiškai identifikuojami kintamieji, konstantos, funkcijos, procedūros ir


kiti programoje vartojami objektai. Vardai sudaromi iš raidžių ir skaitmenų. Paprastai laikomasi
taisykl÷s, jog vardas turi prasid÷ti raide. Kai kuriose kalbose raidžių statusą turi ir vienas kitas
specialios paskirties neturintis simbolis, pavyzdžiui, pabraukimo brūkšnys _. Šis simbolis į
kompiuterio ab÷c÷lę pateko iš rašomosios mašin÷l÷s. Jis buvo vartojamas tekstui pabraukti. Pagal
šią paskirtį kompiuteriuose jis nebevartojamas. Tod÷l, kaip neturintį kitos paskirties, jį imta vartoti
varduose, nes jis vaizdžiai skiria vardo dalis.
Vis tik svarbiausi vardų komponentai yra raid÷s. Tod÷l apie jas pakalb÷sime daugiau. Šiame
skyrelyje kalb÷sime tik apie raides, vartojamas varduose. Jos dar gali būti vartojamos simbolių
eilut÷se (žr.??? skyr.).
Programavimo kalbų semantika neapibr÷žia raidžių prasm÷s. Raid÷s tarnauja kaip „statybin÷
madžiaga“ vardams sudaryti. Vardai jau turi semantiką. D÷l to raidžių aib÷ programavimo kalbai
n÷ra svarbi, ir pasirenkama tokia, kokią galima pateikti kompiuteriai.

66
Tais laikais, kai buvo kuriamos pirmosios programavimo kalbos, kompiuteriuose vienam
simboliui koduoti buvo skiriami tik 6 bitai, t. y. 26 = 64 pozicijų. Į tokią mažą kompiuterio ab÷c÷lę
tilpo tik vieno lygio (tik didžiosios arba tik mažosios) raid÷s. Dabar tokių kompiuterių neb÷ra,
tačiau 6 bitų kodavimo reliktų dar galima matyti daugelyje programavimo kalbų, kuriose didžiosios
raid÷s laikomos lygiavert÷mis mažosioms. Pavyzdžiui, vardai
dydis
DYDIS
Dydis
Algole-60, Algole-68, Adoje, Paskalyje ir daugelyje kitų kalbų laikomi sutampančiais.
V÷lesn÷se programavimo kalbose (pvz., C, Moduloje, Oberone), didžiosios ir mažosios
raid÷s laikomos skirtingomis ir min÷ti trys vardai būtų skirtingi.
Naujesn÷se programavimo kalbose raidžių aib÷ turtingesn÷.
Ados 95 varduose gali būti vartojamos visos lotyniškos ab÷c÷l÷s raid÷s. Jų yra daug. Tod÷l
kalbos aprašyme jos neišvardijamos, o apibr÷žiama, kad raide laikomas simbolis, kurio pavadinimas
ISO/IEC 100646 standarto (Unikode) angliškame variante prasideda žodžiais LATIN CAPITAL
LETTER arba LATIN SMALL LETTER. Taigi vardai
ženklas
Sigurδsson
yra taisyklingi. Tuo tarpu vardai
×
слово
netaisyklingi, kadangi juose vartojamos ne lotyniškos ab÷c÷l÷s raid÷s.
Javos ab÷c÷l÷ dar turtingesn÷, vardams sudaryti gali būti vartojamos 26 pagrindin÷s
didžiosios ir mažosios lotyniškos ab÷c÷l÷s raid÷s, o taip pat visi simboliai (ne tik raid÷s!), kurių
kodai Unikode yra didesni už 160. Taigi Javos programoje būtų teisingi visi 4 aukščiau pamin÷ti
vardai.
Pirmosiose programavimo kalbose vardo ilgis buvo smarkiai ribojamas. Pavyzdžiui,
Fortrane jis tur÷jo neviršyti 6 simbolių. V÷liau šio ribojimo atsisakyta, bet paliekama teis÷ tai
padaryti konkrečiose realizacijose. Čia dažniausiai apsiribojama teksto eilut÷s ilgiu (256
simboliais), kartais laikomi reikšmingais tik pirmieji n (pvz., n = 10) vardo simbolių, o kiti
ignoruojami – jie gali būti, bet vardai, kurių pirmieji n simbolių sutampa, laikomi tais pačiais.
Vardas negali sutapti su rezervuotu žodžiu – simbolių rinkinys, sutampantis su rezervuotu
laiku, visuomet laikomas rezervuotu žodžiu. Tuo tarpu vardai gali sutapti su baziniais, bet
nerezervuotais žodžiais. Ką toks simbolis reiškia – bazinį žodį ar vardą – nustato transliatorius,
nagrin÷damas kontekstą, kuriame jis yra parašytas.
Vardus sudaro ir aprašo programuotojas. Tačiau kiekviena kalba turi tam tikrą skaičių iš
anksto parinktų ir aprašytų vardų, kurie dažnai vadinami standartiniais vardais. Galima įsivaizduoti,
kad tokie vardai yra aprašyti visą programą gaubiančiame fiktyviame bloke, jų aprašai galioja
programoje ir juos galima naudoti pagal programavimo kalboje numatytą paskirtį. Standartiniai
vardai nuo bazinių žodžių skiriasi tuo, kad juos galima aprašyti iš naujo. Pavyzdžiui, Paskalio
kalboje vardu abs žymima standartin÷ funkcija. Bet programoje šį vardą galima panaudoti kitam
tikslui. Jį aprašius

67
var abs: integer
vardas abs bus sveikojo skaičiaus tipo kintamojo vardas, o standartine funkcija abs nebebus galima
pasinaudoti, nes ji nebetenka savo vardo. Tuo tarpu, bazinių žodžių iš naujo aprašyti (t. y. paversti
juos vardais) negalima.
Tie patys žodžiai vienoje kalboje gali būti laikomi baziniais žodžiais, kitoje – standartiniais
vardais. Pavyzdžiui, programavimo kalboje Ada 95 žodis abs yra bazinis žodis, o Paskalyje
standartinis vardas. Abiem atvejais jis reiškia tą patį dalyką – skaičiaus modulį – tik Adoje jis
laikomas operacija, o Paskalyje – funkcija.
Kitas pavyzdys. Paprastųjų duomenų tipų pavadinimai Algole-68 (int, bool, real, char)
laikomi baziniais žodžiais, o Paskalyje (integer, boolean, real, char) – standartiniais vardais. Mat
Paskalyje galima aprašyti naujus duomenų tipus, taigi, jiems reikia sugalvoti vardus. Tod÷l natūralu,
kad ir standartinių duomenų tipų pavadinimai turt÷tų tą patį (t. y. vardų) statusą. Tuo tarpu Algole-
68 naujiems duomenų tipams suteikti vardų negalima (jie aprašomi kintamųjų aprašuose greta
kintamųjų vardų). Tod÷l standartinių duomenų tipų įvardijimas baziniais žodžiais anachronizmo
neįneša.

Pratimai
1. Varduose, rašomuose programose, negalima vartoti visų simbolių, kurie leistini bylų ir
katalogų varduose. Paaiškinkite kod÷l.

3.5. Konstantos

Dar viena leksemų rūšis yra konstantos. Tai skaičiai, login÷s reikšm÷s, simboliai bei jų
eilut÷s.
Sveikieji skaičiai rašomi įprastai. Pamin÷sime porą įdomesnių atvejų.
Adoje skaičiaus skaitmenys gali būti grupuojami, o grup÷s skiriamos pabraukimo simboliu.
Patogu skaityti taip užrašytus didelius skaičius, pavyzdžiui,
3_000_000
Be to, Adoje sveikuosius skaičius su daugeliu nulių dešin÷je galima užrašyti panaudojant
dešimtainį daugiklį, pavyzdžiui,
3E6
Kitose kalbose taip užrašytas skaičius būtų laikomas realiuoju.
Kalboje ML neigiamų skaičių pradžioje rašomas ne minusas, o tild÷, pavyzdžiui,
~25
Minusas arba tild÷ yra neigiamo skaičiaus dalis. Šie ženklai kartu su skaitmenimis sudaro
vieną leksemą. Tod÷l tarpų tarp jų neturi būti. Tiesa, užrašai
~ 25
- 25

68
taip pat teisingi. Tačiau šiuo atveju skaičiai yra teigiami, tik prieš juos parašyti vienviet÷s atimties
operacijos ženklai. Tai nebe skaičius, o reiškinys. Taigi, rezultatą – reiškinio reikšmę – dar
kompiuteris turi apskaičiuoti.
Realieji skaičiai rašomi įvairiau.
Trupmenin÷ dalis nuo sveikosios beveik visose programavimo kalbose skiriama tašku. Mat
skaičiavimo technika ir pirmosios programavimo kalbos at÷jo iš JAV. O ten ir realiame gyvenime
skaičiaus trupmeninę dalį priimta skirti tašku. Yra tik keletas programavimo kalbų, kuriose šiam
tikslui vartojamas kablelis (pvz., Logo).
Matematikoje platesnių r÷žių skaičiams
-9
užrašyti-11vartojamas dešimtainiu daugiklis, pvz.,
vietoj 0,000000000253 rašoma 2,53×10 arba 253×10 . Tam, kad būtų galima tur÷ti panašų, bet
vienodu lygiu rašomus skaičius (t. y, be indeksų, nepakeltų į viršų simbolių), į kompiuterio ab÷c÷lę
buvo įvestas specialus simbolis – dešimtukas žemai – 10. Tada ankstesnis skaičius būtų rašomas
taip: 25310-11. Šitaip skaičiai buvo rašomi Algole-60 ir kitose to meto kalbose. Tačiau šis simbolis
v÷liau buvo išbrauktas iš kompiuterių ab÷c÷l÷s ir vietoj jo imta rašyti raidę e arba E, pavyzdžiui,
253e-11.
Dvejetainių skaičių prireikia rašant sistemines programas, kadangi jos operuoja su kodais,
kurių prigimtis dvejetain÷. Tod÷l dvejetainiai skaičiai būna sisteminio programavimo kalbose.
Dvejetainiai skaičiai rašomi aštuonetainiu arba šešioliktainiu pavidalu. Šešioliktainiai
skaitmenys, didesni už 9, žymimi didžiosiomis raid÷mis A, B, C, D, E, F arba mažosiomis raid÷mis
a, b, c, d, e, f. Požymiai, nurodantis skaičiavimo sistemą, rašomi įvairiai. Pailiustruosime
pavyzdžiais (3.2 lentel÷).

3.2 lentel÷. Skaičius 63, užrašytas aštuonetainiu arba šešioliktainiu pavidalu įvairiose
programavimo kalbose

Programavimo kalba C++ Modula-2 Ada


Aštuonetainis skaičius 077 77B 8#77#
Šešioliktainis skaičius 0x3f 03FH 16#3F#
Skaičiaus užrašas viena ar kita skaičiavimo sistema neturi nieko bendro su kompiuteryje
naudojama skaičiavimo sistema tam skaičiui pavaizduoti. Beveik visi kompiuteriai aritmetines
operacijas atlieka tuk su dvejetainiais skaičiais ir juose skaičiai vaizduojami dvejetaine sistema.
Programavimo kalbose jie vaizduojami programuotojui patogiu pavidalo.
Adoje skaičiai gali būti užrašyti bet kuria skaičiavimo sistema, kurios pagrindas yra nuo 2
iki 16 ir ne tik sveikieji, bet ir realieji.

Login÷s konstantos yra dvi: teisinga ir neteisinga. Programavimo kalbose žymimos


įvairiai: baziniais žodžiais true ir false (Algolas-60, Algolas-68), standartiniais vardais true ir false
(Paskalis, Ada, Modula-2, Oberonas), vardais T ir F (Lispas) ir pan.
Simboliai ir eilut÷s. Simbolis yra pats mažiausias tekstinių duomenų elementas. Natūralu,
kad simboliu būtų laikomas simbolis, reikalingas tekstui pavaizduoti. Tai kompiuterio išorin÷s
ab÷c÷l÷s simbolis. Programa rašoma tos pačios ab÷c÷l÷s simboliais. Tod÷l simbolis, reiškiantis
simbolinę konstantą yra išskiriamas – rašomas tarp tiesių apostrofų (Paskalis, Prologas) arba tarp
paprastų viengubų arba dvugubų kabučių, pavyzdžiui, 'A', "A". Yra programavimo kalbų, kuriose

69
gali būti vartojamas bet kuris užrašas (Modula-2). Tokiais atvejais paprastai reikalaujama, kad abu
tos pačios konstantos užrašo ženklai (žymintys konstantos pradžia ir pabaigą) sutaptų.
Simbolį gaubiantis apostrofai arba kabut÷s čia vaidina metasimbolių vaidmenį. Tačiau jie
patys taip pat gali būti simbolin÷s konstantos. Tam, kad skirtųsi nuo metasimbolių, tokie simboliai
vaizduojami užrašant juos du kartus iš eil÷s. Pavyzdžiui, apostrofas būtų užrašomas keturiais
apostrofais: ' ' ' ' . Šiame užraše du kraštiniai apostrofai yra metasimboliai, nustatantys simbolin÷s
konstantos ribas, o likusiais dviem užrašyta pati konstanta.
Programavimo kalbose, kurias numatoma taikyti ir sisteminio programavimo uždaviniams
spręsti, simbolin÷mis konstantomis laikomi vidin÷s kompiuterio ab÷c÷l÷s simboliai (į šią ab÷c÷lę
įeina ir kompiuterio išorin÷s ab÷c÷l÷s simboliai). Dalis vidinių simbolių yra valdymo simboliai ir
neturi grafinio pavaizdavimo. Jie užrašomi kompiuterio kodais, kokiu nors būdu pažymint, kad juos
išreiškiantys simboliai reiškia kodą, o ne jų pačių pavaizdavimą.
Pavyzdys. Valdančiojo simbolio, kurio dešimtainis kodas yra 13 (aštuonetainis 15C,
šešioliktainis D) užrašas yra toks:
'\13' '\xD' C++
'\xD' Java
'15C' Modula-2
Simbolių eilut÷ yra sudaroma iš simbolių sekos, užrašytos tarp apostrofų arba paprastųjų
kabučių, pavyzdžiui,
'Aš einu namo'
Atsiranda problemų norint atskirti simbolį ir eilutę, sudarytą iš vieno simbolio.
Paprasčiausias sprendimas – vartoti skirtingus skiriamuosius simbolius: simbolį rašyti tarp
apostrofų, o eilutę – tarp paprastųjų kabučių, pavyzdžiui,
'A'
"A"
"Aš einu namo"
Šitaip daroma Adoje, Prologe.
Kai simbolis ir simbolių eilut÷ užrašomi vienodai, tai kas iš tikrųjų yra užrašyta nustatoma iš
konteksto arba programavimo kalboje laikoma, kad simbolio vieno simbolio eilut÷s tipai yra
suderinami ir tod÷l jų galima neskirti. Nors ir (vidinis) duomenų tipų suderinimas gali būti
atliekamas pasinaudojant (išoriniu) kontekstu.
Pratimai
1. Pateikiame to paties skaičiaus 25 užrašą keliais būdais. Pakomentuokite juos.
25
25.0
25E0
'25'
0x1A
1101
'1101'

70
2. Kurie iš šių užrašų gali būti:
* tik simbolių konstantos,
* tik simbolių eilut÷s.
a) 'AŠ',
b) 'Š',
c) ' ',
d) '',
e) 'A '.

3.6. Programos teksto išd÷stymas ir komentarai

Teksto išd÷stymas. Vaizdžiai išd÷stytas tekstas lengviau suvokiamas. Teksto išd÷stymą


lemia teksto skirstymas į eilutes (įskaitant ir tuščių eilučių įterpimą) ir „nematomi“ simboliai –
tarpai. Tarpai, įterpti eilučių pradžiose, suteikia tekstui „laiptuotą“ pavidalą, atspindintį teksto
struktūrą. Tarpai įterpti tarp eilut÷s leksemų išskiria mažiau susijusias programos teksto eilut÷s
dalis.
Tarpo simbolis turi savotišką statusą. Kalbos gramatika jo neapibr÷žia. Laikoma, kad visi
abstraktūs programos simboliai – terminaliniai simboliai išrikiuoti į vieną ilgą eilę be tarpų ir
nesuskirstyti į eilutes. Bet tuos terminalinius simbolius dar reikia gauti iš konkretaus programos
teksto, užrašyto kompiuterio simboliais. Vienas terminalinis simbolis gali būti išreiškiamas keliais
kompiuterio ab÷c÷l÷s simboliais. Tam, kad būtų galima vienareikšmiškai ir paprastai nustatyti ribas
tarp terminalinius simbolius vaizduojančių kompiuterio simbolių grupių, kai kuriais atvejais būtinai
reikia tas grupes atskirti tarpais. Pavyzdžiui, Paskalio kalbos sakinyje
a := b div c + 2
tarpai abipus bazinio žodžio div reikalingi leksikos analizatoriui. Nesant jų, bazinis žodis susilietų
su vardais. Kiti darbai reikalingi programos skaitytojui. Nesant jų:
a :=b div c+2
susidarytų klaidingas įvaizdis apie eilut÷s skirstymą į dalis – atrodytų, kad priskyrimo simbolis ir
pliusas tampriau sieja operandus, negu dalybos operacija.
Komentarai. Kai programavimo kalbos priemon÷mis nepavyksta aiškiai išreikšti programos
prasm÷s, rašomi komentarai. Komentarai - tai tekstai, aiškinantys programą, bet neturintys įtakos
jos veikimui.
Komentarai vartojami visose programavimo kalbose. Jie išskiriami įvairiais būdais (1
lentel÷). Komentarus galima įterpti tarp leksemų, bet ne į leksemų vidų, t. y. ten, kur leistinas tarpo
simbolis.
Komentarų rašymo taisyklių programavimo kalboje n÷ra. Gramatikos požiūriu komentarai
yra tekstai, parašyti kita, programavimo kalbai svetima kalba (pvz., lietuvių) ir įterpti į programą.
Tod÷l programavimo kalbos gramatika gali nustatyti tik komentarų pradžią ir pabaigą, o kaip
rašomi komentarai, neapibr÷žia.
Į programos tekstą galima pažvelgti ir iš kitos pus÷s – iš komentarų pozicijos. Tada būtų
reglamentuojamos komentarų rašymo taisykl÷s pagal komentarų kalbos (pvz., lietuvių) gramatiką, o

71
tikras programos tekstas tos kalbos požiūriu būtų svetimkūnis. Tokiu atveju komentarus ribojantys
simboliai tur÷tų priklausyti komentarų kalbai.
Komentarai gali būti naudojami ne tik žodiniams paaiškinimams, bet ir programos
apipavidalinimui juose panaudojant pseudografikos simbolius jų funkcijas atliekančius įprastus
grafinius simbolius.
Yra straipsnių ir net ištisų knygų, aprašančių programas ir pateikiamų kaip ištisas
taisyklingai parašytos programos tekstas, kurią gali atlikti kompiuteris. Visas tekstas, esantis aplink
programą su visomis pseudografikos priemon÷mis pateikiamas kaip programos komentarai.
3.3 lentel÷
Komentaro pavidalas Programavimo kalba Pastabos (komentarų
komentarai)

-- komentaras Ada Baigiasi eilut÷s pabaigoje


{ komentaras } Paskalis
(* komentaras *) ML, Modula-2, Paskalis
/* komentaras */ C, C++, Java
// komentaras C++, Java Baigiasi eilut÷s pabaigoje
/** komentaras */ Java Vartojamas tik prieš
aprašus. Tai informacija
dokumentacijos ruošimo
sistemai, kad ji komentaro
tekstą panaudotų kaip
aprašų paaiškinimą
" komentaras " Smalltalk
; komentaras Lisp Baigiasi eilut÷s pabaigoje

72
4 DUOMENYS
4.1. Duomenų klasifikacija

Programavimo kalbose duomenys skirstomi į duomenų tipus, arba trumpiau – į tipus.


Šiuolaikin÷se programavimo kalbose duomenų tipas apibūdina galimų duomenų reikšmių aibę ir
operacijų su to tipo duomenimis aibę. Dvi aib÷s nusako dvejopą duomenų tipo paskirtį bei
panaudojimą.
Nuo galimų reikšmių aib÷s dydžio (t. y. aibei priklausančių elementų skaičiaus) priklauso
duomeniui reikalingos vietos kiekis kompiuterio atmintyje. Pirmųjų kompiuterių atmintis buvo
labai maža. Reik÷jo ją taupyti. Tod÷l svarbiausia duomenų tipo paskirtis buvo informuoti
kompiliatorių, kiek vietos jis turi paskirti kompiuterio atmintyje vieno ar kito tipo reikšmei saugoti.
Su skirtingų tipų duomenimis atliekamos skirtingos operacijos. Kai žinomas duomenų tipas,
tai galima patikrinti, ar su duomenimis atliekamos to duomenų tipo operacijos. Tokia kontrol÷
įgalina aptikti daugelį klaidų programoje. Tai antroji duomenų tipų paskirtis, kurios svarba išaugo
v÷liau, kai kompiuterių atmintis išaugo, jos taupymas pasidar÷ nebetoks aktualus, o programos
pasidar÷ sud÷tingesn÷s ir jų kontrol÷ reikalingesn÷.
Kiekviena programavimo kalba turi savą duomenų tipų sistemą. Taigi ir savą duomenų tipų
klasifikaciją. Mus domina bendros duomenų tipų savyb÷s, būdingos daugeliui kalbų. Tod÷l čia
pateiksime apibendrintas klasifikavimo schemas, su kuriomis toliau susiesime konkrečius įvairių
programavimo kalbų duomenų tipus.
Kaip min÷jome, svarbiausi požymiai, apibūdinantys duomenų tipą, yra jo reikšmių aib÷ ir
operacijų su tomis reikšm÷mis aib÷. Tod÷l pirmiausia pagal tai ir suklasifikuosime duomenų tipus
(4.1 pav.), o v÷liau trumpai apžvelgsime ir kitokias klasifikacijas, pagal kitus požymius.

73
4.1 pav. Duomenų tipų klasifikacija

Pagal reikšmių struktūrinimo laipsnį duomenų tipai skirstomi į dvi klases: paprastuosius ir
struktūrinius.
Paprastųjų tipų reikšm÷s nedalomos, o struktūrinių tipų reikšm÷s yra sudarytos iš kitų –
paprastųjų ar struktūrinių – reikšmių. Paprastųjų tipų pavyzdžiai: loginis, sveikųjų skaičių tipas,
struktūrinių: rinkinio (įrašo), masyvo tipas.
Paprastieji duomenų tipai. Jeigu reikšm÷ žymima laisvai parenkamu vardu, tai toks
duomenų tipas vadinamas vardiniu. Įprasta, kad vardinio tipo reikšm÷s yra sutvarkytos ir su jomis
atliekamos sutvarkymui būdingos operacijos: lyginimas (<, >, <=, >=, =, <>), gretimos reikšm÷s
(succ ir pred).
Vardinis duomenų tipas pats paprasčiausias. Į programavimą jis at÷jo kartu su Paskalio
kalba (4.1 lentel÷).

74
4.1 lentel÷. Duomenų tipų atsiradimas programavimo kalbose
──────────────────────────┬───────────────────┬─────────────
Duomenų tipas │ Programavimo kalba│ Metai
──────────────────────────┼───────────────────┼─────────────
Vardinis │ Paskalis │ 1970
Loginis │ Fortranas │ 1954
Simbolinis │ Kobolas │ 1961
Sveikasis │ Fortranas │ 1954
Realusis │ Fortranas │ 1954
Semaforas │ Algolas 68 │ 1973
Masyvas │ Fortranas │ 1954
Ekvivalentas │ │
(alternatyva)* │ Fortranas │ 1954
Alternatyva │ Algolas 68 │ 1973
Rinkinys (įrašas) │ Kobolas │ 1961
Eil÷ (byla) │ Paskalis │ 1970
Sąrašas │ LISP │ ....
Klas÷ (abstraktusis)** │ Simula 67 │ 1967
Abstraktusis │ Ada │ 1983
Objektas (abstraktusis)***│ Smalltalk │ ...
Rodykl÷ │ PL/1 │ 1965
│ │
* Ekvivalentumo (EQUIVALENCE) atributą duomenų tipų apraše galima laikyti alternatyvos prototipu.
** Klasę galima laikyti abstrakčiojo duomenų tipo prototipu.
*** Objektas yra pastaruoju metu labai paplitusi abstrakčiojo duomenų tipo dalin÷ realizacija.

Loginis duomenų tipas turi dvi reikšmes, kurios dažniausiai žymimos standartiniais vardais
true ir false, o kartais ir kitokiais žymenimis (pvz., skaitmenimis 0 ir 1). Su jomis atliekamos
login÷s operacijos.
Simbolinio duomenų tipo reikšm÷s yra simboliai, kuriuos programos tekste įprasta rašyti
tarp apostrofų, pvz., 'A'.
Sveikųjų ir realiųjų skaičių duomenų tipų reikšm÷s yra skaičiai. Reik÷tų atkreipti d÷mesį,
kad programavime sveikieji skaičiai sudaro atskirą aibę, nepriklausančiąč realiųjų skaičių aibei,
t. y., sveikieji skaičiai n÷ra realiųjų skaičių poaibis.
Struktūriniai duomenų tipai programavimo kalbose apibr÷žiami tik iš dalies.
Programuotojas gauna ne išbaigtus tipus, o priemones jiems sudaryti, vadinamas tipų generatoriais
arba tipų konstruktoriais. Galutinai tipą apibr÷žia pats programuotojas. Pavyzdžiui, penkių sveikųjų
skaičių masyvo tipą Paskalio kalba galima apibr÷žti šitaip:
type masyvas = array [1..5] of integer
Tipo masyvas reikšmę galima įsivaizduoti kaip vientisą, nedalomą. Tačiau šį tipą
aprašiusiam programuotojui patogiau ją laikyti struktūrine, sudaryta iš penkių komponentų.
Taigi struktūriniais natūralu laikyti tuos duomenų tipus, kurių reikšmes konstruoja pats
programuotojas iš kitų tipų reikšmių, natūraliau matomų iš vidaus.
Struktūrinių duomenų tipų konstruktoriai vienas nuo kito skiriasi komponentų struktūrinimo
ir indeksavimo būdu. Pavyzdžiui, visi masyvo komponentai turi būti to paties tipo. Jie
identifikuojami indeksais. Tuo tarpu rinkinio (programavimo kalbose vadinamo įrašu (pvz.,
Paskalyje) arba struktūra (pvz., C), komponentai gali būti būti skirtingų tipų. Jie identifikuojami
laukų vardais.

75
Visi struktūriniai duomenų tipai yra panašūs – jie yra tarsi sudarančių komponentų
saugyklos (atmintin÷s). Jeigu į tą saugyklą pad÷jome komponentą, tai v÷liau jį gal÷sime paimti.
Nebent tik į jo vietą įrašytume naują komponentą.
Duomenys jungiami į struktūras tam, kad būtų juos paprasčiau saugoti, peržiūr÷ti ir
persiųsti. Mat iš karto persiunčiama visa struktūrinio duomenų tipo reikšm÷ (pvz., struktūrinio tipo
kintamajam priskiriama reikšm÷, kai kreipiamasi į funkciją arba procedūrą, turinčią struktūrinių
parametrų).
Kai operacija atliekama tik su atskiru duomenų struktūros komponentu, pavyzdžiui, jis
įrašomas ar skaitomas, kiti komponentai neliečiami. Taigi operacijos, susijusios su atmintine
(komponentų rašymas, skaitymas, visos struktūros persiuntimas), gali būti atliekamos ekonomiškai.
Pritaikius reikšmių struktūrinimą galima panašiai spręsti įvairius uždavinius, kuriuose esama
daug duomenų. Pavyzdžiui, į vieną reikšmę galima jungti visus duomenis, aprašančius matricą,
medį, grafą ar kitokį sud÷tingą objektą. Tačiau norint su tuo objektu atlikti tam tikrus veiksmus
(pvz., sudauginti matricas, nukirsti medžio šaką), tenka objekto reikšmę „išardyti“, t. y. nusileisti iki
struktūrą sudarančių komponentų, dažniausiai iki paties žemiausio lygio – paprastųjų duomenų tipų,
nes tik su jų reikšm÷mis galima atlikti prasmingas operacijas (logines, aritmetines).
Standartiniai ir programoje aprašyti duomenų tipai. Standartiniais duomenų tipais
laikomi tokie tipai, kurių visi atributai – tipo vardas, reikšmių aib÷, operacijos su jomis bei tų
operacijų žymenys – apibr÷žti programavimo kalboje. Programoje belieka juos vartoti. Standartinių
duomenų tipų pavyzdžiai: loginis (boolean), simbolinis (char), sveikųjų skaičių (integer), realiųjų
skaičių (real) (4.2 lentel÷).

4.2 lentel÷. Standartiniai ir programoje aprašyti duomenų tipai


────────────────────────┬───────────────────────────────────
Standartiniai │ Programoje aprašyti
────────────────────────┼───────────────────────────────────
Loginis │ Vardinis
Simbolinis │
Skaitmeninis │
────────────────────────┼───────────────────────────────────
│ Struktūrinis
────────────────────────┼───────────────────────────────────
│ Abstraktusis
────────────────────────┼───────────────────────────────────
Rodykl÷ (netipizuota) │ Rodykl÷ (tipizuota)


Naujiems tipams aprašyti vartojami duomenų tipų konstruktoriai. Pavyzdžiui, konkretus


masyvo tipas gali būti aprašytas pritaikius masyvo konstruktorių:
type masyvas = array [1..10] of integer
Čia aprašytas naujas duomenų tipas masyvas, kuriam konstruktorius nustato r÷žius (1..10) ir
elementų tipą (integer). R÷žius ir elementų tipą galima laikyti tipo konstruktoriaus parametrais.
Taigi, galima sakyti, kad pavyzdyje pateiktu atveju tipas masyvas buvo gautas parametrizuojant
masyvo tipo konstruktorių trimis parametrais, iš kurių du yra sveikieji skaičiai ir trečiasis – tipo
vardas. Jeigu taikytume procedūrų parametrų žymenis, tai gal÷tume tipo aprašą užrašyti maždaug
taip:
type masyvas = array(1, 10, integer).

76
Toks žym÷jimas semantiškai gali būti laikomas ekvivalenčiu ankstesniajam. Tuo tarpu
sintaksiniu požiūriu patogesnis (vaizdesnis) yra pirmasis, tod÷l jis ir vartojamas.
Pasinaudojant vienu masyvo tipo konstruktoriumi galima apibr÷žti daugelį konkrečių
masyvų, besiskiriančių r÷žiais ir elementų tipais. Taigi tiksliau būtų kalb÷ti ne apie vieną masyvo
tipą, o apie masyvų tipų šeimą. D÷l tos pačios priežasties ir visi kiti programoje aprašomi duomenų
tipai (vardinis, alternatyva, įrašas, seka, abstraktusis, rodykl÷) iš tikrųjų yra duomenų tipų šeimos.
Standartinių duomenų tipų vardų statusas. Ankstesn÷se programavimo kalbose (iki
Algolo 68) duomenų tipų vardų nebuvo galima aprašyti. Tada standartinių duomenų tipų
pavadinimai tur÷jo bazinio žodžio statusą. Programoje tiesiog prie kintamųjų vardų buvo rašomi
naujų (struktūrinių) duomenų aprašai. V÷lesn÷se kalbose atskirai buvo galima aprašyti tipo vardą ir
v÷liau jį vartoti kintamųjų vardų aprašuose. Tada atsirado duomenų tipo vardo sąvoka ir
standartinių duomenų tipų vardus imta laikyti standartiniais vardais.
Diskretieji ir tolydieji duomenų tipai. Kompiuterio prigimtis yra diskreti. Jo atmintin÷s
būsenos yra diskrečios, jų skaičius baigtinis. Tod÷l ir visų duomenų tipų realizacija yra diskreti.
Tačiau matematikoje vartojami ir tolydieji duomenų tipai – racionalieji bei realieji skaičiai. Jie
kompiuteryje vaizduojami tam tikrais kodais, kurie yra diskretūs. Tod÷l kompiuteryje iš tikrųjų gali
būti tik tolydžiųjų dydžių diskretieji modeliai. Tačiau programavimo kalbose tų modelių
diskrečiosios savyb÷s neapibr÷žiamos. Priešingai, stengiamasi nuo jų atsiriboti. D÷l to realiųjų
skaičių duomenų tipą laikysime tolydžiuoju. Na, o visi kiti paprastieji duomenų tipai (loginis,
vardinis, simbolinis, sveikasis skaičius, semaforas) yra diskretieji. Kitiems (struktūriniams,
abstraktiesiems, rodykl÷ms) diskretumo tolydumo klasifikacija praktiškai netaikoma. Tačiau
teoriniu požiūriu jie yra diskretieji.
Abstraktieji duomenų tipai grynu pavidalu vartojami retai (pvz., kalbose CLU, LARCH).
Tačiau jų id÷ja įgyvendinta obektin÷se programavimo kalbose (pvz. Smalltalk, Eiffel). Jose
duomenų tipo rolę vaidina klas÷s, o reikkšmių rolę objektai. Objektais taip pat papildomos ir kitos
kalbos, kurios buvo suprojektuotos be tokių duomenų tipų (pvz., Paskalis, C). Objektinis
programavimas laikomas atskira programavimo paradigma, tačiau glaudžiai siejasi su procedūriniu
programavimu. Tod÷l kartais objektinis programavimas laikomas struktūrinio programavimo
dalimi.
Klas÷s sąvoka pirmą kartą buvo panaudota kalboje Simula-67. Taigi Simulą-67 galima sieti
su objektinio programavimo pradžia.
Klas÷ – tai atskiras duomenų tipas, kuris aprašo ne tik duomenų aibę, bet ir operacijų su tais
duomenimis aibę. Jau min÷jome, kad bet koks duomenų tipas apibr÷žia dvi aibes: reikšmių ir
operacijų. Tačiau procedūrin÷se programavimo kalbose įprasta apibr÷žti tik reikšmių aibę.
Operacijų aib÷ neaprašoma; tik neišreikštiniu pavidalu reglamentuojama, kokias programavimo
kalboje apibr÷žtas operacijas galima vartoti su tu duomenų tipo reikšm÷mis. Tuo tarpu klas÷se
(objektuose) aprašoma operacijų su duomenimis aib÷ (operacijos vadinamos įvairiai: funkcijomis,
procedūromis, metodais).
Rodykl÷s. Rodykl÷s yra adresai. Jos tarnauja kitų duomenų tipų reikšmių nurodymui. Tod÷l
į rodykles galima žiūr÷ti dvejopai: 1) kaip į labai paprastą duomenų tipą, kurio reikšmių aib÷,
sutampa su kompiuterio atmintin÷s adresų aibe, o operacijos susijusios su adresavimu, ir 2) kaip į
sud÷tingą duomenų tipą, apibūdinamą rodykl÷s rodomu duomenų tipu. Tod÷l rodykles galima
klasifikuoti pagal jų rodomus duomenų tipus. O tai bus kitas duomenų klasifikavimo modelis. kurį
aptarsime 4.2 skyrelyje.

77
4.2. Duomenų rūšys: konstantų, kintamųjų ir rodyklių modelis

Kompiuteris atlieka operacijas su reikšm÷mis Reikšm÷s gali būti išreiškiamos konstantomis


arba kintamaisiais. Konstantų reikšm÷s pastovios. Kintamieji gali prarasti turimas reikšmes ir įgyti
naujas. Įprastų operacijų (pvz., aritmetinių) operandai gali būti ir konstantos, ir kintamieji. Tuo
tarpu priskyrimo sakinyje konstantos ir kintamojo vaidmuo skiriasi. Dešin÷je priskyrimo simbolio
:= pus÷je gali būti bet kas, kas turi reikšmę, vadinasi, ir konstanta, ir kintamasis. Tuo tarpu kair÷je
pus÷je gali būti tik kintamasis. Vadinasi, jeigu į priskyrimo sakinį žiūr÷tume kaip į tam tikrą
veiksmą (operaciją) iš duomenų tipų kontrol÷s pozicijų, tai išeitų, kad kintamojo tipas turi kažkuo
skirtis nuo tokio pat konstantos tipo, nors duomenų tipų požiūriu abu būtų to paties (pvz., sveikojo)
tipo. Šitaip atsiranda adresavimo gylio sąvoka. Kartu su duomenų tipo sąvoka ji sudaro duomenų
rūšies (angl. mode) sąvoka, kuri apima ir duomenų tipą, ir adresavimo gylį.
Tipų rūšys buvo susistemintos Algolo-68 kalboje. Jo modeliu ir pasinaudosime.
Modelį sudaro dvi dalys. Pirmoji atspindi programos tekstą, antroji – kompiuterį (jo vidų).
Tai, kas yra programos tekste, rašysime virš dvigubos horizontalios linijos, tai, kas yra
kompiuteryje, – po ja (4.2 pav.). Vardu a aprašyta konstanta 5. Jos vardas programoje tvirtai
susiejamas su skaičiumi 5 kompiuteryje. Nor÷dami pabr÷žti sąsajos pastovumą, ją vaizduojame
dviguba linija.

4.2 pav. Adresavimo gylių modelis: konstanta (a), kintamasis (aa) ir rodykl÷ (aaa)
(vartojami Algolo-68 žymenys)

78
Kintamasis aa žymi ne patį duomenį, o jo adresą kompiuterio atmintin÷je. Adresas žymimas
figūra su dviguba linija galuose. Į atmintį tuo adresu gali būti rašomi įvairūs duomenys, tod÷l sąsaja
tarp adreso ir duomenų gali būti keičiama.
Kintamasis aaa yra rodykl÷s tipo. Kompiuteryje rodykl÷ – tai adreso adresas. Pagal šią
schemą gali būti ir aukštesnio lygio (laipsnio) rodykl÷s: adreso adreso adresas ir t.t.
Kintamojo apraše adresavimo gylis apibūdinamas žodžių ref skaičiumi. Jis atitinka
adresavimo gylį kompiuteryje. Šitaip kokybinis konstantų, kintamųjų ir rodyklių skirtumas virsta
kiekybiniu.
Kompiuterio požiūriu konstantos reikšm÷ ir yra ta tikroji reikšm÷, su kuria jis atlieka
operacijas. Kintamojo reikšm÷ yra adresas, kuris nurodo tikrąją reikšmę. Rodykl÷s reikšm÷ yra
adreso adresas. Taigi, jeigu duomenimis laikytume ir adresus, tai reik÷tų laikyti, kad konstanta, jos
reikšmę galįs įgyti kintamasis ir tą kintamąjį rodanti rodykl÷, priklauso skirtingiems duomenų
tipams. Tačiau galimas ir kitoks požiūris, kai laikoma, kad konstanta, kintamasis ir rodykl÷ yra
tokio paties tipo. Skiriasi tik adresavimo gylis, apibūdinamas žodžių ref skaičiumi. Konstantos
adresavimo gylis yra nulinis, kintamojo – pirmas, rodykl÷s – antras. Taigi, tur÷dami tokį
adresavimo gylių modelį galime nebenaudoti konstantos rodykl÷s sąvokos. Šis modelis aprašo ir
aukštesnio lygio rodykles: rodyklių rodykles, rodyklių rodyklių rodykles ir t. t. Įrodyta, kad pakanka
tik vieno lygio rodyklių. Taigi pakanka trijų adresavimo gylių.
Abiejose priskyrimo sakinio pus÷se turi būti vienodo tipo duomenys. Tačiau kair÷s pus÷s
adresavimo gylis turi būti vienetu didesnis negu dešin÷s.
Šis reikalavimas netiesiogiai uždraudžia konstantos vardą rašyti kair÷je priskyrimo sakinio
pus÷je, nes konstantos adresavimo gylis yra pats mažiausias (nulinis) ir neb÷ra jam tinkamo
adresavimo gylio, kurį būtų galima nurodyti dešin÷je priskyrimo simbolio pus÷je. Taigi galimi
priskyrimo sakiniai (4.3 pav.).
aa := a;
aaa := aa.
Paveiksle grafiškai pavaizduota modelio būsena po priskyrimo.

4.3 pav. Modelio būsena atlikus priskyrimo sakinius

Jeigu dešin÷s pus÷s adresavimo gylis per didelis, tai reiškia, kad sakinyje nurodyta ne
reikšm÷, kurią reikia priskirti, o jos adresas. Tod÷l reikia atlikti papildomą veiksmą – paimti
reikšmę iš atmintin÷s. Šitokį veiksmą iš tikrųjų atlieka kompiuteris. Ši operacija žymima vardu
deref (angl. dereferencing, t. y. žodžio ref pašalinimas).

79
Pavyzdys. Turime aprašus:
integer a = 5;
ref integer aa, bb, cc;
ref ref integer aaa, ccc.
Tada galima rašyti šitokius priskyrimo sakinius:
bb := deref(aa);
cc := deref(deref (aaa));
ccc := deref(aaa).
Modelio būsena atlikus šiuos sakinius pavaizduota 3 paveiksle. Grafiškai operacija deref
išreiškiama figūras jungiančios rodykl÷s poslinkiu per vieną figūrą žemyn.

4.4 pav. Modelio būsena atlikus priskyrimo sakinius

Adresavimo operacijos, analogiškos operacijai deref, išreikštiniu būdu rašomos žemo


(kompiuterinio, mašininio) lygio programavimo kalbose (pvz. asembleryje). Aukšto lygio
programavimo kalbose jos nerašomos, tačiau suprantama, kad visur, kur tur÷tų būti, jos vykdomos
savaime. O vykdomos visur, kur reikia reikšm÷s ten, o parašytas kintamasis. Pavyzdžiui, priskyrimo
sakinyje
c := a + 2*b
iš tikrųjų atliekamos dvi reikšm÷s gavimo operacijos:
c := deref(a) + 2*deref(b)
Nevienareikšmiškas priskyrimo veiksmo traktavimas gali atsirasti, kai abiejose sakinio
pus÷se yra keletas ref (4.5 pav.). Kas bus atlikus pavyzdžiui tokį priskyrimą:
a := b?

80
4.5 pav.

81
4.6 pav.

Reikia susitarti, kurį variantą priimti: tą, kuris 4.6 paveiksle pavaizduotas plonesne linija, ar
tą, kuris pavaizduotas storesne. Logiškiau nemažinti kair÷s pus÷s adresavimo gylio, t. y., imti tą,
kuris pavaizduotas storesne linija.
Čia pateiktas modelis patogus tuo, kad jame mažai sąvokų ir jam realizuoti reikia nedaug
programavimo kalbos konstrukcijų. Kai šis modelis programavimo kalboje netaikomas, atsiranda
didel÷ duomenų tipų rūšių – konstantų, kintamųjų ir rodyklių – įvairov÷. Apie tai kalb÷sime kituose
skyreliuose.

Realizacija programavimo kalbose


Algolo-68 kalba sudaryta pagal čia pateiktą adresavimo gylių modelį. Iš tikrųjų šis modelis
ir at÷jo į programavimą kartu su šia kalba. Tačiau ir Algolo-68 programose nereikia rašyti operacijų
deref išreikštiniu pavidalu. Jos yra vartojamos tik aprašant pačią kalbą, norint tiksliau apibr÷žti
kalbos semantiką. Panašų tikslą tur÷jome ir mes – giliau suvokti duomenų tipus, kad suprastume ne
tik kas parašyta išreikštiniu pavidalu, bet ir kas slepiasi tarp eilučių. Tod÷l ir pateik÷me šį modelį.
Pateiksime sveikųjų skaičių konstantos i, kintamojo ii ir rodykl÷s iii aprašus Algolo-68
kalba:
int i = 2;
ref int ii = loc int;
ref ref int iii = ref loc int
Šie aprašai tiksliai atspindi anksčiau pateiktą modelį. Lygyb÷s ženklas rodo tvirtą sąsają tarp
programos tekste parašyto vardo ir objekto kompiuteryje. Ši sąsaja 3 paveiksle pažym÷ta dvigubu
brūkšniu. Bazinis žodis loc reiškia lokalaus adreso generavimą.
Šitokie aprašai tikslūs, bet pernelyg ilgi. Tod÷l vartojamos santrumpos ir paskutiniuosius du
aprašus galima pakeisti trumpesniais:

82
int ii;
ref int iii.
Paskalis. Šis modelis netaikomas. Rodyklių dalis yra atskirta nuo kintamųjų dalies.
Rodykl÷s tipo aprašo pradžioje rašoma rodykl÷s ženklas ^, pavyzdžiui,
var iii: ^ integer;
Konstantos aprašomos kaip atskira duomenų rūšis, pavyzdžiui,
const max = 10000.
Ada. Rodykl÷ laikoma atskiru duomenų tipu. Rodykl÷s tipo aprašo pradžioje rašomas
bazinis žodis access, pavyzdžiui,
iii: access integer;
Konstantos aprašomos kaip atskira duomenų rūšis.
C. Adresai vartojami gana intensyvia Tačiau šis modelis netaikomas. D÷l nesistemingo
adresų vartojimo atsiranda palankesn÷s sąlygos klaidoms, prarandamos duomenų tipų kontrol÷s
galimyb÷s.

4.3. Konstantos ir jų vardai

Matematikoje pastovūs dydžiai vadinami konstantomis. Programavime ne visiems


duomenims taikytina dydžio sąvoka. Tod÷l tiksliau būtų sakyti, kad konstanta yra pastovus duomuo.
Programose konstantos žymimos specialiais žymenimis, pavyzdžiui, 3.1415926536, 625,
'ALFA'. Tokie žymenys dažnai vadinami literalais. Literalai būna apibr÷žti programavimo kalboje.
Tai, galima sakyti, tikriniai konstantų vardai.
Daugumoje programavimo kalbų programuotojas gali konstantas žym÷ti ir patogiau, patys
parinkdami joms (bendrinius) vardus. Pateiksime pavyzdžių Paskalio kalba:
const pi = 3.1415926536;
c = 300000 { km/s }.
Taip aprašyti vardai pi ir c tampa naujais konstantų žymenimis (literalų sinonimais).
Reikšm÷ su konstantos vardu susiejama tik vieną kartą, kai aprašoma konstanta, ir v÷liau jos
pakeisti nebegalima – ji išlieka ta pati per visą vardo gyvavimo laiką. Tuo vardu pažym÷ta
konstanta skiriasi nuo kintamojo, kuris taip pat žymimas vardu.
Konstantomis galima aprašyti įvairaus pastovumo duomenis. Pagal tai skiriami trys
konstantų tipai:
1. Konstantos reikšm÷ aiški iš programos teksto be skaičiavimų. Tai literalas arba prieš tai
aprašytas konstantos vardas.
Pavyzdys:
const pi = 3.1415926536;
2. Konstantos reikšmei apskaičiuoti pakanka informacijos (kitų duomenų), esančios
programos tekste, t. y., reikšmę galima apskaičiuoti programos kompiliavimo metu.
Pavyzdys
const du_pi = 2*3.1415926536;

83
3. Konstantos reikšmei apskaičiuoti reikia duomenų gaunamų programos vykdymo metu,
t. y., konstantos reikšm÷ priklauso nuo programos pradinių duomenų. Kai programa atliekama iš
naujo arba kreipiamasi iš naujo į funkciją arba procedūrą, kurioje aprašyta konstanta, tas pats
konstantos vardas gali būti susiejamas su nauju duomeniu.
Pavyzdys
procedure p (a: integer; var b: integer);
const alfa = a;
...
end;
Visais atvejais kol gyvuoja konstantos vardas, tol jis žymi tą patį duomenį – tokį, koks jis
buvo aprašant vardą. Tačiau kai programa, funkcija arba procedūra baigia darbą, tai nustoja
gyvavusi ir joje aprašyta konstanta. Kitą kartą atliekant programą (ar kitą kartą kreipiantis į
procedūrą ar funkciją), sukuriama nauja konstanta, pažym÷ta tokiu pat vardu.

Realizacija programavimo kalbose


Algolas-68. Kontantos yra trečiojo tipo.
Paskalis. Konstantos pirmojo tipo. Tačiau prieš konstantą galima parašyti minuso (teoriškai
– ir pliuso) ženklą, pavyzdžiui,
const alfa = 12;
beta = -alfa;
Konstantos tipas nenurodomas. Jis vienareikšmiškai nustatomas iš literalo užrašo pavidalo.
Trečiojo tipo konstantos neišreikštiniu būdu atsiranda masyvo tipo parametruose,
pavyzdžiui,
procedure p (m: array [a..b: integer] of real);
Masyvo r÷žiai gaunami kartu su parametru m. Tod÷l jie priklauso nuo faktinio parametro,
pakeičiančio formalųjį parametrą m. Tačiau r÷žių a ir b procedūroje keisti negalima. Taigi jie turi
konstantos statusą. Tačiau sintaksiškai jie laikomi ne konstantomis, bet ir ne kintamaisiais, o
daugikliais. Daugiklis yra reiškinio dalis. Taigi jam, kaip ir reiškiniui, naujos reikšm÷s priskirti
negalima.
Turbo Paskalyje dar vartojamos taip vadinamos tipizuotos konstantos, kai nurodomas ir
konstantos tipas. Gali būti ir struktūrinių tipų konstantos, pavyzdžiui,
const masyvas: array [1..3] of integer = (11, 22, 33);
rinkinys: record a, b, c: integer end = (11, 22, 33).
Šis pavyzdys parodo, d÷l ko reikia nurodyti duomenų tipą: iš reikšmių užrašų negalima
vienareikšmiškai nustatyti jų tipų – masyvas ar įrašas. Tačiau iš tikrųjų tipizuotos Turbo Paskalio
konstantos n÷ra konstantos. Tai statiniai kintamieji, nes jų reikšm÷s gali būti keičiamos atliekant
programą.
Ada, C ir JAVA. Konstantos yra trečiojo tipo.

4.4. Kintamieji, programos būsena

Viena pagrindinių algoritmo (o tuo pačiu ir programos) savybių yra masiškumas, t. y. tas
pats algoritmas turi tikti apdoroti įvairiems pradiniams duomenims, patenkantiems į tam tikras

84
algoritmo apibr÷žties sritis. Vadinasi, algoritme, taip pat ir programoje reikia operuoti su iš anksto
nežinomais duomenimis. Tokie duomenys siejami su kintamaisiais, o kintamieji žymimi vardais.
Kintamasis turi reikšmę ir jos duomenų tipą. Statin÷se programavimo kalbose (o visos čia
aptariamos programavimo kalbos yra statin÷s) su kintamuoju apraše susiejamas duomenų tipas ir ši
sąsaja išlieka pastovi visą kintamojo gyvavimo laiką. Tuo tarpu kintamojo reikšm÷ gali būti
keičiama atliekant programą. Kiekvienu laiko momentu kintamasis „rodo“ konkretų duomenį
(reikšmę).
Programa duotu jos vykdymo momentu gali būti apibūdinama programos būsena.
Programos būsena – tai visų jos kintamųjų reikšm÷s tam tikru (duotu) laiko momentu. Programos
vykdymo procesas apibūdinamas jos būsenų kaita (būsenų seka). Kai atliekamas veiksmas,
programa pereina iš vienos būsenos į kitą.
Procedūrin÷je programavimo kalboje užrašyti veiksmai nusako būsenų kaitą. Programos
vykdymas pradedamas nuo pradin÷s būsenos, kurią galima laikyti nuline arba neapibr÷žta.
Atliekami veiksmai keičia programos būseną. Būsena keičiama priskiriant naujas reikšmes
kintamiesiems. Pradinių duomenų skaitymą taip pat galima prilyginti reikšmių priskyrimui. Kai
programa baigia darbą, jos būsena v÷l tampa nuline arba neapibr÷žta. Išlieka tik tie jos rezultatai,
kurie buvo atiduoti į išorę – įrašyti į bylas.
Reikšmių priskyrimas kintamiesiems ir programos būsenos nusakymas kintamųjų
reikšm÷mis yra svarbiausias visų procedūrinių programavimo kalbų skiriamasis bruožas.
Kintamieji skirstomi į statinius ir dinaminius. Tačiau ši klasifikacija n÷ra vienareikšm÷.
Egzistuoja dvi statinių ir dinaminių kintamųjų sampratos.
Vienu požiūriu statiniais kintamaisiais vadinami visi pirmojo adresavimo lygio kintamieji,
t. y., įprasti kintamieji, o dinaminiais – antrojo adresavimo lygio kintamieji, t. y., rodykl÷s.
Kitu požiūriu dinaminiais kintamaisiais laikomi įprasti kintamieji, kurių reikšm÷ms saugoti
paskiriama vieta kompiuterio atmintin÷je, kai jie aprašomi ir ši vieta išlieka iki tol, kol galioja
kintamojo vardas. Kai išeinama iš bloko, jiems paskirta atmintin÷ anuliuojama ir jų reikšm÷s
dingsta. Tai įprasti kintamieji. Statiniais kintamaisiais vadinami kintamieji, kurių reišm÷s išlieka ir
kai nustoja galioti kintamojo vardas, nors tuo metu kintamasis, aišku, ir jo reikšm÷, yra
nepasiekiami. Kai skaičiavimo procesas grįžta, į bloką, kuriame buvo aprašytas statinis kintamasis,
v÷l galima panaudoti jo senąją reikšmę.
Antrojo tipo statiniais kintamieji duoda papildomų galimybių. Pavyzdžiui, su jais patogu
programuoti pseudoatsitiktinių skaičių generatorių. Kai kreipiamasi į tokį generatorių, jis
sugeneruoja vieną pseudoatsitiktinį skaičių ir baigia darbą, bet sugeneruotą skaičių įsimena jo
statinis kintamasis. Kai į generatorių kreipiamasi kitą kartą, pagal statinio kintamojo reikšmę jis
žino kokį tolesnį skaičių generuoti.
Pirmą kartą tokie statiniai kintamieji buvo panaudoti Algole-60. Tai buvo vadinami own
(nuosavieji) kintamieji. Jie suk÷l÷ daug diskusijų, kadangi Algole-60 nebuvo kintamųjų pradinių
reikšmių priskyrimo mechanizmo jų aprašuose. Tod÷l atsirado problema, kaip reikšmę priskirti
statiniam kintamajam. Jeigu ji būtų priskiriama priskyrimo sakiniu bloko pradžioje, tai nebetektų
prasm÷s kintamojo statiškumas, kadangi dingtų ankstesn÷ jo reikšm÷. Jeigu tokios priskyrimo
nebūtų, tai kaip tur÷tų elgtis blokas, kai į jį kreipiamasi pirmą kartą.
Šios problemos išsprendžiamos, kai programavimo kalboje numatytas pradinių reikšmių
priskyrimas kintamiesiems jų aprašuose. Tada laikoma, kad toks priskyrimas atliekamas tik vieną
kartą programos įkrovos metu.

Realizacija programavimo kalbose

85
C, JAVA. Abi kalbos turi statinius kintamuosius. Jie aprašuose žymimi baziniu žodžiu
static.
Paskalis. Standarinis Paskalis statinių kintamųjų neturi. Turbo Paskalis turi tipizuotas
konstantas, kurios iš tikrųjų yra statiniai kintamieji. Jiems pradin÷s reikšm÷s suteikiamos programos
įkrovos metu, jos gali būti v÷liau keičiamos ir išsaugomos (bet nepasiekiamos), kai blokas baigia
darbą. Kai sugrįžtama į bloką, šių kintamųjų, vadinamų konstantomis, reikšm÷s v÷l tampa
pasiekiamos.

4.5. Operacijos, reiškiniai ir reikšmių priskyrimas

Operacijos programavimo kalbose užrašomos dvejopai: operatoriniu pavidalu ir funkciniu


pavidalu.
Operatoriniame užraše rašomas operatorius (operacijos simbolis), o greta jo, arba iš abiejų
pusių – operandai, pavyzdžiui,
a + b.
Funkciniame užraše rašomas operacijos (funkcijos) vardas, o po jo, skliaustuose išvardijami
oprandai, pavyzdžiui,
max(a, b).
Bet kurio pavidalo operacijos užrašas gali būti panaudotas kaip kitos operacijos operandas.
Taip formuojami reiškiniai – iš paprastesnių reiškinių gaunami sud÷tingesni.
Operacijų prioritetai. Funkcinis pavidalas vienareikšmiškai nustato funkcijų atlikimo
tvarką, nes čia privalomi skliaustai. Tuo tarpu operatoriniame užraše skliaustai gali būti praleisti.
Tada operacijų atlikimo tvarka nustatoma pagal jų prioritetus. Yra nusistov÷ję prioritetai tam tikrose
dažniau vartojamų operacijų grup÷se. Aritmetinių operacijų grup÷je daugyba ir dalyba turi aukštesnį
prioritetą, negu sud÷tis ir atimtis, loginių operacijų grup÷je konjunkcija turi aukštesnį prioritetą,
negu disjunkcija. Jie yra tie patys beveik visose programavimo kalbose. Tuo tarpu kitų operacijų
prioritetai, o taip pat tame pačiame reiškinyje esančių skirtingų operacijų grupių prioritetai yra
įvairūs (1 lentel÷). Kai operacijos vienodo prioriteto, tai susitariama operacijų atlikimo kryptis: iš
kair÷s į dešinę (dažniausiai) ar iš dešin÷s į kairę. Matematikoje visos operacijos, išskyrus k÷limą
laipsniu, atliekamos iš kair÷s į dešinę. Tokių pat taisyklių su retomis išimtimis laikomasi ir
programavimo kalbose.
Kai operacija apibr÷žta pačioje kalboje, tai jos prioritetą apibr÷žia reiškinio sintaks÷. Jeigu
kalboje yra galimyb÷ aprašyti naujas operacijas, tai tokia kalba turi tur÷ti priemones operacijų
prioritetams nustatyti. Šitokius prioritetų aprašus turi programavimo kalba Algolas-68. Joje
kiekvienai naujai operacijai suteikiamas prioritetas iš intervalo 0..9, t. y., visos operacijos
skirstomos į nedaugiau kaip 9 prioritetinių grupių, žymimų intervalo [1; 9] skaičiais.
Pavyzdys. Aprašas
prior op ~ = 5
Toks aprašas reiškia, kad naujos operacijos ~ prioritetas bus 5. Tokį prioritetą Algole-68 turi
operacija … Vadinasi operacija ~ tur÷s tokį pat prioritetą, kaip ir …
Naujos operacijos apraše dar reikia parodyti, kur turi būti rašomas vienviet÷s operacijos
operandas: prieš operatorių ar po jo (pvz., ~a ar a~). Teoriškai analogiška informaciją tur÷ti būti
pateikta ir apie dvivietę operaciją. Tačiau šiuo atveju pasirinkimo beveik n÷ra: operatorius rašomas
tarp operandų (toks užrašas vadinamas infiksiniu).

86
Operacijų polimorfizmas. Bendra problema abiems užrašo pavidalams yra operacijų
polimorfizmas. Tas pats operatorius arba tas pats funkcijos vardas gali būti vartojamas kelioms
operacijoms pažym÷ti. Pavyzdžiui, operatoriumi + žymima ir sveikųjų, ir realiųjų skaičių sud÷tis, o
Turbo Paskalyje – dar ir simbolių eilučių sąjunga. Funkcija abs taikoma sveikiesiems ir realiesiems
skaičiams. Kokia iš tikrųjų operaciją žymi tas ženklas, turi būti vienareikšmiškai galima nustatyti iš
konteksto, dažniausiai – operandų tipų. Tai nesunku padaryti kai operacijų skaičius programavimo
kalboje yra žinomas, t. y., kai dirbama tik su standartin÷mis funkcijomis arba operacijomis. Tačiau
kai atsiranda naujų, programuotojo sudarytų operacijų, gali iškilti nevienareikšmiškumo problemų.
Tod÷l naujų operacijų aprašymo taisyklių programavimo kalbose sudarymas yra labai atsakingas
darbas.
Priskyrimo sakinys ir priskyrimo operacija. Reiškinio reikšm÷s priskyrimas kintamajam
užrašomas priskyrimo sakiniu, pavyzdžiui,
kint := r;
Simbolis := yra priskyrimo ženklas. Kair÷je jo pus÷je turi būti kintamasis, o dešin÷je
reikšm÷ (reiškinys), kuri ir priskiriama kintamajam.
Pirmose programavimo kalbose vietoj priskyrimo ženklo buvo dažnai vartojamas ženklas =.
Tačiau šiuo ženklu įprasta žym÷ti lyginimo operaciją. Kad būtų išvengta painiavos skirtingus
dalykus žymint tuo pačiu ženklu, buvo sugalvoti kitokie priskyrimo simboliai: <-, := ir kt.
Daugiausiai vartojamas pastarasis ženklas :=.
Priskyrimo veiksmą kartais patogu traktuoti ir kaip operaciją. Tada priskyrimo ženklas :=
tampa operacijos ženklu, lygiateisiu su kitų operacijų ženklais, o visą priskyrimo sakinį galima
vadinti reiškiniu.
Priskyrimo operacija turi būti atliekama, kai apskaičiuotas dešin÷je ženklo := pus÷je esantis
reiškinys, t. y. v÷liausiai, nors jos ženklas parašytas kairiau visų kitų operacijų ženklų. Taigi
priskyrimo operacijos prioritetas turi būti žemiausias.
Priskyrimo operacija turi du operandus. Jie gali būti įvairių tipų, tačiau abiejų tipai turi
sutapti arba turi būti suderinami pagal programavimo kalboje apibr÷žtas taisykles.
Kair÷je priskyrimo ženklo pus÷je gali būti tik kintamasis (ne reikšm÷!). Vadinasi, operandų
rūšis skiriasi: kairysis operandas turi tur÷ti vienu ref daugiau negu dešinysis.
Kiekviena operacija duoda rezultatą. Kokio rezultato reikia tik÷tis iš priskyrimo operacijos?
Priskyrimo tikslas yra priskirti jau turimą reikšmę kintamajam, t. y. pakeisti kintamojo
reikšmę. Tuo tarpu kitos, įprastos (pvz., aritmetin÷s), operacijos nekeičia savo operandų reikšmių,
net jeigu jie ir yra kintamieji. Nenatūralu, kad ir funkcijos keistų parametrus ar kitų kintamųjų
reikšmes. Tačiau tokią galimybę funkcijos (operaciją taip pat galima laikyti funkcija) turi. Ji
vadinama šalutiniu poveikiu. Iš to išeina, kad priskyrimo operacijos pagrindinis tikslas išreiškiamas
šalutiniu poveikiu – kintamojo reikšm÷s pakeitimu.
Jeigu yra šalutinis poveikis, tai tur÷tų būti ir pagrindinis, t. y. operacijos rezultatas, kurį
gal÷tume laikyti ir priskyrimo reiškinio (ar priskyrimo sakinio) reikšme. Rezultatu galima pasirinkti
vieną iš dviejų: arba priskiriamo reiškinio reikšmę, arba kintamojo, kuriam priskiriamas reiškinys,
adresą.
Universalesnis antrasis atvejis: iš adreso galima gauti reikšmę, bet ne atvirkščiai. Tod÷l
laikoma, kad priskyrimo operacijos rezultatas yra kair÷s pus÷s operando adresas. Ši taisykl÷ kartu
įpareigoja, kad kairysis operandas turi tur÷ti adresą, t. y. jis gali būti tik kintamasis, pavyzdžiui,
a := b+c;
c := d;

87
bet ne konstanta ar reiškinys, pavyzdžiui,
5 := b+c;
a+c := b+c;
(c) := d.
Laikant priskyrimo operacija, labai paprastai paaiškinama daugkartinis priskyrimas,
pavyzdžiui,
a := b := c := d.
Viename priskyrimo sakinyje (reiškinyje) gali būti kelios priskyrimo operacijos, panašiai,
kaip ir kelios sud÷ties, atimties arba kitokios operacijos. Reikia tik susitarti, kad greta esančios
priskyrimo operacijos atliekamos iš dešin÷s į kairę. Tada ankstesnio pavyzdžio reiškinys būtų
skaičiuojamas taip, lyg būtų sud÷ti skliaustai
a := (b := (c := d))
Galimi ir mažiau įprasti reiškiniai, pavyzdžiui,
a := b+(c := d)
Sąlyginio ir variantinio sakinio reikšm÷s. Kiti sakiniai taip pat gali tur÷ti reikšmes ir būti
traktuojami kaip reiškiniai. Jau Algole-60 buvo įteisintas sąlyginis reiškinys, turintįs pavidalą:
if b then r1 else r2
čia b – loginis reiškinys,
r1 ir r2 – bet kurio to paties tipo reiškiniai.
Tai iš tikrųjų yra operacija, turinti tris operandus ir tokį neįprastą pavidalą.
Algole-68 vartojami ir alternatyvūs sąlygin÷s operacijos žymenys: trys jos operandai
skiriami vertikaliais brūkšneliais. Ankstesnis reiškinys būtų užrašytas šitaip:
b  r1 r2
Sąlyginis sakinys gali būti suprastintas ir tur÷ti tik vieną šaką. Sąlyginis reiškinys turi tur÷ti
abi šakas. Priešingu atveju reiškinio reikšm÷ bus ne visada apibr÷žta.
Analogiškas yra variantinis reiškinys, kurio pavidalas gali būti toks:
case a of
c1: r1;
c2: r2;
...
cn: rn
end
čia c1, c2, ... cn – reiškiniai.

Realizacija programavimo kalbose


Algolas-68. Kiekviena šios kalbos konstrukcija turi reikšmę ir gali būti traktuojama kaip
reiškinys. Tod÷l nedaroma skirtumo tarp reiškinio ir sakinio – jie net vadinami tuo pačiu vardu –
sakiniu (angliškai: clause).
Priskyrimo operacijos rezultatas yra kair÷je ženklo := pus÷je parašyto kintamojo adresas.

88
Algole-68 net ir sakinius skiriantis kabliataškis laikomas operacija. Jis laikomas tęsimo
operacijos ženklu. O jos rezultatas yra tipo void, turinčio tik vieną reikšmę empty. Taigi, kai
priskyrimo sakinys baigiamas vykdyti, jo reikšm÷ tampa empty.
Paskalis, Ada. Priskyrimas laikomas sakiniu. Vienu sakiniu užrašomas reikšm÷s
priskyrimas tik vienam kintamajam.
C, JAVA. Priskyrimas traktuojamas kaip operacija. Ypač patogu priskyrimo sakinį parašyti
ciklo while antrašt÷je į vieną konstrukciją sujungiant sąlygos tikrinimą ir kintamojo reikšm÷s
pakeitimą (žingsnio žengimą), pavyzdžiui,
while (n := n+1) < a
Vartojamos ir įvairios su priskyrimu susijusios santrumpos. Pavyzdžiui, sakinys
V1, V2, ..., Vn := R1, R2, ..., Rn
yra ekvivalentus vienu metu atliekamiems sakiniams:
V1 := R1;
V2 := R2;
...
Vn := Rn.
Pabr÷žiame, kad vienu metu, nes, pavyzdžiui, sakinys
x, y := y, x
yra ekvivalentus vienu metu atliekamiems sakiniams
x := y;
y := x,
kuriuos norint išreikšti nuosekliais veiksmais prireiktų trijų sakinių ir papildomo kintamojo:
i := x;
x := y;
y := i.
Priskyrimą galima sujungti su kita operacija ir užrašyti veiksmus trumpiau, pavyzdžiui,
x += 5 vietoj x = x+5
x *= y+1 vietoj x = x*(y+1)
Sutrumpintą užrašą transliatorius paverčia trumpesne programa: pakanka kintamojo x adresą
rasti tik vieną kartą kiekviename sakinyje.
Pratimai
1. Kokios operacijos bus atliekamos, nors jos neužrašytos išreikštiniu pavidalu, kai
atliekamas šitoks priskyrimo sakinys
a := b := (a + 15) * -2 * - (-2)
2. Kalb÷jome apie reikšm÷s, saugomos duotu adresu, gavimo operaciją deref. Kod÷l
nevartojama jai atvirkščia operacija (tarkime, ref)? Kokios problemos iškiltų ją apibr÷žiant?
3. Išreiškite kurios nors Jums žinomos programavimo kalbos C, C++ arba Java duomenų
rūšis šiame skyriuje pateikto modelio terminais (su ref).

89
5. DUOMENŲ TIPAI IR STRUKTŪROS

5.1. Paprastieji duomenų tipai

5.1.1. Vardiniai tipai

Tai pats paprasčiausias duomenų tipas, kuris gali būti laikomas visų kitų diskrečiųjų duomenų
tipų prototipu.
Vardinio duomenų tipo reikšm÷s žymimos vardais. Vardus parenka programuotojas,
aprašydamas tipą ir tuo pačiu apibr÷ždamas jo reikšmių aibæ.
Pavyzdys. Vardinių tipų aprašų pavyzdžiai Paskalio kalba.
type spalva = (m÷lyna, žalia, raudona);
type rūšis = (aukščiausia, pirma, antra, trečia, brokas).
Pirmasis aprašas pasako, kad tipo spalva duomenys gali įgyti tris reikšmes, kurios pavadintos
vardais m÷lyna, žalia, raudona. Tai konstantos. Taigi vardinis duomenų tipas duoda dar vieną
konstantų gavimo būdą.
Operacijų aibæ apibr÷žia pati programavimo kalba. Ji vienoda visiems vardiniams duomenų
tipamas. Kadangi vardiniai tipai gali aprašyti pačius įvairiausius duomenis, tai sunku parinkti
operacijas, turinčias prasmæ visiems vardiniams tipams. Tod÷l į programavimo kalbą įjungiamos
tik pačios bendriausios operacijos, susijusios su reikšmių sutvarkymu, kurį duoda tipo aprašas. Tai
lyginimo operacijos (<, <=, >, >=, =, <>) bei tolesn÷s reikšm÷s (succ) bei ankstesn÷s (pred)
reikšm÷s gavimo funkcijos.
Kartais vartojama funkcija, dažniausiai žymima vardu ord, kurios rezultatas yra konstantos
eil÷s numeris vardinio duomenų tipo apraše.
Kadangi kiekvienas vardinis duomenų tipas yra skirtingas, tai reikia kalb÷ti ne apie vieną
vardinį duomenų tipą, o apie jų šeimą. Vadinasi operacijos yra polimorfin÷s.
Vardinio duomenų tipo reikšmių vardai atsiranda programoje ir už jos ribų n÷ra žinomi. D÷l
to negalima korektiškai apibr÷žti jų skaitymo ir rašymo operacijų. Tod÷l beveik visose
programavimo kalbose jie n÷ra nei skaitomi, nei rašomi ir yra vartojami tik programų viduje, kaip
patogi priemon÷ žym÷ti tarpiniams rezultatams, turintiems nedaug reikšmių, kurias patogu vadinti
vardais. Programos, aktyviai naudojančios vardinius duomenų tipus pavyzdys gali būti
transliatorius. Jame apdorojamos programos konstrukcijos (leksemos, kitos sintaksin÷s
konstrukcijos, duomenų tipų pavadinimai ir pan.) aprašomos vardiniais duomenų tipais. Tuo tarpu
tiek pradiniai duomenys (transliuojamos programos tekstas), tiek galutinis rezultatas (sutransliuota
programa) yra tekstai.
Kompiuteryje visų diskrečiųjų duomenų tipų reikšm÷s modeliuojamos sveikaisiais skaičiais.
Vardinių tipų reikšm÷s koduojamos sveikaisiais skaičiais pradedant nuliu arba vienetu (pagal
operacijos ord rezultatą). Visos vardinių tipų lyginimo operacijos keičiamos sveikųjų skaičių
lyginimo operacijomis. Operacijos succ ir pred modeliuojamos sud÷timi ir atimtimi, t. y. vietoj
operacijos succ(a) atliekama operacija a+1, vietoj pred(a) atliekama a-1. Tačiau reikia nepamiršti,
kad tai yra tik realizacija kompiuteryje ir d÷l to, kad reikšm÷s vaizduojamos sveikaisiais skaičiais

90
nereiškia, kad su jomis gali būti atliekamos aritmetin÷s operacijos. Vardinis duomenų tipas ir buvo
sugalvotas tam, kad būtų galima lengviau atrasti klaidas, kai bandoma atlikti neteis÷tas operacijas.
Realizacija programavimo kalbose
Paskalis. Vardinis duomenų tipas laikomas pradiniu duomenų tipu iš kurio kildinami visi kiti
diskretieji duomenų tipai (loginis, simbolinis, sveikųjų skaičių). Tod÷l pastarieji turi ir visas
vardinio tipo operacijas: lyginimo, succ, pred.
Ada. Tas pats konstantos vardas gali būti panaudotas kelių vardinių tipų aprašuose,
pavyzdžiui,
type t is (alfa, beta, gama, delta);
type tt is (delta, ro, epsilon);
Kurio iš tikrųjų tipo teikšmæ žymi vardas delta, nurodoma tipo vardu, prirašytu prie
konstantos vardo:
t(delta);
tt(delta);
Vartojami duomenų tipų atributai, kurių pagalba gaunama informacija apie duomenų tipą.
Yra du standartiniai vardinių (o tuo pačiu – ir visų diskrečiųjų) tipų atributai: first ir last. Atributo
first reikšm÷ yra pirmoji (mažiausia) duomenų tipo reikšm÷, o atributo last – paskutin÷ (didžiausia)
reikšm÷, pavyzdžiui,
first't = alfa;
last't = delta.
Atributą galima laikyti savotiška funkcija, kurios argumentas yra duomenų tipas.
Atributus patogu vartoti ciklų, kuriuose nurodomas visų vardinio tipo reikšmių perrinkimas,
antrašt÷se.
Kaip ir Paskalyje, vardinis duomenų tipas laikomas pradiniu duomenų tipu iš kurio kildinami
visi kiti diskretieji duomenų tipai. Dar daugiau, Ada neturi atskiro simbolinio tipo. Standartinis
duomenų tipas char laikomas atskiru vardinio tipo atveju. Mat vardinio tipo konstantos gali būti
žymimos ne tik vardais, bet ir simboliais. Pateiksime pavyzdį:
type SpecRaid is ('A', 'B', 'C', alfa, beta, gama);
Apraš÷me vardinį duomenų tipą, kurio pirmosios trys reikšm÷s žymimos simboliais, o
tolesn÷s trys – vardais.
Šitaip Adoje patogu aprašyti įvairių kalbų ab÷c÷lių (besiskiriančių nuo angliškos ab÷c÷l÷s)
duomenų tipus, kuriuose raid÷s išd÷stytos toje ab÷c÷l÷je priimta eil÷s tvarka. Tokioje ab÷c÷l÷je gali
būti ir tokių raidžių, kurių neturi kompiuteris. Tokiu atveju jas programoje reik÷tų žym÷ti vardais.
C kalba vardinio duomenų tipo neturi. Tačiau joje yra grupinis sveikųjų skaičių konstantų
aprašas savo forma labai primenantis vardinio duomenų tipo aprašą kitose kalbose, pavyzdžiui,
vienas grupinis aprašas
enum t {alfa, beta, gama, delta}
prilygsta keturiems atskirų konstantų aprašams:
const alfa = 0;
const beta = 1;

91
const gama = 2;
const delta = 3.

5.1.2. Loginis tipas

Loginis duomenų tipas yra standartinis beveik visose programavimo kalbose. Tiesa, kai
kuriose vartojamas neišreikštiniu pavidalu. Tačiau jis pats, jo konstantos bei operacijos žymimos
nevienodai (5.1 lentel÷).

5.1 lentel÷. Loginio tipo, jo konstantų ir operacijų žymenys programavimo kalbose

Algolas- Ada Paskalis Modula-2 Oberonas C JAVA


68
Loginis tipas bool BOOLEAN boolean boolean boolean boolean boolean
Teisinga true TRUE true true true *) true
Neteisinga false FALSE false false false 0 false
Inversija ¬ not not not not not ! !
Konjunkcija ∧ & and and and and && &&
and
Disjunkcija ∨ or or or or or  
*Bet koks nelygus nuliui skaičius

Loginis duomenų tipas turi dvi reikšmes: teisinga (žym÷sime true) ir nereisiga (žym÷sime
false). Programavimo kalbose, turinčiose loginį duomenų tipą, visada vartojamos trys login÷s
operacijos: inversija, konjunkcija ir disjunkcija, o kartais ir daugiau operacijų: griežtoji disjunkcija,
implikacija, ekvivalentumas. Jų rezultatai pateikti 5.2 lentel÷je.

5.2 lentel÷. Loginių operacijų rezultatai

a b not a a and b a or b
false false true false false
false true false false true
true false false true
true true false true

92
Konjunkcijos reikšm÷ yra lygi false, jeigu bent vieno operando reikšm÷ yra false. Taigi
pakanka rasti pirmą false, o kito operando galima nebeskaičiuoti. Tai paspartintų skaičiavinus.
Tačiau programavime ši konjunkcijos savyb÷ išnaudojama dar ir kitam tikslui. Paiškinsime tai
pavyzdžiu. Tarkime, kad turime šitokį loginį reiškinį:
(i <= n) and A[i] > 0
kurio interpretacija yra tokia: sąlyga tenkinama, jeigu i neviršyja viršutinio masyvo A r÷žio ir
(jeigu taip) i-tojo elemento reikšm÷ teigiama.
Kai i <= n ne tik kad galima neskaičiuoti antrojo operando reikšm÷s, bet ir negalima
skaičiuoti, nes, jeigu būtų skaičiuojama, tai iššauktų klaidos situaciją – būtų ieškoma masyvo
elemento, kurio indeksas nepatenka į leistinus r÷žius. D÷l to kartais būna vartojamas dar ir kitas
konjunkcijos operacijos variantas, žymimas kitu operatoriumi ir apibr÷žiamas taip, kad antrasis
operandas nebūtų skaičiuojamas, jeigu pirmojo reikšm÷ yra false.
Loginio tipo reikšm÷s vartojamos ne tik kaip duomenys. Jomis nurodoma ir veiksmų atlikimo
tvarka – parenkama sąlyginio sakinio veiksmų šaka, valdomas ciklų kartojimas. Taigi,
programavime loginiai duomenys turi išskirtinį ir labai svarbų statusą. Be loginio tipo nebūtų
galima valdyti veiksmų atlikimo tvarkos.
Loginis tipas yra panašus į vardinį tipą tuo, kad jo reikšm÷s daugelyje programavimo kalbų
žymimos vardais. Dažnai programavimo vadov÷liuose šitaip aprašomas loginis duomenų tipas,
naudojantis vardiniu tipu:
type boolean = (false, true)
Tačiau toks aprašas tik paaiškina loginį duomenų tipą, bet jis aprašo naują vardinį tipą. Jeigu
tokį aprašą įd÷tume į programą, tai jis reikštų, kad jo galiojimo srityje vardai boolean, false ir true
yra nauji aprašyti ir tai yra naujo vardinio duomenų tipo, pavadinto vardu boolean elementai. Šis
tipas nieko bendro neturi su standartinio loginiu duomenų tipu, t. y., su jo reikšm÷mis negal÷tų būti
atliekamos login÷s operacijos, jo reikšm÷s negal÷tų būti panaudojamos kaip sąlygos sąlyginiuose,
ciklo ir kituose valdymo sakiniuose reikalaujančiuose loginių reikšmių. Čia galima pasteb÷ti
panašumą į standartines funkcijas. Pavyzdžiui, standartin÷s funkcijos abs rezultato tipas gali būti
sveikųjų skaičių arba realiųjų – toks pats, kaip ir argumento tipas. Tuo tarpu programoje aprašyti
galima tik mažesnių galimybių funkciją abs, tinkančią tik vienam kuriam nors duomenų tipui.
Realizacija programavimo kalbose
Algolas-68. Turi standartinį loginį duomenų tipą bool ir operacijas: inversiją, konjunkciją,
disjunkciją, ekvivalentumą ir implikaciją.
Paskalis. Turi standartinį loginį tipą boolean.
Visi diskretieji duomenų tipai perima vardinių tipų šeimos operacijas. Kadangi loginis tipas
turi tik dvi reikšmes (viena jų yra pirmoji, o kita – paskutin÷), tai operacijos succ ir pred gali būti
taikomos tik vienai reikšmei. Tod÷l iš jų tiesiogin÷s naudos mažai. Tačiau reikšmių sutvarkymas
atveria kelia vartoti loginus duomenis ten, kur būdingas reikšmių perrinkimas determinuota eil÷s
tvarka – ciklo kintamasis bei masyvo indeksas gali būti loginio tipo. Be to kai kurios kitos
operacijos, pasirodo, gali būti naudingos arba bent savotiškai įdomios.
Lyginimo operacijos turi atitikmenis logikoje:
= atitinka ekvivalentumą, žymimą ≡,
<> neekvivalentumą (≡),
<= implikaciją (⊂).

93
Ada. Turi standartinį loginį duomenų tipą boolean. Panašiai kaip ir Paskalyje jis kildinamas iš
vardinio duomenų tipo. Tod÷l turi ir visas Paskalio loginio tipo savybes.
C. Loginio duomenų tipo neturi. Jo vaidmenį atlieka sveikieji skaičiai. Skaičius, lygus nuliui,
gali būti interpretuojamas kaip login÷ konstanta false, o nelygus muliui – true.

5.1.3. Simbolių tipas

Simbolių tipo reikšm÷s yra simboliai, kurie gali būti panaudoti tekste: raid÷s, skaitmenys,
skyrybos ženklai, operacijų ženklai ir kiti simboliai. Jie turi būti kompiuterio ab÷c÷l÷je, t. y., juos
galima parodyti ekrane, išspausdinti. Skaitmenys yra rikiuojami did÷jančiai, o raid÷s – pagal
ab÷c÷læ. Be to, yra nustatyta ir visų teksto simbolių rikiavimo tvarka, nors ji mažiau žinoma.
Skirtingos valstyb÷s vartoja skirtingas ab÷c÷les ir skirtingą simbolių rikiavimo eilæ. Taigi
programavimo kalbose tiksliai apibr÷žti simbolių tipo aibæ bei operacijas su jais yra sunkiau. Tod÷l
dažniausiai apibr÷žiama tik nedidel÷ dalis simbolių tipo savybių, o visa kita paliekama realizacijų
nuožiūrai. O realizacija dažniausiai pasikliauja kompiuterio ab÷c÷le, kurioje yra visi simboliai, tiek
vartojami tekstuose, tiek ir valdantieji, neturintys grafinio pavaizdavimo. Laikoma, kad simbolių
rikiavimo tvarka atitinka jų kodų eil÷s tvarką.
Simbolių konstantos turi specialius žymenis – tai patys simboliai, parašyti taip, kad būtų
aišku, jog tai simbolinio tipo konstantos – dažniausiai tarp apostrofų.

Realizacija programavimo kalbose


Algolas-68. Turi standartinį duomenų tipą char. Tačiau simbolių aib÷ nereglamentuojama.
Paskalis. Turi standartinį duomenų tipą char. Simbolių aib÷s Paskalio standartas neapibr÷žia,
tačiau reikalauja, kad:
1) lotyniškosios ab÷c÷l÷s raid÷s būtų išd÷stytos pagal ab÷c÷læ ir tarp raidžių nebūtų jokių
simbolių, išskyrus tarpus;
2) skaitmenys būtų išd÷styti iš eil÷s, be tarpų.
Kai skaitmenys išd÷styti iš eil÷s ir be tarpų, kad ir kokie būtų jų kodai, juos lengva paversti
skaičiais. Šiam tikslui Paskalio kalboje vartojama funkcija ord(c). Jos argumentas yra simbolis, o
rezultatas simbolio kodas kompiuterio ab÷c÷l÷je – jo eil÷s numeris. Panaudojant šią funkciją galima
užrašyti skaitmens simbolio pavertimą vienaženkliu skaičiumi. Reiškinys
ord(c) – ord('0')
yra lygus skaičiui, kurį išreiškia simboliu c pavaizduotas skaitmuo, nepriklausomai nuo to, kokiais
kodais yra koduojami skaitmenys. Svarbu tik, kad būtų išlaikytas antrasis reikalavimas.
Ada. Atskiro simbolinio tipo neturi. Standartinis tipas character laikomas atskiru vardinio
tipo atveju. Jo ab÷c÷l÷ – simboliai iš ASCII kodo lentel÷s. Galima aprašyti ir naujus simbolinius
tipus, turinčius kitokį simbolių išd÷stymą. Simbolinio tipo konstantos gali būti ne tik simboliai,
parašyti tarp apostrofų, bet ir vardai. Tod÷l galima aprašyti simbolius, neturinčius grafinio
pavaizdavimo.
Pateiksime keletą programoje aprašytų simbolinių duomenų tipų pavyzdžių:

94
type ROMöNIŠKAS is ('I', 'V', 'X', 'L', 'C', 'D', 'M');
type OPERACIJA is ('+', '-', '*', '/');
type NELYGINIS is ('1', '3', '5', '7', '9')
Kai aprašomos kelios ab÷c÷les, jose gali pasikarti tie patys simboliai. Kad būtų galima
nustatyti, kuriai ab÷c÷lei (kuriam vardiniam tipui) priklauso pakartotas simbolis, prieš jį rašomas
prefiksas, nurodantis tipą, pavyzdžiui:
ROMöNIŠKAS'('V')
Aišku, kad kiekvienos ab÷c÷l÷s simbolių eil÷ bus sava, nepriklausoma nuo kitų ab÷c÷lių
(programoje aprašytų simbolinių duomenų tipų).
C. Turi standartinį tipą char. Jo konstantos žymimos simboliais tarp apostrofų, o jeigu
simbolis neturi grafinio pavaizdavimo, tai eilute, kurios pirmasis simbolis yra '\', o toliau eina
simbolio kodas – jo eil÷s numeris aštuonetaine sistema arba tam tikras kalboje nustatytas kodas.
Pavyzdžiui, simbolis, kurio numeris 226 žymimas '\226', per÷jimo į kitą eilutæ simbolis žymimas
'\n'.
Simbolin÷s reikšm÷s gali būti interpretuojamos kaip sveikieji skaičiai ir su jomis atliekamos
operacijos.
JAVA. Tas pats, kaip C kalboje. Tačiau simboliai yr iš Unikodo aib÷s. Vienam simboliui
skiriama 16 bitų.

5.1.4. Atkarpos tipai ir potipiai

Kai aprašomas naujas paprastasis duomenų tipas, tai kartu apibr÷žiama jo reikšmių aib÷,
kurios visi elementai (t. y. visos duomenų tipo reikšm÷s) pagal nusistov÷jusią duomenų tipų
sampratą turi skirtis nuo visų kitų duomenų tipų reikšmių. Tačiau dažnai patogu paimti kito
duomenų tipo, vadinamo baziniu, reikšmių poaibį ir operacijas (arba jų poaibį). Šitokiu atveju gali
būti gaunamas:
1) naujas duomenų tipas su naujomis reikšm÷mis ir naujomis operacijomis, kurių vardai (arba
kitokie žymenys) sutampa su atitinkamais bazinio duomenų tipo žymenimis, bet gali būti
atskiriamos nurodant tipo vardą, pavyzdžiui, prefiksu (žr. 5.1.3 skyr.).
2) bazinio duomenų tipo potipis, kurio reikšmių ir operacijų poaibis yra neatsiriamos nuo
bazinio tipo reikšmių bei operacijų.
Sutvarkytų duomenų tipų gana natūralus poaibis yra ištisin÷ (be tarpų) reikšmių atkarpa. Toks
poaibis išskiriamas nurodant abi tos atkarpos kraštines reikšmes: minimalią (apatinį r÷žį) ir
maksimalią (viršutinį r÷žį). Visos reikšm÷s, patenkančios į šią atkarpą, sudaro reikšmių poaibį.
Perimamos visos bazinio tipo operacijos.
Tipas, kurio reikšmių poaibis apibr÷žiamas nurodytu būdu, vadinamas atkarpos tipu. Jis
gaunamas iš kito duomenų tipo, vadinamo baziniu, jį parametrizuojant – nurodant dvi to tipo
reikšm÷s: mažiausią ir didžiausią.

95
Realizacija programavimo kalbose
Paskalis. Turi atkarpos tipą, kuris yra bazinio tipo potipis. Atkarpos tipo kintamieji gali įgyti
tik reikšmes, kurios patenka į jų tipo atkarpą. Tačiau kai reikšm÷ paimama iš kintamojo ir su ja
atliekamos operacijos, tai ji tampa bazinio tipo reikšme. D÷l to yra tik atkarpos tipo kintamieji, bet
n÷ra atkarpos tipo reiškinių.
Ada. Turi abu atkarpos tipo variantus, t. y., galima aprašyti naują duomenų tipą kaip naują
arba kaip bazinio tipo potipį.

5.1.5. Sveikieji skaičiai

Matematikoje sveikųjų skaičių aib÷ yra begalin÷. Kompiuteryje ją riboja žodžio ilgis, ir
programavime vartojamas tos aib÷s poaibis. Tod÷l sveikųjų skaičių tipą galima laikyti atkarpos tipu,
kurio bazinis tipas yra matematinių sveikųjų skaičių tipas su begaline reikšmių aibe. Beveik visos
programavimo kalbos turi standartinį sveikųjų skaičių tipą, žymimą integer arba int, kurio intervalo
programavimo kalba nefiksuoja ir palieka jį transliatoriaus autorių nuožiūrai.
Skaičiams žym÷ti yra specialūs simboliai (žymenys). Juos galima laikyti tikriniais skaičių
vardais.
Sveikiesiems skaičiams būdingos aritmetin÷s operacijos: sud÷tis (+), atimtis (-), daugyba (*)
ir dalyba (/). Visos operacijos dviviet÷s. Tačiau sud÷ties ir atimties operacijos gali būti ir vienviet÷s.
Vienviet÷ sud÷tis gali būti traktuojama kaip tapatumo operacija, o vienviet÷ atimtis – skaičiaus
ženklo keitimo operacija. Vienvietes operacijas galima pakeisti dviviet÷mis, kurių pirmasis
operandas yra nulis:
+a = 0+a;
-a = 0-a.
Sveikųjų skaičių dalyba duoda du rezultatus: dalmenį ir liekaną. Operacijų, ypač aritmetinių,
rezultatus įprasta vartoti reiškiniuose. O aritmetinio reiškinio rezultatas yra vienas skaičius. Tod÷l ir
operacijos rezultatas turi būti vienas skaičius. D÷l to dalybos operacija skaldoma į dvi: dalmens
radimą (ši operacija ir vadinama dalyba) ir liekanos radimą.
Dalybos (dalmens radimo) operaciją turi (arba bent prival÷tų tur÷ti) visos programavimo
kalbos, turinčios sveikųjų skaičių tipą. Tuo tarpu liekanos radimą turi mažiau kalbų. Mat turint
dalybos operaciją liekanos radimą nesunku suprogramuoti:
a \ b = a - a / b * b,
čia ženklu \ žym÷jome liekanos radimo operaciją.
Dviejų sveikųjų skaičių dalybos rezultatu galima laikyti ir trupmeninį skaičių. Bet tada
gaunamas jau kito – realiųjų skaičių tipo rezultatas.
Matematikoje prie aritmetinių operacijų priskiriamas ir k÷limas laipsniu. Tačiau
programavime ir šiuo atveju nemalonumų pridaro neigiami skaičiai.
p
Jeigu laipsnio rodiklis p>0, tai k÷limo laipsniu a rezultatą galima išreikšti daugkartine
sandauga:
p
a = a*a*...*a

96
p kartų
ir natūralu, kad rezultatas būtų sveikasis skaičius.
Jeigu p<0, tai k÷limas laipsniu apibr÷žiamas kaip:
a^(1/p).
Šiuo atveju rezultatas bus realusis skaičius. Taigi, k÷limo laipniu operacijos rezultato tipas
priklauso nuo operando reikšm÷s. Tod÷l programavimo kalbose, kuriose yra statin÷ tipų kontrol÷,
k÷limo laipsniu operacija nevartojama arba apibr÷žiama su išimtimis d÷l neigiamų skaičių.
Be pagrindinio standartinio sveikųjų skaičių duomenų tipo programavimo kalbose vartojami
ir kiti sveikųjų skaičių tipai, besiskiriantys reikšmių intervalu. Pateiksime keletą tokių tipų darybos
būdų.
1. Jeigu programavimo kalba turi atkarpos tipą, tai juo pasinaudojant galima gauti naujus
sveikųjų skaičių tipus arba potipius pasinaudojant šiuo tipu, pavyzdžiui:
type natūralusis = atkarpa 1..maxint;
type skaitmuo = atkarpa 0..9;
type gimimo_metai = atkarpa 1850..2000;
type x = atkarpa -5..5.
2. Programavimo kalboje yra apibr÷žti keli sveikųjų skaičių tipai, pavyzdžiui, natūraliųjų
skaičių tipas.
3. Yra specialios priemon÷s naujiems sveikųjų skaičių tipams konstruoti: nurodomas
maksimalus skaičiaus skilčių skaičius, vartojami papildomi baziniai žodžiai, pavyzdžiui, short (jei
norima skaičių intervalą sumažinti) ir long (jei norima skaičių intervalą padidinti).
Kitokių ir tiksliai tokių, kokių reikia, r÷žių sveikuosius skaičius galima gauti panaudojant
atkarpos tipą. Tada būna geresn÷ skaičiavimų kontrol÷, nes galima tikrinti, ar pradiniai duomenys
bei gauti rezultatai patenka į reikiamus r÷žius. Taip pat transliatoriui suteikiama galimyb÷ taupyti
atmintį, nes, žinant iš anksto (transliavimo metu), kad skaičius bus nedidelis, galima jam skirti
mažiau atminties.
Skaičių intervalo nurodymas duomenų tipo apraše gali pasitarnauti ir kaip komentaras, nes iš
jo dažnai galima spræsti apie to duomenų tipo, o kartu ir juo aprašyto kintamojo paskirtį.
Vartojant vien sveikųjų skaičių atkarpų tipus gaunama visiška algoritmo nepriklausomyb÷
nuo kompiuterio.

Realizacija programavimo kalbose


Algol-68. Turi standartinį tipą int. Iš jo galima padaryti daug išvestinių tipų prirašant žodžių
short arba long, pavyzdžiui:
short int a.
Jeigu norima pasakyti, kad skaičius dar trumpesnis, rašomi du žodžiai short, pavyzdžiui:
short short int b.
Apskritai, kuo daugiau žodžių short, tuo trumpesn÷ atkarpa. Kalboje žodžių short skaičius,
kurį galima prirašyti tipo apraše, neribojamas. Tačiau realizacijoje reaguojama tik į ribotą žodžių
short skaičių. Likusieji žodžiai atkarpos nebetrumpina.

97
Paprasčiausiose realizacijose gali būti nereaguojama n÷ į vieną žodį short arba long. Tada
visi sveikieji skaičiai kompiuteryje koduojami vienodai.
Analogiškai galima sveikųjų skaičių atkarpą ir prapl÷sti. Tai nurodoma prieš tipo žodį int
prirašant žodžius long, pavyzdžiui:
long int c;
long long int d.
Paskalis. Turi standarinį tipą integer. Kiti sveikųjų skaičių tipai gaunami panaudojant
atkarpos tipą.
Ada. Standartinio sveikojo skaičių tipo (su fiksuotais r÷žiais) neturi.Visi sveikųjų skaičių tipai
aprašomi atkarpos tipu. Programuotojas turi aprašyti tokią sveikųjų skaičių atkarpą, kokios iš
tikrųjų jam reikia. Tos atkarpos tipo reaalizavimo problema paliekama transliatoriui. Jeigu ta
atkarpa telpa į kompiuterio, kuriam transliuojama programa, sveikųjų skaičių diapazoną, tai viskas
gerai. Jei netelpa, tai transliatorius pats turi sukurti tokios skaičių atkarpos vaizdavimo būdą ir
modeliuoti operacijas su jos skaičiais arba pranešti programuotojui, kad transliatorius nesugeba
susidoroti su tokiais dideliais skaičiais.
Skaičiaus užraše gali būti tarpai, skirstantys ilgą skaičių į grupes po tris skaitmenis,
pavyzdžiui,
3 000 000
Didelius sveikuosius skaičius galima rašyti rodykliniu pavidalu, pavyzdžiui,
3E6
Koks skaičius: sveikasis ar trupmeninis yra užrašytas rodykliniu pavidalu, apsprendžia
skaičiaus pagrindin÷ dalis. Jeigu ji yra sveikasis skaičius, tai ir visas skaičius laikomas sveikuoju,
jei realusis (dešimtain÷ trupmena) – tai ir visas skaičius realusis.
C. Turi standartinį sveikųjų skaičių tipą int ir standartinį neneigiamų skaičių unsigned.
Skaičius galima sutrumpinti ir pailginti prirašant prieš int tik vieną žodį short arba long.
Sveikųjų skaičių tipas C kalboje yra pagrindinis diskretusis duomenų tipas. Juo modeliuojami
ir kiti diskretieji tipai, netgi loginis bei rodykl÷s. D÷l to su skaičiais gali būti atliekamos ir jiems
nebūdingos login÷s operacijos.
JAVA. Turi keturis skirtingų r÷žių sveikųjų skaičių tipus: byte (1 baitas), short (2 baitai), int
(4 baitai) ir long (8 baitai).

5.1.6. Realieji skaičiai

Skaitmenin÷s skaičiavimo mašinos atmintyje galima pavaizduoti tik baigtinį reikšmių skaičių.
D÷l to faktiškai kompiuteris dirba ne su tikrais matematiniais racionaliaisiais skaičiais, bet su
apytiksliais jų modeliais, arba mašininiais racionaliaisiais skaičiais. Programavimo kalbose jie
vadinami realiaisiais skaičiais. Realiuosius skaičius turi visos universalios programavimo kalbos.
Kompiuteryje vartojami du realiųjų skaičių vaizdavimo būdai: fiksuoto kablelio formatu ir
slankaus kablelio formatu. Fiksuoto kablelio skaičiai apibūdinami absoliučia paklaida, o slankaus –
santykine. Tod÷l norint išsamiai charakterizuoti realiųjų skaičių duomenų tipą reik÷tų nurodyti
keturis parametrus: du intevalo r÷žius (mažiausią ir didžiausią), paklaidos rūšį (absoliutin÷ ar

98
santykin÷) ir maksimalų paklaidos dydį. Tačiau beveik visose programavimo kalbose šie parametrai
nereglamentuojami. Dažniausiai manoma, kad realieji skaičiai vaizduojami slankaus kablelio
formatu, t. y., jų paklaida santykin÷.
Skirtingi vaizdavimo būdai kompiuteryje apsprendžia ir du vaizdavimu būdus programose:
dešimtain÷s trupmenos ir rodykliniu pavidalu.
Dešimtaine trupmena pavaizduotų skaičių trupmenin÷ dalis beveik visada skiriama tašku. Ir
tik nedaugelyje programavimo kalbų (pvz., Logo) – kableliu. Skyrimas vien tašku atsirado
istoriškai, kadangi pirmąsias programavimo kalbas kūr÷ amerikiečiai, o jie trupmeninæ skaičiaus
dalį ir matematikoje skiria tašku.
Rodykliniu pavidalu užrašyti skaičiai turi ir dešimtainį daugiklį 10n. Ankstesn÷se
programavimo kalbose (pvz., Algole-60) buvo vartojamas specialus simbolis: mažas dešimtukas 10
daugiklio laipsniui užrašyti ir realieji skaičiai rodykliniu pavidalu buvo rašomi, pavyzdžiui, šitaip:
1.2571012
310-15
V÷liau specialaus simbolio buvo atsisakyta ir jo vietoje imta vartoti raidæ e arba E.
Su realiaisiais skaičiais atliekamos aritmetin÷s (+, -, *, /) ir lyginimo (<, <=, =, <>, >, >=)
operacijos, analogiškos operacijoms su sveikaisiais skaičiais. Skiriasi tik dalyba, nes čia liekanos
sąvoka neturi prasm÷s.
Suderinamumas su sveikaisiais skaičiais. Programavime sveikieji skaičiai nelaikomi
realiųjų skaičių poaibiu. Tod÷l formaliai sveikieji ir realieji skaičiai priklauso skirtingiems
duomenų tipams ir juos reikia griežtai skirti. Tačiau daugelyje skaičiavimų patogu vartoti vieną –
skaičių – sąvoką. Šį faktą patvirtina ir tai, kad aritmetin÷s operacijos su abiejų rūšių skaičiais
žymimos tais pačiais ženklais. D÷l to yra numatytas vieno tipo skaičių keitimas kito tipo skaičiais.
Jis sprendžiamas įvairiai. Tačiau galima pasteb÷ti bendrus bruožus, išplaukiančius iš pačių skaičių
savybių.
Sveikąjį skaičių pakeisti realiuoju galima beveik visada, nes toks pakeitimas yra paprastas ir
vienareikšmis. Tai galima įsivaizduoti kaip kaip trupmeninio nulio prirašymą prie sveikojo
skaičiaus, pavyzdžiui,
25⇒25.0
Atvirkščias pakeitimas n÷ra toks paprastas, nes neaišku kaip pasielgti su trupmenine dalimi –
ją atmesti ar skaičių apvalinti. D÷l to pasirinktą tokio pakeitimo būdą reikia užrašyti išreikštiniu
pavidalu, pavyzdžiui, standartine funkcija. pateiksime tokio keitimo pavyzdį Paskalio kalba:
trunc(5.7) ⇒ 5
round(5.7) ⇒ 6.

Realizacija programavimo kalbose


Algolas-68. Turi standartinį duomenų tipą real. Kai pakanka mažesnio tikslumo, aprašas
papildomas žodžiais short, o kai reikia didesnio – žodžiais long, pavyzdžiui:
short short real trumpas;
long long long real ilgas.

99
Kuo daugiau prirašyta šių žodžių, tuo mažesnis ar didesnis tikslumas tur÷tų būti. Tačiau
transliatorius reaguoja tik į keletą žodžių (paprastai vieną žodį short ar long). Tolesni žodžiai
tikslumui nebeturi įtakos.
Paskalis. Turi vieną standartinį realiųjų skaičių tipą real.
Oberon-2. Turi du realiųjų skaičių duomenų tipus: REAL ir LONGREAL. Visi aritmetiniai
duomenų tipai yra surikiuoti į eilæ
LONGREAL, REAL, LONGINT, INTEGER, SHORTINT
„Didesniam“ (esančiam dešiniau) duomenų tipui priklausanti reikšm÷ gali būti vartojama
vietoj bet kurios „mažesnio“ duomenų tipo reikšm÷s. Tuo tarpu atvirkščio suderinimo būdą reikia
nurodyti išreikštiniu pavidalu.
Ada. Turi gerai (o gal ir geriausiai) apgalvotą realiojo tipo parametrizavimo siatemą. Aprašant
tipą, galima nurodyti reikšmių intervalą (po žodžio range) ir tikslumą (po žodžių delta ar digits).
Jeigu apraše yra žodis delta, tai nurodoma leistina absoliutin÷ paklaida. Jeigu apraše yra žodis
digits, tai nurodomas santykinis tikslumas, išreikštas skilčių skaičiumi. Pateiksime keletą
pavyzdžių:
type plotas is digits 5 range -1E30..1E30;
trumpas is digits 3 range -1E15..1E15;
fiksuotas is delta .01 range -100.0..100.0;
temperatūra is delta .1 range 34.0..42.0;
Taigi Adoje realieji skaičiai parametrizuojami labai lanksčiai ir visapusiškai. Tai didelis
žingsnis į priekį, palyginti su kitomis programavimo kalbomis. D÷l tokio parametrizavimo
programos tampa mobilesn÷s. Mat tipo apraše nurodoma viskas, ko uždavinyje reikalaujama iš
realiųjų skaičių, o ne pasikliaujama tuo (tiksliau – bet kuo), ką duoda kompiuteris. Jeigu
kompiuteris negali realizuoti tipo apraše pateiktų parametrų, tai transliatorius turi juos realizuoti
programiškai arba atsisakyti transliuoti.
C, JAVA. Turi pagrindinį standartinį realiųjų skaičių tipą float ir dvigubo tikslumo tipą
double.

5.1.7. Trivialūs domenų tipai

Iš nagrin÷tų duomenų tipų mažiausią reikšmių skaičių tur÷jo loginis duomenų tipas – tik dvi.
Ar duomenų tipas gali tur÷ti dar mažiau reikšmių, pavyzdžiui, vieną arba n÷ vienos (t. y., reikšmių
skaičių, lygų nuliui)? Teoriškai į šį klausimą reik÷tų atsakyti. Juk galima aprašyti vardinį duomenų
tipą, turintį vieną reikšmæ, o jeigu kalboje n÷ra ribojimų d÷l tuščio reikmių sąrašo vardinio tipo
apraše – ir neturintį n÷ vienos reikšm÷s. Tačiau praktiškai tokie duomenų tipai nebūtų naudingi.
Vieną reikšmæ turinčio tipo kintamieji gal÷tų įgyti tik tą vienintelæ reikšmæ ir taptų lyg ir
konstantomis, žyminčiomis tą vienintelæ reikšmæ, nes n÷ra kitos reikšm÷s kuria būtų galima
pakeisti ją reikšmæ. Programavimo kalbose vartojami netgi standartiniai duomenų tipai, turintys tik
vieną reikšmæ. Tokie duomenų tipai padeda išreiškšti tam tikras abstrakcijas ar apibendrinimus.
Tipas void, turintis vienintelæ reikšmæ empty yra vartojamas Algole-68, C ir kitose kalbose.

100
Algole-68 kiekviena algoritmin÷ fraz÷, ar tai būtų reiškinys, ar sakinys, ar funkcija, ar
procedūra įgyja reikšmæ. Reiškinio arba funkcijos tipas bei reikšm÷ akivaizdūs. Na, o sakinys arba
procedūra laikoma kad yra tipo void ir įgyja reikšmæ empty.
Kalbose C ir JAVA, panašiai kaip ir Algole-68, procedūra prilygsta void tipo funkcijai.
Trivialų duomenų tipą, turintį tik vieną reikšmæ, galima gretinti su tuščiu sakiniu – pastarasis
nenurodo jokio veiksmo. Tačiau, kaip žinome, ir tuščias sakinys kartais naudojamas (pvz., žymei
parašyti programos arba jos bloko pabaigoje).

5.2. Rodykl÷s

Rodykl÷s atitikmuo kompiuteryje yra adresas. Tai žemo lygio programavimo konstrukcija,
kurios vaidmuo duomenų struktūrose dažnai gretinamas su nukreipimo sakinio vaidmeniu valdymo
struktūrose. Iš tikrųjų rodykl÷s yra klaidų šaltinis programose, nes programas, kuriose gausu
rodyklių, yra sunku skaityti.
Pirmosios programavimo kalbos: Fortranas, Kobolas, Algolas-60 rodyklių netur÷jo. Jos
atsirado kartu su antros kartos programavimo kalbomis: PL/1, Algolu-68, Paskaliu.
Rodykl÷s tipas. Rodykl÷s reikšm÷ yra adresas. Taigi, jeigu duomenų tipą apibūdintume
vien reikšmių aibe, tai išeitų, kad visos rodykl÷s priklauso tam pačiam duomenų tipui. Tod÷l
ankstesn÷se programavimo kalbose, kai mažiau d÷mesio buvo skiriama duomenų tipų kontrolei
(pvz., PL/1), buvo laikoma, kad visos rodykl÷s priklauso tam pačiam duomenų tipui. Toks rodyklių
traktavimas nesiderina su 5.2 skyrelyje pateiktu adresavimo gylio modeliu. O svarbiausia –
negalima kontroliuoti duomenų tipų. D÷l to beveik visose programavimo kalbose rodykl÷s yra
tipizuojamos. Jų tipas – tai jų rodomų duomenų tipas, tik rodykl÷ turi kitokį adresavimo gylį ir
pagal mūsų pateiktą modelį ji būtų kitokios rūšies, negu jos rodomas duomenų tipas. Rodyklių
kontrol÷ pagal jų rodomų dumenų tipą įgalina aptikti daugelį klaidų programoje, bet programų
skaitymą nedaug palengvina.
Rodykl÷s, kaip reikšm÷s, duomenų tipas yra paprastasis. Kiekvienai rodyklei skiriama tiek
vietos atmintin÷je, kiek reikia adresui. Tačiau rodykl÷s rodoma reikšm÷ gali būti bet kokio tipo.
Rodykl÷ atlieka ir jos rodomos reikšm÷s vardo funkciją. Tod÷l rodoma reikšm÷ gali netur÷ti
vardo kito vardo ir gali bųti identifikuojama tik rodykle. Dažniausiai taip ir būna. Taigi, programoje
atsiranda du duomenų pasauliai: vienas, kurie reikšm÷s identifikuojamos kintamųjų vardais,
aprašytais programoje ir kitas, kurių reikšm÷s identifikuojamos rodyklių reikšm÷mis. Rodykl÷s
reikšmes įgyja ir keičia, kai atliekama programa. Vadinasi, rodyklių rodomų reikšmių vardai yra
sukuriami dinamiškai. O vieta reikšmei atmintin÷je gali būti paskirta tik tada, kai galima ją
nurodyti, t. y., kai ji turi vardą. Tod÷l ir atmintin÷s skirstymas rodyklių rodomiems duomenims
vyksta dinamiškai. Transliatorius atmintinę, skirtą duomenims, dalina į dvi zonas. Vieną zoną
kintamųjų, kurių vardai aprašyti programoje, reikšm÷ms saugoti, kitą – rodyklių rodomoms
reikšm÷ms saugoti (5.2 pav.).

101
5.2 pav. Atminties paskirstymas duomenims

Pirmojoje zonoje atmintin÷ skirstoma d÷klo principu, nes čia poreikis atmintinei atsiranda ir
dingsta tvarkingai: kai kreipimasi į programos bloką (funkciją, procedūrą, modulį) ir jis pradeda
veikti, tai jame aprašytiems kintamiesiems paskiriama vieta atmintin÷je, kai blokas baigia darbą,
paskyrą jo atmintinei galima anuliuoti. O blokai baigia darbą atvirkščia tvarka, negu jie prad÷jo
darbą. Taigi anuliuoti jiems skirtą vietą atmintin÷je galima d÷klo principu.
Antrojoje, rodyklių tvarkomoje zonoje, poreikis atmintinei atsiranda programos bloko
vykdymo metu. Ir šią atmintin÷s zoną galima skirstyti iš eil÷s. Tačiau, kada kurią paskyrą anuliuoti,
nusp÷ti negalima. Laisvos vietos gali atsirasti bet kurioje šios zonos vietoje. Tod÷l ši atmintin÷s
zona vadinama dinamine, o angliškai heap.
Su dinamine atmintin÷s zona yra dar viena problema. Tą pačią reikšmę gali rodyti kelios
rodykl÷s. Tod÷l, kai nustoja ją rodyti kuri nors rodykl÷, dar neaišku, ar tos reikšm÷s nerodo kuri
nors kita rodykl÷, t. y., ar jau galima atmintin÷s paskyrą tai reikšmei anuliuoti. D÷l tos pačios
priežasties negalima anuliuoti paskyrų toms reikšm÷ms, kurias rodo rodykl÷s, aprašytos tame bloke,
kuris baigia darbą: tas pačias reikšmes gali rodyti kitų, dar veikiančių, blokų rodykl÷s. Tod÷l
dinaminei atminties zonai sutvarkyti vartojamos programos, vadinamos šiukšlių rinktuvais. Kai visa
abiems zonoms skirta atmintin÷s vieta užsipildo ir nebelieka vietos naujoms reikšm÷ms,
iškviečiamas šiukšlių rinktuvas, kuris išanalizuoja dinaminę atmintin÷s zoną, suranda niekieno
neberodomas reikšmes (šiukšles) ir jų užimtas atmintin÷s vietas grąžina į laisvų vietų sąrašą. Be to
šiukšlių rinktuvai pertvarko dinamin÷s atminties zoną taip, kad visos užimtos vietos būtų surinktos į
zonos pradžią, o po jų eitų ištisin÷ laisvos atmintin÷s zona.
Rodykl÷s tipas turi dar vieną papildomą reikšmę, nerodančią jokios reikšm÷s. Ši reikšm÷
žymima nil, null ir kitaip.
Rodykl÷s gali būti tiesiogiai rašomos į programą. Netiesiogiai jos panaudojamos rekursiniams
duomenų tipams bei objektams sudaryti.

102
Realizacija programavimo kalbose
Paskalis. Rodykl÷s tipas oficialiai (Paskalio kalbos standarte) žymimas rodykle aukštyn ↑.
Asmeninių kompiuterių ab÷c÷l÷je tokio simbolio n÷ra, tod÷l realizacijose jis pakeičiamas simboliu
^. Nieko nerodanti rodykl÷ žymima baziniu žodžiu nil. Rodyklių rodomos reikšm÷s sudaro atskirą
pasaulį – rodykl÷s negali rodyti vardais žymimų reikšmių. Tuo tarpu Turbo Paskalyje rodykl÷ gali
rodyti ir į vardu pažym÷tą kintamąjį.
Pavyzdys.
var i: integer;
r: ^integer;
...
p := addr(i).
Funkcijos addr reikšm÷ yra jos argumento adresas (rodykl÷).
Paskalis turi standartines operacijas new(r) ir dispose(r). Pirmoji sukuria rodykl÷s rodomą
reikšmę ir skiria jai vietą atmintin÷je, antroji – rodyklei priskiria reikšmę nil ir panaikina jos rodytą
reikšmę, o tos reikšm÷s užimtą vietą grąžina į laisvų atmintin÷s vietų sąrašą. Taigi Paskalyje
nenumatytas šiukšlių rinktuvas. Nereikalingų vietų išlaisvinimu turi pasirūpinti progranuotojas.
Ada. Rodykl÷s tipas žymimas baziniu žodžiu access. Ados autoriai aiškino, kad ilgesnis
bazinis žodis lengviau pastebimas, negu rodykl÷l÷ ar stogelis ir tod÷l mažesn÷ klaidų tikimyb÷.

5.3. Struktūriniai duomenų tipai

5.3.1. Rinkinys

Rinkinio tipas sujungia kelias (įvairių tipų) reikšmes − komponentus į vieną reikšmæ.
Kiekvienas komponentas sudaro vieną rinkinio reikšm÷s lauką. Laukai identifikuojami selektoriais.
Rinkiniai realizuoti visose universaliose programavimo kalbose, tik skirtingai vadinami −
dažniausiai įrašais (angl. record) arba struktūromis (angl. structure). Į rinkinį patogu jungti kelias
tarpusavy susijusias reikšmes (pvz., apibūdinančias vieną objektą), kai dažnai tenka operuoti
visomis reikšm÷mis (rinkinio komponentais) drauge.
1 pavyzdys. Data.
Type metai = 1900..2000;
m÷nuo = (sausis, vasaris, kovas, balandis, geguž÷, birželis,
liepa, rugpjūtis, rugs÷jis, spalis, lapkritis, gruodis);
diena = 1..31;
data = record
mt: metai;
mn: m÷nuo;
dn: diena

103
end.
2 pavyzdys. Lošimo korta.
korta = record
spalva: (čirvai, būgnai, kryžiai, vynai);
vert÷: (devynak÷, dešimtak÷, valetas, dama, karalius, tūzas)
end.
Pirmajame pavyzdyje rinkinio komponentų tipai aprašyti atskirai, antrajame – paties rinkinio
apraše.
Rinkinio komponentai identifikuojami selektoriais. Pavyzdžiui, datos tipo kintamojo dt
komponentas dn būtų užrašomas šitaip:
dt.d.
Selektorių vardus (pvz., dn, spalva) galima vartoti tik kartu su rinkinio tipo, kuriame yra
aprašytas tas selektorius, kintamojo vardu. Vadinasi, selektorių vardai lyg ir lokalizuoti rinkinio tipo
apraše.
Kai vienoje vietoje atliekami veiksmai su keliais to paties rinkinio komponentais, tai tų
komponentų įvardyjimą galima suprastinti. Tam tikslui programavimo kalbose yra numatytos
konstrukcijos. Paskalio kalba šiam tikslui turi prijungimo sakinį. Tai struktūrinis sakinys kurio
antrašt÷je nurodyto rinkinio tipo kintamojo vardą galima praleisti ir rinkinio komponentus įvardyti
vien selektoriais.
3 pavyzdys. Tarkime, kad duomenų tipas data jau aprašytas (žr. 1 pavyzdį).
var dt: data;
...
with dt do
mt := 2007;
dn := 30;
x := mt + 1
end
Rinkinio reikšm÷ yra sud÷tin÷ – sudaryta iš kelių jo komponentų reikšmių. Tačiau ji
traktuojama kaip viena reikšm÷. Tod÷l rinkinio reikšmæ galima priskirti tokio paties rinkinio tipo
kintamajam, galima perduoti parametru funkcijai, procedūrai ar kitokiam moduliui. Galimas ir
rinkinio tipo reiškinys. Tačiau įprastos operacijos, kurių ir operandas (-ai) ir rezultatas būtų rinkiniai
programavimo kalbos beveik neturi, išskyrus vieną kitą atveją, kurį galima pavadinti ezotika. Mat
sunku apibr÷žti prasmingas operacijas su reikšme, kurios komponentai gali būti labai įvairūs.
Operacijomis galima pavadinti tik komponento įrašymą į rinkinį arba komponento skaitymą iš
rinkinio. Pavyzdžiui, tokie užrašai:
dt.mt := 2007
m := dt.mt
ir reikštų tokias operacijas.
Tačiau užrašą dt.mt vadiname sud÷tiniu kintamuoju, o ne operacija, nors iš tirųjų tai yra
neišreikštiniu (tiksliau – netradiciniu) pavidalu užrašyta operacija. Jeigu toks užrašas yra kair÷je

104
priskyrimo ženklo pus÷je, tai jis nurodo rinkinio komponento rašymo operaciją, jei dešin÷je –
skaitymo.
Rinkinio tipo konstantos vartojamos nedaugelyje kalbų. Taip pat retai kuri kalba turi rinkinio
(ne komponento!) skaitymo ar rašymo operacijas
Rinkinys gali būti sudarytas iš įvairių tipų komponentų. Tarp jų ir struktūrinių rinkinių,
alternatyvų. Rinkinį sudarydami iš kitų struktūrinių komponentų struktūrizuojame duomenis.

Realizacija programavimo kalbose


Algolas-68. Rinkinys vadinamas struktūra ir užrašomas šitaip (pailiustruosime datos
pavyzdžiu):
mode data = struct
metai mt;
m÷nuo mn;
diena dn
end
Rinkinio komponentas identifikuojamas selektoriumi ir po jo (ne prieš!) einančiu rinkinio tipo
kintamojo vardu, atskirtu žodžiu of, pavyzdžiui:
mt of dt
Bazinis žodis struct pabr÷žia rinkinio svarbą − jis sutapatinamas su duomenų struktūra
apskritai. Istoriškai pirmasis struktūrinis duomenų tipas buvo masyvas. Tačiau pirmosiose
programavimo kalbose buvo ribojamas masyvo elementų tipas − buvo reikalaujama, kad jis būtų
paprastasis. Tod÷l duomenų struktūrizavimas nebuvo galimas. O tokia galimyb÷ atsirado kartu su
rinkiniu.
Paskalis. Apie standartinio Paskalio rinkinį, kuris šioje kalboje vadinamas įrašu, jau
kalb÷jome. Turbo Paskalis turi ir rinkinio tipo konstantas. Tai skliaustuose išvardytos rinkinio
komponentų konstantos, pavyzdžiui,
(2007, sausis, 23)
C, JAVA. Rinkinys vadinamas struktūra:
struct T
{
T1 S1;
T2 S2;
...;
Tn Sn
}
Ada. Rinkinys vadinamas įrašu:
type T is
record S1: T1;

105
S2: T2;
...;
Sn: Tn
end record.

5.3.2. Variantinis rinkinys

Rinkinio reikšmæ sudaro keli komponentai drauge. Programavime reikalingas ir toks


duomenų tipas, kuriame būtų aprašyti keli laukai (taip kaip ir rinkinyje), tačiau jo reikšm÷ būtų
sudaryta tik iš vieno komponento ir būtų žinoma, iš kurio būtent komponento ji sudaryta. Tokio
duomenų tipo praktinis pavyzdys gal÷tų būti anketa, kai reikia atsakyti į alternatyvius klausimus,
pavyzdžiui: „Jeigu esate studentas, nurodykite kursą, jei d÷stytojas – mokslinį vardą: asistentas,
docentas, profesorius“. Atsakymui į tokį kausimą aprašyti reik÷tų min÷to duomenų tipo, kurio
apraše būtų nurodyta alternatyva (studentas ar d÷stytojas) ir dar du laukai, po vieną kiekvienai
alternatyvos reikšmei. Tačiau tokio duomenų tipo reikšm÷ tur÷tų tik vieną lauką, nes vienu metu tas
pats žmogus negali būti ir studentas, ir d÷stytojas (taip reikalauja anketa), o taip pat informacija apie
pasirinktą alternatyvą, kuri gali būti traktuojama ir kaip papildomas tokio rinkinio laukas.
Duomenų tipų teorijoje alternatyvi duomenų struktūra laikoma atskiru struktūriniu duomenų
tipu – alternatyva. Tačiau šiuolaikin÷se programavimo kalbose ji dažniausiai būna išreiškiama
rinkiniu su variantine dalimi. Min÷tą pavyzdį Paskalio kalbos rinkiniu, turinčiu tik variantinæ dalį,
būtų užrašyta šitaip:
type anketa = record case kas: (stud, d÷st) of
studentas: (kursas: 1..6);
d÷stytojas: (vardas: (asistentas, docentas, profesorius))
end
Šis rinkinys turi tris laukus: kas, studentas ir d÷stytojas.
Aprašæ šio tipo kintamąjį kintamąjį:
var aš: anketa
galime priskirti reikšmes jo laukams:
aš.kas := d÷st;
aš.d÷stytojas := docentas
Laukui studentas reikšm÷s priskirti nebereikia, nes galima tik viena alternatyva.
Programavimo kalba, turinti griežtos duomenų tipų kontrol÷s mechanizmą, netur÷tų leisti į rinkinį
įrašyti kitokią alternatyvą, negu nurodyta alternatyvos lauke, o taip pat skaityti netinkamą
alternatyvą. Deja, Paskalio kalba, kurią čia naudojome kaip pavyzdį, tolios kontrol÷s neturi. Tai
vienintel÷ silpna šios kalbos vieta duomenų tipų kontrol÷s pažiūriu (napainiokime su Turbo
Paskaliu, jis turi daugiau silpnų vietų).

106
Kiekvienas variantin÷s dalies variantas (alternatyvos tipo komponentas) suskliaudžiamas, o
skliaustuose rašomi selektoriai. Kai alternatyvos komponentas yra įrašas (tokių atvejų būna
daugiausia), tokio įrašo nereikia aprašyti atskirai − pakanka jo laukus išvardyti skliaustuose.
Pateiksime pavyzdį.
type fig = (skritulys, kvadratas, stačiakampis, trikampis);
figūra = record
plotas: real;
case f: fig of
skritulys: (r: real);
kvadratas: (a: real);
stačiakampis: (m, n: real);
trikampis: (x, y, z: real)
end;
var u: figūra.
Kintamojo u reikšm÷ turi 9 komponentus: u.plotas, u.f, u.r, u.a, u.m, u.n, u.x, u.y, u.z. Kadangi
visi komponentai yra vieno struktūros lygio (nors kai kurie ir suskliausti), tai tipo apraše tur÷jome
parinkti skirtingus jų vardus.
Alternatyvos duomenų tipas dar vadinamas žym÷tąja duomenų tipų sąjunga, nes tai iš tikrųjų
yra duomenų tipų sąjunga (panašiai, kaip ir aibių). O žym÷tąja vadinamas d÷l to, kad galimi ir
vienodų tipų laukai ir juos galima atskirti, nes jie yra pažym÷ti alternatyvos reikšme.

Realizacija programavimo kalbose.


CLU. Alternatyvos tipus išreikštiniu pavidalu turi programavimo kalba CLU. Joje yra du
alternatyvos tipo variantai: modifikuojamas, žymimas žodžiu variant, ir nemodifikuojamas − oneof
(„vienas iš“)*. Duomenų tipų aprašai, operacijos ir su jomis susietas specialus variantinis sakinys
CLU kalboje yra analogiški čia aprašytiems. Skiriasi tik žymenys.
Algolas-68. Turi nežym÷tas tipų sąjungos konstruktorių union. Pavyzdžiui, sveikųjų skaičių
ir loginio tipo sąjubga būtų aprašomas šitaip:
mode intbool = union (int, boolean)
Selektorių tipo apraše n÷ra. Komponentą galima atskirti tik pagal jo tipą. Tod÷l visų
komponentų tipai turi būti skirtingi.
Algole-68 taip pat yra specialus variantinis sakinys, įgalinantis „saugiai“ išardyti alternatyvos
tipo reikšmæ ir veiksmus išskirstyti į šakas, atitinkančias tipo apraše nurodytus komponentų tipus.
Taigi yra realizuota duomenų tipų suderinamumo kontrol÷.
C. Turi duomenų sąjungos tipą. Pateiksime pavyzdį.
union realbool
{
float r;
bool b;

107
}
Nors laukai ir turi vardus, tačiau tipų kontrol÷s n÷ra − programuotojas turi pats pasirūpinti iš
alternatyvos išimto komponento tipo nustatymu. Taigi alternatyva realizuota tik iš dalies.
Ada. Panašiai, kaip ir Paskalyje, turi rinkinį su variantine dalimi.

5.3.3. Masyvas

Masyvas yra pati seniausia ir dažniausiai vartojama duomenų struktūra. Masyvas buvo jau
pačiose pirmose programavimo kalbose. Fortrane ir Algole-60 tai buvo vienintelis struktūrinis
duomenų tipas. Iš tikrųjų masyvas yra pats tinkamiausias duomenų tipas, į kurį galima lengvai
sud÷ti labai daug komponentų. Jis neprarado svarbos ir šiuolaikin÷se programavimo kalbose.
Masyvas jungia daugelį vienodo tipo komponentų į vieną reikšmæ. Masyvo komponentai
vadinami jo elementais. Elementai nurodomi reikšm÷mis, vadinamomis indeksais, kurių tipas
vadinamas indeksų tipu. Indeksų tipas dažniausiai sveikųjų skaičių atkarpa. Programavimo kalbose,
kurios neturi atkarpos tipo, vietoj „indeksų tipo“ vartojama sąvoka „sveikųjų skaičių r÷žiai“. Ta
atkarpa gali būti labai didel÷ ir masyvas gali tur÷ti labai daug elementų. Tod÷l masyvas yra
potencialiai didesn÷ duomenų saugykla, negu rinkinys.
Indeksų tipo reikšm÷s atlieka masyvo elementų selektorių vaidmenį. Taigi galima sakyti, kad
masyvo elementai yra sužym÷ti indeksų tipo reikšm÷mis.
Masyvo aprašo pavyzdys
array [1..20] of real
Nors masyvo reikšm÷ yra sud÷tin÷ – sudaryta iš daugelio jo komponentų reikšmių, tačiau gali
būti traktuojama kaip viena reikšm÷. Tod÷l masyvo reikšmæ galima priskirti tokio paties rinkinio
tipo kintamajam, galima perduoti parametru funkcijai, procedūrai ar kitokiam moduliui. Galimas ir
masyvo tipo reiškinys. Tačiau įprastos operacijos, kurių ir operandas (-ai) ir rezultatas būtų
masyvai, vartojamos tik kai kuriose kalbose. Dažniausiai tai būna masyvo dalies (segmento)
išpjovimas arba dviejų segmentų sujungimas į vieną masyvą (žr. žemiau realizaciją Algole-68).
Panašiai kaip ir rinkinio atveju, elemento rašymą į masyvą ir skaitymą iš masyvo galima
vadinti neišreikštin÷mis indeksavimo operacijomis. Pavyzdžiui, užrašai:
m[4] := 2.5
a := m[4]
ir reikštų tokias operacijas.
Tačiau užrašą m[4] vadiname sud÷tiniu kintamuoju, o ne operacija, nors iš tikrųjų tai yra
neišreikštiniu (tiksliau – netradiciniu) pavidalu užrašyta operacija. Jeigu toks užrašas yra kair÷je
priskyrimo ženklo pus÷je, tai jis nurodo rinkinio komponento rašymo operaciją, jei dešin÷je –
skaitymo.
Masyvo tipo konstantos vartojamos nedaugelyje kalbų. Taip pat retai kuri kalba turi rinkinio
(ne komponento!) skaitymo ar rašymo operacijas
Pirmosios kartos programavimo kalbose Fortane ir Algole-60 vienintel÷ duomenų struktūra
buvo masyvas. Buvo reikalaujama, kad masyvo elementai būtų paprastojo tipo. Kad būtų galima
patogiai dirbti su kelių matmenų duomenų struktūromis, kurių elementai indeksuojami keliais

108
indeksais (pvz., matricomis), buvo vartojami daugiamačiai masyvai. Jų apraše nurodoma tiek r÷žių
porų (indeksų tipų), kiek matmenų turi masyvas, pavyzdžiui, kvadratinæ sveikųjų skaičių matricą
Mn×n galima pavaizduoti šitokiu dvimačiu Algolo-68 masyvu:
int [1: n, 1: n] M.
V÷lesn÷se programavimo kalbose, kai atsirado daugiau duomenų struktūrų, buvo atsisakyta
ribojimo, kad masyvo elementų tipas būtų paprastasis. Pasidar÷ įmanoma vieną daugiamatį masyvą
pakeisti keliais vienas į kitą įd÷tais vienmačiais masyvais. Pavyzdžiui, min÷tą matricą Paskalio
kalba galima užrašyti kaip dvimatį masyvą:
array [1..n, 1..n] of integer
arba išreikšti vienmačiu masyvu, kurio elementai taip pat masyvai:
array [1..n] of
array [1..n] of integer
Daugiamatis masyvas išliko tik kaip patogi aprašų santrumpa. Tuo tarpu teorijoje, taip pat
transliatorių sudarymo praktikoje pakanka paprastesnių vienmačių masyvų. Be to, kai
programavimo kalboje yra laisvas duomenų struktūrizavimas, tai bendru atveju masyvo matavimų
skaičių sunku motyvuotai apibr÷žti. Pavyzdžiui, neaišku, keliamačiu laikyti šitokį masyvą:
array [a..b] of
record
d: array [e..f] of integer;
g: real;
f: record
h: array [i..j] of
array [k..l] of real;
m: char
end
end.
R÷žių pastovumas. Yra daug uždavinių, kuriuose iš anksto sunku numatyti, kiek iš tikrųjų
bus duomenų, kuriuos reik÷s jungti į masyvą. Tod÷l būtų patogu dinamiškai keisti masyvo r÷žius.
Tačiau tada atsiranda tipų kontrol÷s problemų. Jeigu masyvo r÷žius laikome masyvo tipo aprašo
dalimi, tai jie turi būti pastovūs (juos pakeitæ, gausime naują duomenų tipą). Tokiu atveju daugelį
klaidų, susijusių su masyvo r÷žių pažeidimu, galima aptikti transliavimo metu, kontroliuojant
duomenų tipus.
Jeigu masyvo r÷žiai keičiami atliekant programą, tai tada juos reikia laikyti masyvo reikšm÷s
(bet ne masyvo tipo) dalimi. Masyvo tipas pasidaro pernelyg bendras − tam pačiam duomenų tipui
priklauso masyvai, turintys įvairius r÷žius, ir statin÷s tipų kontrol÷s galimyb÷s sumaž÷ja.
Realizacija programavimo kalbose
Paskalis. Masyvo indeksų tipas gali būti bet koks diskretusis duomenų tipas – vardinis,
sveikųjų skaičių, loginis, simbolių – arba jų atkarpa.
Laikomasi griežtos tipų kontrol÷s principų. Tod÷l priimta, kad r÷žiai turi būti pastovūs ir
išreiškiami konstantomis.

109
Programavimo praktikoje labiau vertinamas išraiškos priemonių lankstumas ir darbo
patogumas, negu griežta tipų kontrol÷. Tod÷l pastovūs Paskalio kalbos masyvų r÷žiai susilauk÷
daug kritikos. Tai laikoma svarbiausiu kalbos trūkumu. Į tai buvo atkreiptas d÷mesys
standartizuojant kalbą. Kalbos standarte yra numatyta galimyb÷ nenurodyti formaliųjų funkcijų ir
procedūrų parametrų − masyvų r÷žių. Yra atskira formaliųjų parametrų rūšis − masyvų parametrai.
Tokių masyvų r÷žiai nurodomi vardais, pavyzdžiui:
procedure p (var masyvas: array [m..n: integer] of real).
Čia m ir n gali būti bet kokie sveikieji skaičiai. Kreipimosi į procedūrą metu jie įgyja faktinių
parametrų − masyvų r÷žius. Pavyzdžiui, jeigu yra aprašyti du masyvai:
var mas1: array [1..10] of real;
mas2: array [100..1550] of real,
tai kreipinys p(mas1) r÷žiams suteikia reikšmes: m=1 in n=10, o kreipinys p(mas2) −
reikšmes m=100 ir n=1550.
Čia r÷žiai atlieka konstantų, kurioms reikšm÷s suteikiamos kreipimosi į procedūrą metu,
vaidmenį. Tačiau sintaksiškai r÷žiai apibr÷žiami kaip daugikliai (daugiklis yra reiškinio dalis). Bet
ir taip apibr÷žius išsaugoma pagrindin÷ konstantos savyb÷ (jų reisti negalima).
Algolas-68. Kai masyvo indeksų viršutinis r÷žis mažesnis už apatinį, tai laikoma, kad, kad ir
toks masyvas turi reikšmæ. Tiktai jos panaudoti negalima. Tai mistin÷ reikšm÷ vadinama šm÷kla
(angl. ghost).
Masyvo r÷žiai gali būti fiksuoti arba slankūs. Pastaruoju atveju masyvo tipo apraše
nenurodomas vienas arba net abu r÷žiai. Masyvo apraše r÷žiai nenurodomi. Jie atsiranda tiktai tada,
kai kintamajam priskiriama masyvo tipo reikšm÷. Masyvas tada ir įgyja tos, priskiriamos, reikšm÷s
r÷žius. Keičiasi ir masyvo tipo reikšm÷s sąvoka − dabar laikoma, kad masyvo reikšmæ sudaro ne tik
jo elementai, bet ir r÷žiai. Kol operuojama atskirais masyvo elementais, r÷žiai išlieka pastovūs −
indeksavimo operacijos jų pakeisti negali. Kai kintamajam priskiriama nauja masyvo tipo reikšm÷,
jo r÷žiai v÷l pasikeičia.
Masyvo r÷žius galima didinti arba mažinti iš abiejų pusių. Ir šiuo atveju pasikeitus r÷žiams
gaunamas naujas masyvas.
Ada. Masyvo tipų aprašuose r÷žių galima nenurodyti, pavyzdžiui:
type A is array (INTEGER range <>) of E.
Čia simboliai range <> pasako, kad indeksai turi būti sveikųjų skaičių atkarpa. Tačiau kokie
konkretūs r÷žiai − nenurodoma. Jie nurodomi kintamųjų aprašuose, pavyzdžiui:
MASYVAS: A(1..20);
MAS: A(−5..5).
Taigi to paties tipo masyvai (šiuo atveju tipo A) gali skirtis r÷žių reikšm÷mis.
Formaliojo procedūros parametro r÷žiai taip pat gali būti nenurodomi. Kreipiantis į procedūrą,
jie įgyja faktinio parametro r÷žius.
C, JAVA. Masyvo indeksai gali būti tik sveikieji skaičiai. Be to, pirmasis r÷žis visada lygus
nuliui. Jo ir nurodyti nereikia.
CLU. Šioje kalboje r÷žių keitimo problema sprendžiama labai įdomiai. Čia masyvų r÷žiai gali
būti keičiami dinamiškai. Aprašant masyvo tipą, jie išvis nenurodomi. Yra operacija, sukurianti
tuščią masyvą su nurodytu apatiniu r÷žiu. (Laikoma, kad tuščio masyvo viršutinis r÷žis yra vienetu

110
mažesnis negu apatinis.) Kitos operacijos masyvą gali „auginti“ prijungdamos naują elementą
masyvo pradžioje (tada apatinis r÷žis vienetu mažinamas) arba masyvo pabaigoje (tada viršutinis
r÷žis vienetu didinamas).

5.3.4. Kiti duomenų tipai

Aptartą duomenų tipų rinkinį galima vadinti baziniu. Iš jo patogu sudaryti visus kitus
praktikoje pasitaikančius duomenų tipus. Galima sakyti, kad tai minimalus jų rinkinys.
Programavimo kalbose vartojama daugiau duomenų tipų ir struktūrų. Trumpai pakalb÷sime apie
keletą iš jų.
Simbolių eilut÷. Tai simbolių masyvas
eilut÷ = [ind] char.
Eilut÷s vartojamos dažnai. Daugelyje programavimo kalbų jos laikomos atskiru duomenų tipu
ir su jomis yra apibr÷žtos operacijos: dviejų eilučių sujungimas į vieną, eilut÷s dalies išskyrimas,
eilut÷s dalies pakeitimas ir pan.
Bitų eilut÷ (kodas). Tai bitų rinkinys. Kiekvienas duomuo koduojamas bitais. Žemo lygio
programavimo kalbose galima operuoti su duomenį vaizduojančiais bitais. Prasmingo duomens
(pvz., realiojo skaičiaus) požiūriu tokios operacijos neturi prasm÷s. Tačiau sistemin÷se programose
(pvz., tvarkykl÷se) operuojama su kodais. Tod÷l ir kai kurios aukšto lygio programavimo kalbos turi
tokį duomenų tipą, o kartais ir galimybæ operuoti su prasmingų duomenų tipų reikšmių kodais, be
abejo, pažeidžiant duomenų tipų kontrol÷s normas.
Bitų eilut÷s reikšm÷ paprastai būna kompiuterio žodis (1, 2, 4, 8 baitai). Su bitų eilut÷mis
atliekamos login÷s operacijos (su kiekvienu bitu atskirai, kaip sakoma, pabičiui), o taip pat kodo
postūmio operacijos. Kartais kodai interpretuojami kaip neneigiami sveikieji skaičiai ir su jais
atliekamos aritmetin÷s operacijos.
Aib÷. Paskalio kalba turi aib÷s tipų konstruktorių. Jo pavidalas yra šitoks:
set of T,
čia T − aib÷s komponentų tipas.
Komponentai gali būti tik paprastojo diskrečiojo duomenų tipo ir dargi turinčio nedidelį
reikšmių skaičių. Šis ribojimas atsiranda d÷l to, kad Paskalyje numatyta labai paprasta aib÷s
realizacija loginiu masyvu:
type set = array T of boolean.
Kai masyvo indeksų tipas T turi ne daugiau reikšmių negu bitų kompiuterio žodis, tokios
aib÷s realizuojamos labai ekonomiškai − aib÷s reikšmei įsiminti pakanka vieno kompiuterio žodžio,
o aibių operacijos lengvai realizuojamos taikant visam kompiuterio žodžiui logines kompiuterio
komandas, kurios kiekvieną žodžio bitą interpretuoja kaip atskirą loginæ reikšmæ.
Čia aptartus tris duomenų prie struktūrinių duomenų tipų. Jų reikšm÷s sudarytosi iš
komponentų: simbolių eilučių – iš simbolių, bitų eilučių – iš bitų, aibių – iš aibei priklausančių
elementų. Tačiau jų komponentai gali būti ne bet kokie, o tik tam tikri paprastieji duomenų tipai.
Taigi tokios laisvos duomenų struktūrizacijos, kurią galima gauti įdedant vienas į kitą rinkinius bei
masyvus be jokių ribojimų, su šiame skyrelyje aprašytais duomenų tipais negausime. Tod÷l apie
juos kartais sakoma, kad jie yra struktūriniai duomenų tipai tik iš dalies.

111
5.3.5. Struktūrinių duomenų tipų suderinamumas

Duomenų tipų suderinamumo problema iškyla, kai reikia nustatyti, ar leistinų tipų yra dvi
reikšm÷s. Tokios reikšm÷s – du operacijos operandai, kairiosios ir dešiniosios priskyrimo sakinio
pus÷s, formalieji ir faktiniai parametrai kreipinyje į funkciją, procedūrą ar modulį.
1 pavyzdys. Turime tokius rinkinius:
type r1 = record
a: inteber;
b: boolean
end;
type r2 = record
a: inteber;
b: boolean
end;
type r3 = record
c: inteber;
d: boolean
end;
var r1k, r1kk: r1;
r2k: r2;
r3k: r3
Kurių iš čia aprašytų kintamųjų r1k, r1kk, r2k, r3k reikšm÷s yra suderinamos?
Dar daugiau abejotinų variantų galima parašyti su masyvu.
2 pavydys.
typem1 = array [1..4] of real;
typem1 = array [1..4] of real;
typem1 = array [2..5] of real;
typem1 = array [(alfa, beta, gama, delta)]
var m1k, m1kk: m1;
m2k: m2;
m3k: m3;
m4k: m4.
typem1 = array [1..4] of real;
typem1 = array [1..4] of real;
typem1 = array [1..4] of real;

112
m2 = array [
Kurių kintamųjų: m1k, m1kk, m2k, m3k, m4k reikšm÷s yra suderinamos?
Dažniausiai vartojamos dvi duomenų tipų suderinamumo sąvokos: tipų tapatumas ir tipų
ekvivalentumas.
Tipų· tapatumas. Du duomenų tipai t1 ir t2 laikomi tapačiais, jeigu jų vardai sutampa. Taip
pat duomenų tipai gali būti laikomi tapačiais, jeigu vieno duomenų tipo vardas aprašomas kito tipo
vardu, pavyzdžiui,
type t1 = t2.
ir, aišku, programavimo kalba turi tokią aprašų galimybæ.
Visi abiejuose pavyzdžiuose aprašyti duomenų tipai n÷ra vienas kitam tapatūs. Tod÷l tik
dviejų porų kintamųjų:
r1k, r1kk ir
m1k, m2kk
reikšm÷s yra tapačių duomenų tipų.
Tipų ekvivalentumas. Tipai laikomi ekvivalenčiais, jeigu jų reikšm÷s turi vienodą struktūrą.
Tod÷l tipų ekvivalentumas dar vadinamas struktūriniu duomenų tipų ekvivalentumu. Aišku, kad
pavyzdžiuose aprašyti duomenų tipai r1 ir r2, o taip pat m1 ir m2 yra struktūriškai ekvivalentūs.
Taigi, jau turime du trejetus kintamųjų, kurių reikšm÷s ekvivalenčios:
r1k, r1kk, r2k ir
m1k, m1kk, m2k.
Ar struktūriškai ekvivalentūs kiti pavyzdžiuose pateikti duomenų tipai, priklausys nuo
susitarimo, ar duomenų struktūros komponentus identifikuojančius elementus (rinkinio laukų
vardus, masyvo elementų indeksus) laikysime duomenų struktūros dalimi. Jeigu taip, tai tada tokie
duomenų tipai nebus ekvivalentūs, jei ne – visi 1 pavyzdžio rinkinių duomenų tipai bus
ekvivalentūs (taigi ir kintamųjų r1k, r1kk, r2k, r3k reikšm÷s ekvivalenčios), visi 2 pavyzdžio
masyvų duomenų tipai bus ekvivalentūs (taigi ir kintamųjų m1k, m1kk, m2k, m3k ir m4k reikšm÷s
bus ekvivalenčios).
Programavimo kalbose įvairiai apib÷žiamas duomenų tipų suderinamumas
Realizacija programavimo kalbose
Paskalis. Standartiniame Paskalyje laikomasi duomenų tipų tapatumo reikalavimų. Tuo tarpu
Turbo Paskalyje funkcijų arba procedūrų formalieji ir faktiniai parametrai derinami pagal
ekvivalentumo taisykles. Bet koks indeksų tipas faktinio parametro, kuris yra masyvas, funkcijoje
arba procedūroje virsta sveikųjų skaičių atkarpa, kurios apatinis r÷žis lygus vienetui.

5.4. Objektai
5.4.1. Objektinio programavimo samprata

Objektai ir jų klas÷s pirmą kartą buvo panaudoti programavimo kalboje Simula-67. Tačiau
šiuolaikin÷ objektinio programavimo paradigma atsirado kartu su programavimo kalba Smalltalk.

113
V÷liau buvo suprojektuota daugiau objektinio programavimo kalbų (Eiffel ir kt.). Objektinio
programavimo konstrukcijomis buvo papildytos ir kitų, jau egzistuojančių programavimo kalbų
naujos versijos. Šitaip atsirado objektai kalboje C++ bei Turbo Paskalyje.
Objektinis programavimas yra atskira programavimo šaka. Šios knygos orientacija yra
procedūrinis programavimas. Tod÷l pabandysime tik trumpai apžvelgti objektinio programavimo
id÷ją iš procedūrinio programavimo pozicijų.
Procedūriniame programavime operuojama su reikšm÷mis, o objektiniame – su objektais.
Tod÷l pažvelkime į reikšmių ir objektų santykį.
Procedūriniame programavime į pirmą vietą iškyla kintamojo sąvoka. Kintamieji įgyja
reikšmes. Reikšmių pavyzdžiai − skaičiai, login÷s reikšm÷s.
Svarbiausias reikšmių bruožas yra jų pastovumas. Reikšm÷s egzistuoja nuolat. Jos
neatsiranda, nesikeičia ir neišnyksta. Imkime, pavyzdžiui, skaičių 5. Kad ir kokius atliktume
skaičiavimus, penketas netaps nei ketvertu, nei šešetu, nei kokiu nors kitu skaičiumi. Kai atliekame,
pavyzdžiui, sud÷ties operacija 2+3, tai sakome, kad gauname 5. Tačiau sud÷ties operacija naujo
skaičiaus 5 nesukuria, taip pat nesunaikina skaičių 2 ir 3. Skaičiai 2, 3 ir 5 egzistavo prieš atliekant
sud÷tį, egzistuoja ir ją atlikus. Matematin÷ lygyb÷ 2+3=5 nurodo tik ryšį tarp skaičių 2, 3 ir 5,
išreikštų sud÷ties veiksmu.
Beprasmiška kalb÷ti apie reikšmių egzempliorius, o kartu ir reikšmių kopijavimą. Kiekvienos
reikšm÷s egzistuoja vienintelis egzempliorius.
Sakome, kad sud÷ję 2 ir 3, taip pat 1 ir 4 gausime tą patį skaičių, o ne du naujus lygius
skaičius. Šis pasakymas ir pabr÷žia, kad kiekvienas skaičius yra vienintelis.
Reikšm÷ − tai matematin÷ abstrakcija. Viena abstrakcija apibendrina daugelio realaus
pasaulio daiktų savybes. Pavyzdžiui, skaičius 2 reiškia du daiktus, t. y. daiktų porą. Tai viena visų
galimų porų abstrakcija nepriklauso nuo to, kiek pasaulyje šiuo metu yra porų, kiek jų atsiranda,
kiek išnyksta. D÷l pastovumo reikšm÷s dar vadinamos konstantomis.
Kintamieji įgyja reikšmes. Jos gali būti keičiamos. Kintamieji modeliuoja kompiuterio
atmintinæ. Jie atspindi šiuolaikinių fon Noimano tipo kompiuterių veikimo principą: duomenys −
kintamųjų reikšm÷s − imami iš atmintin÷s, iš jų skaičiuojamos naujos kintamųjų reikšm÷s ir v÷l
rašomos į atmintį. Taigi tarp kompiuterio ir jo atminties nuolat cirkuliuoja duomenys per siaurus
„kintamųjų reikšmių“ vartus, kurie kartais taikliai palyginami su butelio kakleliu.
Sakome, kad priskyrimo sakiniu galima pakeisti kintamojo reikšmæ. Iš tikrųjų keičiama ne
pati reikšm÷, o kintamojo sąsaja su reikšme: sąsaja su viena reikšme nutraukiama ir sukuriama
nauja sąsaja su kita reikšme.
Realiame pasaulyje susiduriame su žemesnio lygio abstrakcijomis − objektais. Objektas − tai
tam tikro realaus daikto modelis, atspindintis tam tikras jo savybes.
Realus pasaulis dinaminis. Jo daiktų būsenos nuolat keičiasi. Tie pokyčiai turi būti atspindimi
ir daikto modelyje − objekte. Taigi objektai turi keistis.
Tarkime, kad objektas a yra konkretaus asmens (pvz., Jono) modelis. Asmuo gali būti
apibūdinamas įvairiais atributais: gimimo metai, gyvenamoji vieta, šeimynin÷ pad÷tis ir t. t. Vieni
atributai yra pastovūs (pvz., gimimo metai), kiti − gali keistis (pvz., gyvenamoji vieta).
Objektą galima įsivaizduoti kaip rinkinį, kurio kiekvienai savybei (charakteristikai,
parametrui) skiriamas atskiras laukas.
Su tuo pačiu objektu gali būti susiejami keli kintamieji. Pavyzdžiui, duomenys apie tą patį
asmenį gali būti reikalingi personalo skyriui, policijos skyriui, bankui ir kitoms įstaigoms. Būtų

114
patogu, kad visi duomenys apie asmenį būtų sukaupti viename objekte (viename duomenų
rinkinyje), o iš visų įstaigų būtų nuorodos į tą vieną objektą. Tada pasikeitus duomenims apie
konkretų asmenį, pakaktų pakeisti tik vieną, tą asmenį vaizduojantį, objektą. Visų programų, t. y.
visi jose aprašyti kintamieji lyg ir dalijasi tuo pačiu objektu. Panašiai ir su reikšm÷mis. Daugelis
kintamųjų gali įgyti tą pačią reikšmæ. Skirtumas tik toks, kad reikšm÷s nesikeičia, o objektai
keičiasi. Objektai gali būti sukurti ar sunaikinti.
Objekto struktūra ir savyb÷s apibr÷žiamos klas÷s apraše. Santykis tarp klasių ir objektų yra
analogiškas santykiui tarp duomenų tipų ir reikšmių:
klas÷ duomenų tipas
klas÷s aprašas duomenų tipo aprašas
objektas reikšm÷
Šiokia tokia specifika, taip pat naujas terminas („klas÷“ vietoj „duomenų tipo“) atsirado tod÷l,
kad, kaip buvo min÷ta, objektai skiriasi nuo reikšmių.
Klas÷s aprašą sudaro šios pagrindin÷s dalys:
klas÷s vardas;
parametrai;
matomumas;
paveld÷jimas;
atributai.
Svarbiausia klas÷s aprašo dalis yra atributų aprašai. Atributai apibūdina reikšmių duomenų
tipą ir operacijas su tomis reikšm÷mis. Operacijos išreiškiamos funkcijomis arba procedūromis.
1 pavyzdys. Taškas plokštumoje. Atributai:
sukurti;
x: real;
y: real;
atstumas: real; {iki koordinačių centro}
perkelti (xx, yy: real).
Kaip jau min÷jome, objektai keičiasi. Norint, kad objektas prad÷tų egzistuoti, reikia jį sukurti.
Tod÷l kiekvienas objektas turi tur÷ti sukūrimo operaciją, kuri turi būti aprašyta toje klas÷je, kuriai
priklauso objektas.
Pavyzdyje pateikta operacija sukurti gali tur÷ti parametrus, pavyzdžiui, sukuriamo taško
koordinates. Bet gali jų ir netur÷ti, laikant, kad naujai sukurto taško abi koordinat÷s yra lygios
nuliui. Kai taškas bus sukurtas, tada jo koordinates bus galima pakeisti.
Laukai x ir y yra taško koordinat÷s, o atstumas − atstumas iki koordinačių centro. Tai
konkretaus taško atributai. Juos galima panaudoti programoje, t. y. paklausti konkretaus objekto
(taško), koks jo x, koks y arba koks atstumas. Kaip šiuos atributus realizuoja kompiuteris − ar
duomenų tipais, ar funkcijomis, vartotojas netur÷tų žinoti. Ko gero, koordinat÷s yra realiųjų skaičių
reikšm÷s, o atstumas yra funkcija, pavyzdžiui, šitokia:
function atstumas: real;
begin

115
atstumas := sqrt(x∗x + y∗y)
end.
Pakeisti objekto būseną galima panaudojant procedūras − šiuo atveju procedūrą perkelti. Jos
aprašas gali būti šitoks:
procedure perkelti (xx, yy: real);
begin
x := x + xx;
y := y + yy
end.
Vartotojas procedūrą turi atskirti nuo kitų atributų, kadangi kreipinys į jį rašomas atskiru
sakiniu, tuo tarpu kiti atributai yra reikšm÷s, ir jos vartojamos reiškiniuose.
2 pavyzdys. Asmuo. Atributai:
sukurti ...;
vardas: string;
pavard÷: string;
gimimo_metai: 1800..2010;
amžius: 1..200;
gyvenamoji_vieta: adresas;
pakeisti_gyvenamąją_vietą (naujas_adresas: adresas);
t÷vas: asmuo;
motina: asmuo;
sutuoktinis: asmuo;
panaikinti.
Šiame pavyzdyje yra trys naujos rūšies atributai: t÷vas, motina ir sutuoktinis. Tai objektai. Jie
priklauso tai pačiai klasei, kuri ir aprašoma. Gauname lyg ir rekursiją. Tačiau vienas objektas negali
būti kito objekto dalis. Bet atributai gali būti ir objektai. Bet koks aprašas reiškia, toks objektas
egzistuoja nepriklausomai nuo aprašomo objekto, o šiame objekte yra tik nuoroda į kitą objektą.
Panašiai kaip ir gyvenime. Kokioje nors apskaitos kortel÷je apie asmenį (sakysim, personalo arba
pasų skyriuje) yra visi duomenys apie mano asmenį. Tarp jų gali būti ir t÷vų pavard÷s bei vardai.
Tačiau asmens kortel÷je t÷vai yra tik identifikuojami, bet nerašomi visi duomenys apie juos. Jų
duomenys yra jų asmenin÷se kortel÷se.
Į to paties asmens objektą gali būti daug nuorodų iš įvairių klasių objektų. Tačiau vieno
asmens objektas yra vienintelis. Jeigu pasikeis kuris nors asmens atributas, tai tą atributą reik÷s
pakeisti tik vieninteliame tą asmenį aprašančiame objekte. Visi kiti objektai, kuriuose yra nuorodos
į tą asmenį, prireikus gaus tą pačią, naujausią, informaciją apie jį. Štai d÷l šitokios priežasties
objektiniame programavime išvengiama klaidų, susijusių su ne tos (pasenusios) reikšm÷s
panaudojimu.
Kaip jau galima nusp÷ti, nuorodos (klasių atributai) realizuojamos rodykl÷mis. Tačiau čia
rodykl÷s pasl÷ptos ir d÷l to sumaž÷ja klaidų pavojus.

116
Asmens (klas÷s objekto) sukūrimo operacija gali būti aprašyta įvairiai (d÷l to ties ja raš÷me
daugtaškį). Galima jos parametruose nurodyti visus konkretaus asmens duomenis, galima sukurti ir
„tuščią“ asmenį, t. y. lyg ir į asmenų kartoteką įd÷ti naują tuščią kortelæ. Tokiu atveju reik÷tų
operacijų (naujų atributų), užrašančių duomenis į asmens objektą (t. y., į jo kortelæ).
Jeigu yra objekto sukūrimo operacija, reikia tur÷ti ir objekto sunaikinimo operaciją. Šios
operacijos gali būti du variantai: sunaikinamas objektas arba sunaikinama nuoroda į duotą objektą.
Pavyzdžiui, sutuoktiniams išsiskyrus, reik÷tų panaikinti vyro objekte nuorodą į (buvusios) žmonos
asmenį, o žmonos objekte − į (buvusio) vyro asmenį. Tuo tarpu abu asmenys, vadinasi, ir juos
aprašantys objektai egzistuoja ir toliau.
Operacijų algoritmai priklauso ne klasei, o objektui. Galima įsivaizduoti, kad kuriant objektą
visas jo operacijas atliekantys algoritmai nukopijuojami iš klas÷s aprašo į objekto laukus. Taigi, kai
objektas sukuriamas, jis tampa visiškai savarankiškas, nepriklausomas nuo klas÷s, nes turi viską: ir
duomenis, ir algoritmus.
Kai atliekama programa, objektai sąveikauja vienas su kitu. Jie siunčia vienas kitam
pranešimus. Pranešime nurodoma, kokias operacijas turi atlikti jo gav÷jas (objektas). Tod÷l
objektiniame programavime galima sudaryti visą programą vien iš objektų. Panašiai kaip
procedūriniame programavime galima sudaryti visą programą vien iš procedūrų (procedūros
sąveikauja viena su kita, keičiasi duomenimis).
Operacijų priklausomyb÷ objektui daro įtaką ir operacijų antrašt÷ms bei kreipiniams į jas.
Operacijos antrašt÷je nebereikia nurodyti formalaus parametro − to objekto, kuriam priklauso
operacija, t. y. operacijos savininko. Atidus skaitytojas tur÷jo pasteb÷ti, kad trūksta parametrų
pavyzdžiuose. O jeigu n÷ra formalaus parametro, tai neturi būti ir jį atitinkančio faktinio parametro
kreipinyje į operaciją. Tačiau kreipinys gali būti ne tame pačiame objekte, kuriame yra operacija (o
taip dažniausiai ir būna), tod÷l kreipinyje reikalinga informacija, identifikuojanti objektą. Tiktai
objektas, kuriam priklauso operacija, turi kitokį, išskirtinį statusą − jis traktuojamas kaip operacijos
savininkas, o ne kaip parametras. Tod÷l kreipinyje prieš operacijos vardą rašomas objekto, kuriam
priklauso operacija, vardas ir nuo operacijos vardo skiriamas tašku.
3 pavyzdys. Taškas plokštumoje papildytas atributu atstumas_b − procedūra, nustatančia
atstumą iki kito taško (b).
sukurti;
x: real;
y: real;

atstumas: real {iki koordinačių centro}


function atstumas: real;
begin
atstumas := sqrt (x∗x + y∗y)
end;

atstumas_b (b: taškas): real; {iki taško b}


function atstumas_b (b: taškas): real;
var dx, dy: real;

117
begin
dx := b.x − x;
dy := b.y − y;
atstumas b := sqrt(dx∗dx + dy∗dy)
end;

perkelti (xx, yy: real);


procedure perkelti (xx, yy: real);
begin
x := x + xx;
y := y + yy
end;
Tarkime, kad turime du klasei taškas priklausančius objektus
a, b: taškas.
Ir duomenys, ir operacijos bus identifikuojamos visiškai taip pat, kaip įrašų laukai:
a.sukurti { sukuriamas taškas a }
b.sukurti; { ir taškas b koordinačių centre };
a.perkelti (3.5, 6.0) { dabar a.x = 3.5, a.y = 6.0 };
a.perkelti (−1.0, 1.5) { dabar a.x = 2.5, a.y = 7.5 };
b.perkelti (4.2, 0) { dabar b.x = 4.2, b.y = 0 } ;
b.perkelti (0, 7.3) { dabar b.x = 4.2, b.y = 7.3 };
i := a.atstumas { taško a atstumas iki
koordinačių centro };
j := b.atstumas { taško b atstumas iki
koordinačių centro };
j := a.atstumas_b (b) { atstumas nuo duoto taško (t. y. a)
iki kito taško, šiuo atveju b };
j := b.atstumas_b (a) { dabar atstumą skaičiuoja taškas
b nuo savæs iki taško a };
i := a.x { taško a koordinat÷ x }
j := a.y { taško a koordinat÷ y }
i := b.x { taško b koordinat÷ x }
j := b.y { taško b koordinat÷ y }
Šiuos pavyzdžius pateik÷me nor÷dami paryškinti specifinį požiūrį į veiksmus, tampriai
siejamus su objektu − operacijų šeimininku.

118
Procedūriniame programavime sakoma: „Reikia atlikti veiksmą x su duomenimis y“.
Objektiniame programavime sakoma: „reikia atlikti objekto y veiksmą x“. Tod÷l pateiktame
pavyzdyje atstumas tarp taškų a ir b skaičiuojamas nesimetriškai. Galima taikyti taškui a
priklausančią funkciją a.atstumas_b(b), kuri apskaičiuos atstumą nuo savo šeimininko iki kito taško
b, nurodyto parametru. Galima taikyti taškui b priklausančią funkciją b.atstumas_b(a), kuri
apskaičiuos atstumą priešinga kryptimi − nuo savo šeimininko iki kito taško a, nurodyto parametru.

5.4.2. Realizacija procedūrin÷se programavimo kalbose

Eifelio programavimo kalbos paskirtis − didelių programavimo sistemų projektavimas


naudojantis objektinio programavimo metodu. Joje visapusiškai išpl÷toti objektinio programavimo
principai, sklandžiai ir organiškai įtraukti į kalbą. D÷l to Eifelį galima laikyti geru objektinio
programavimo atstovu.
Eifelio kalbos sąvokas ir žymenis mes vartojome šiame skyriuje, tačiau ne visus. Viena
svarbiausių neaprašytų dalių yra programos kontrol÷s priemon÷s, kuriomis papildomi klasių bei
atskirų jų atributų aprašai.
Operacijų aprašai yra papildomi sąlygomis, kurios turi būti tenkinamos prieš atliekant
operaciją ir vadinamos priešsąlyg÷mis ir sąlygomis, kurios turi susidaryti atlikus operaciją ir
vadinamos posąlyg÷mis. Pavyzdžiui, jeigu aprašytume klasæ d÷klas, tai prieš atliekant operaciją
išimti d÷klas turi būti netuščias, o atlikus operaciją d÷klo komponentų skaičius turi būti vienetu
mažesnis negu buvo prieš atliekant operaciją (čia turima omeny tai, kad d÷klo elementų skaičius yra
atskiras d÷klo atributas).
Visas klas÷s aprašas yra papildomas objekto invariantais. Klas÷s d÷klas invariantas gali būti
sąlyga, kad d÷klo elementų skaičius neneigiamas.
Ada. Objektai gali būti realizuojami moduliu. Modulyje aprašomi duomenys ir operacijos su
jais. Tačiau modulis neturi paveld÷jimo mechanizmo. Be to, nelabai lanksčiai valdomas modulio
elementų matomumas: galioja principas „viskas arba nieko“.
Turbo Paskalis. Paskalio kalba buvo sukurta anksčiau negu susiformavo objektinio
programavimo paradigma. Tod÷l kalbos standarte nebuvo ir konstrukcijų objektams aprašyti.
Tačiau v÷lesn÷s realizacijos buvo papildytos tokiomis konstrukcijomis. IBM genties
kompiuteriuose vartojamas Turbo Paskalis. Nuo 5.5 versijos į jį įtraukta nauja duomenų tipo šeima
− objektai. Jo aprašo schema yra tokia:
type OBJEKTAS = object
[paveld÷jimas]
elementų sąrašas
end
Tipo object reikšm÷s yra objektai.
Paveld÷jimo dalyje gali būti nurodytas vienas duomenų tipas − aprašomo duomenų tipo t÷vas.
Elementų sąrašas primena įrašo laukų sąrašą. Tiktai jo komponentai gali būti ne tik
duomenys, bet ir operacijos su tais duomenimis. Operacijos gali būti aprašomos 4 konstrukcijomis:
funkcija, procedūra, konstruktoriumi, destruktoriumi. Pastarosios dvi konstrukcijos yra panašios į

119
procedūrą. Viena jų sukuria objektą, kita − sunaikina. Komponentų grup÷s gali būti apibūdinamos
vienu iš dviejų bazinių žodžių: private − jeigu norima komponentą uždaryti objekto viduje, t. y.
padaryti jį nematomu objekto išor÷je, arba public − jeigu norima, kad komponentas būtų matomas
ir išor÷je.
Pavyzdys.
type taškas = object
x, y: real;
constructor sukurti (x, y: real);
procedure perkelti (xx, yy: real);
destructor sunaikinti: virtual
end.
Objekto apraše nurodomos tik operacijų antrašt÷s. Pačios operacijos aprašomos atskirai.
Aprašius objekto tipą, galima aprašyti ir to objekto tipo kintamuosius. Jie dar vadinami
objekto tipo egzemplioriais arba tiesiog objektais.
Operacijos, kaip ir kiti komponentai, laikomi objekto (reikšm÷s) nuosavybe. Jie
identifikuojami taip, kaip ir įrašo komponentai: sud÷tiniu vardu, sudarytu iš objekto vardo ir tašku
atskirto operacijos vardo.
Objektų tipų suderinamumas toks pats kaip ir objektiniame programavime. T÷vo objekto
kintamajam gali būti priskirta bet kurio jo palikuonio reikšm÷. Tačiau priskyrimo atveju
perduodama tik virtualiosios operacijos (jos apibūdinamos žodžiu virtual). Kitos (statin÷s)
operacijos tvirtai surišamos su objektu jo sukūrimo momentu ir paveldimumo ryšiai jų pakeisti
negali (tai nebūdinga objektiniam programavimui).
C++. Kalba turi dvi duomenų tipų šeimas: struktūrą (rinkinį) ir klasæ, kuriomis galima
realizuoti objektinio programavimo metodologiją.
Struktūros laukai gali būti ne tik duomenys, bet ir funkcijos bei procedūros. Šitaip į vieną
aprašą galima sud÷ti ir duomenis, ir operacijas su jais. Operacijos tampa lyg ir struktūrin÷s reikšm÷s
komponentais. Į jas kreipiamasi taip, kaip ir į įrašo lauką:
struct point
{
float x, y;
void perkelti (float x, float y);
float atstumas ();
}.
Visi struktūros laukai matomi iš išor÷s. Taigi vartotojas gali prieiti prie duomenų ir tiesiogiai,
ne tik per operacijas.
Klas÷, kaip ir rinkinys, sudaryta iš laukų. Tačiau išor÷je matomi tik tie laukai, kurie aprašyti
po bazinio žodžio public. Pateiksime pavyzdį.
class taškas
{
float x, y;

120
public
void perkelti (float x, y);
float atstumas ();
}.
Laukai x ir y išor÷je nematomi. Vartotojas gali operuoti tik operacijomis perkelti ir atstumas.
Taigi klasæ galima laikyti abstrakčiojo duomenų tipo, kartu ir objektų klas÷s realizacija.
Struktūrą galima laikyti atskiru klas÷s atveju, kai žodis public parašytas prieš visų laukų
sąrašą, t. y. kai visi laukai matomi.
Klas÷s reikšm÷ms (objektams) inicijuoti vartojama speciali operacija konstruktorius, o
sunaikinti − destruktorius.
Išor÷je nematomi laukai yra matomi objekto viduje. Taip yra visose objektų klas÷se (laukai,
kurių niekas nematytų, neturi prasm÷s). Tačiau C++ kalboje visus laukus mato ne tik jo savininkas,
bet ir visi kiti tos pačios klas÷s objektai. Taigi tarp tos pačios klas÷s objektų „paslapčių“ n÷ra. Dar
daugiau, dvi klas÷s gali būti paskelbtos draugiškomis (baziniu žodžiu friend). Tada jų laukai tampa
matomi ir „draugams“. D÷l šitokio matomumo galimyba efektyviau realizuoti giminingų klasių
operacijas. Operacijos gali naudoti tiesiogiai ne tik savos klas÷s, bet ir „draugų“ duomenų
atvaizdžius, t. y. aplenkti „draugų“ operacijas, o kartu išvengti duomenų perkodavimo.
Pavyzdžiui, turime matricos ir vektoriaus klases. Tada matricos daugybos iš vektoriaus
operaciją galima aprašyti bet kurioje klas÷je. Tarkime, kad ji aprašyta matricos klas÷je. Tokiu atveju
operacija gali naudotis savos klas÷s duomenų matricos pavaizdavimu, o vektorių paimti kaip
abstrakčią reikšmæ, ją „išardyti“ į reikiamą pavaizdavimą ir sudauginti. Jeigu matricos ir vektoriaus
klas÷s yra „draugai“, tai operacija gali tiesiogiai paimti vektoriaus pavaizdavimą. Be abejo, tokiu
atveju vektoriaus pavaizdavimas turi būti žinomas, ir dar geriau − jeigu jis yra tokios pat rūšies kaip
ir matricos (pvz., ir matrica, ir vektorius pavaizduoti masyvais).
Pri÷jimas prie kitų objektų privačių laukų mažina programų patikimumą, nes padid÷ja jų
priklausomyb÷ nuo realizacijos.

*Kiekvienas šios kalbos struktūrinis tipas turi du variantus: modifikuojamą (mutacinį) ir


nemodifikuojamą (nemutacinį – pastovų). Laikoma, kad tipų reikšm÷s yra objektai, kurių
būseną galima keisti, tuo tarpu nemutacinių tipų objektų bûsenų keisti negalima.

Pratimai
1. Tarkime, kad turime sutvarkyto vardinio duomenų tipo aprašo schemą:
type t = (t_pirm, ..., t_pask),
o programavimo kalboje yra aprašytos tik dvi operacijos. Viena jų, pažym÷ta vardu ord, atvaizduoja
tipo t reikšmes į sveikuosius skaičius ir atvirkštin÷ operacija t_atv atvaizduoja sveikuosius skaičius į
tipo t reikšmes. Abiejų operacijų savybes aprašo vienintel÷ lygyb÷
t_atv(ord(x)) = x,
čia x – tipo t reikšm÷.
Reikia sudaryti operacijų succ, pred, <, >, <=, >=, = ir <> algoritmus.
Jeigu galima sudaryti tokius algoritmus, aprašykite juos Paskalio kalbos funkcijomis
naudodamiesi duotomis operacijomis, o taip pat aritmetin÷mis sveikųjų skaičių operacijomis. Jei
negalima – motyvuokite kod÷l.

121
2. Blokin÷s struktūros programavimo kalboje galima skirtingo lygio blokuose aprašyti
vardinius tipus taip, kad dalies jų konstantų vardai būtų neprieinami, nors pačios konstantos ir
egzistuotų.
Parašykite Paskalio programos fragmentą, iliustruojantį šią situaciją ir paaiškinkite, kaip
netiesiogiai, be vardų, būtų galima tas konstantas panaudoti.
3. Aprašykite lietuvišką didžiųjų raidžių ab÷c÷læ Ados kalba.
4. Masyvą visada galima pakeisti rinkiniu, turinčiu tiek pat laukų, kiek elementų turi masyvas.
Ar visada galimas atvirkščias pakeitimas.
5. Kaip galima išreikšti masyvu, turinčiu fiksuotą elementų skaičių, sąrašu, kurio ilgis
keičiasi, bet yra ribotas didžiausias ilgis
6. Kintamieji a ir b yra realiojo duomenų tipo, kintamasis m – sveikojo. Kokios adresavimo
ir duomenų tipų keitimo operacijos, neužrašytos išreikštiniu pavidalu, turi būti atliekamos, kai turi
būti atliekamos šiuose sakiniuose:
a := 2 + a;
b := m * (-a);
b := -m * - (a).
Kurios iš neišreikštinių operacijų gali būti atliktos programos kompiliavimo metu?

122
6. VALDYMO STRUKTŪROS

6.1. Valdymo struktūrų samprata

Tiesioginiai veiksmai su duomenimis išreiškiami priskyrimo sakiniais. Kompiuteris, juos


atlikdamas skaičiuoja reiškinių reikšmes, priskiria jas kintamiesiems ir taip pasiekiamas norimas
rezultatas.
Vienas po kito einantys priskyrimo sakiniai sudaro nuoseklią sakinių vykdymo eilę. Tačiau
nedaug esama programų, kurios sudaromos tik iš priskyrimo sakinių. Sud÷tingiems skaičiavimams
aprašyti sakiniai, valdantys kitų sakinių vykdymo tvarką. Tokie sakiniai dar vadinami valdymo
struktūromis. Būtinos bent dvi valdymo struktūros: išrinkimas ir kartojimas.
Galime įžvelgti analogiją tarp valdymo ir duomenų struktūrų. Vieno veiksmo išrinkimas yra
panašus į vieno duomens išrinkimą alternatyvos duomenų tipe (rinkinio variantin÷je dalyje,
žym÷tojoje duomenų tipų sąjungoje). Veiksmų kartojimas panašus į masyvą, kuriame „kartojami“
vienodo duomenų tipo elementai. Yra ir daugiau analogijų (6.1 lentel÷)

6.1 lentel÷. Duomenų ir valdymo struktūrų sugretinimas

Duomenų tipai ir struktūros Valdymo struktūros


Paprastieji duomenų tipai Priskyrimo sakinys
Alternatyva Išrinkimo sakinys. Sąlyginis sakinys
Įrašas Sud÷tinis sakinys (sakinių seka)
Seka Nežinomo skaičiaus kartojimo ciklas
(dažniausiai prasideda baziniais žodžiais while
arba repeat)
Masyvas Žinomo skaičiaus kartojimo ciklas (dažniausiai
prasideda baziniu žodžiu for)
Abstraktieji duomenų tipai Funkcijos ir procedūros

Pirmoji s÷kmingai vartojama procedūrin÷ programavimo kalba Fortranas tur÷jo labai menkas
valdymo struktūras ir visos jos buvo gana stipriai susietos su kompiuterio architektūra (IBM 704).
Taigi tuo metu valdymo struktūros buvo labiau mašinin÷s, o ne programavimo kalbos raiškos
priemon÷s.
Daugiausia d÷mesio valdymo struktūros susilauk÷ 7-ajame dešimtmetyje. Tuo metu buvo
įrodyta, kad algoritmą, kuris gali būti pavaizduotas blokin÷mis schemomis, galima užrašyti
programavimo kalba, turinčia dvi valdymo struktūras: viena jų turi būti skiriama pasirinkti vieną iš
dviejų galimų variantų ir kita – loginei iteracijai valdyti (kartojimui). Šią teoremą suformulavo ir
1966 metais įrod÷ mokslininkai Korado Bomas (Corrado Böhm) ir Džiuzep÷ Jakopinis (Giuseppe
Jocopini). Ši teorema sudar÷ sąlygas per÷jimo sakiniui nebevartoti.

123
Iš tikrųjų, dauguma programavimo kalbų turi žymiai daugiau valdymo struktūrų, negu dvi
min÷tąsias. Tai siejama su programų rašymo patogumu. Pavyzdžiui, atskiriems atvejams kur kas
patogiau vartoti ciklą žinomo kartojimų skaičiaus ciklą, o ne vien nežinomo kartojimo skaičiaus
ciklus.
Iš kitos pus÷s, programavimo kalboje negali būti pernelyg daug valdymo struktūrų, nes
tuomet bu sunku skaityti programas.
Valdymo struktūra yra valdymo konstrukcija, sudaryta iš sekos specialių žymenų ir sakinių,
kurių vykdymą valdo ši struktūra. Kaip jau min÷jome knygos pradžioje, valdymo struktūros turi
didelę, bene didžiausią įtaką programų skaitomumui ir rašymui. Programą lengviau skaityti ir
suprasti, kai jos valdymo struktūros turi po vieną į÷jimą (pradžią) ir po vieną iš÷jimą (pabaigą).
Tod÷l tokios valdymo struktūros dažniausiai ir vartojamos.
Nors apie valdymo struktūras daug prirašyta, tačiau ginčai vis dar tebesitęsia. Iš vienos pus÷s,
gerai žinome, kad užtektų sakinių sekos, išrinkimo ir kartojimo sakinių bet kuriai programai
sudaryti. Iš kitos pus÷s, naudinga tur÷ti daugiau lankstesnių ir tam tikrais atvejais vaizdesnių
valdymo struktūrų, pavyzdžiui, įvairių rūšių ciklus: su sąlygos tikrinimu ciklo pradžioje ir
pabaigoje, su ciklo nutraukimu ir panašiai.

6.2. Struktūrin÷s schemos

Aptariant valdymo struktūras kartais patogu jas pavaizduoti struktūrin÷mis schemomis.


Struktūrin÷ schema – tai veiksmai, pavaizduoti sutartiniais žymenimis, ir ryšiai tarp jų. Veiksmai
rašomi į atitinkamos formos geometrines figūras, ryšiai nusakomi rodyklin÷mis linijomis. Paprastai
vartojami trijų rūšių žymenys.
1. Pagrindiniai veiksmai. Jie rašomi į stačiakampius. Jais nusakomi veiksmai, kurie gali
keisti kintamųjų reikšmes, bet neturi įtakos veiksmų atlikimo sekai. Iš stačiakampio gali būti tik
vienas iš÷jimas.

2. Sąlygos. Rašomos į rombo ar šiek tiek ištempto rombo pavidalo figūras. Jomis nusakomi
veiksmai, kurie gali keisti veiksmų (sakinių) atlikimo seką, bet nekeičia kintamųjų reikšmių.

124
3. Ryšiai. Ryšiai tarp atliekamų veiksmų nusakomi linijomis su rodykl÷mis, rodančiomis
kryptį – koks toliau bus atliekamas veiksmas. Jomis nekeičiamos nei kintamųjų reikšm÷s, nei
sakinių atlikimo eiga.
Dar kartais vartojami žymenys programos pradžiai ir pabaigai žym÷ti: apskritimai.

.
.
.

Aštuntojo dešimtmečio pradžioje buvo sudarytos pagrindinių valdymo struktūrų struktūrin÷s


schemos. Jos vadinamos D struktūromis (jas susisteminusio mokslininko E. Dijkstros garbei). D
struktūromis vadinamos šios keturios struktūrin÷s schemos:
a) paprastas veiksmas

Tai būtų, pavyzdžiui, priskyrimo arba procedūros sakinys.


b) seka S1S2…Sn

S1

S2

.
.
.

Sn

125
c) sąlyginis sakinys
if a
then S1
else S2
end if

F T

S2 S1

d) ciklas
while a do
S
end while

F T
a

Visose schemose S raide pažym÷ta bet kuri D struktūra, a – sąlyga.


Raide F pažym÷jome šaką, kuri atliekama, kai sąlyga netenkinama, o raide T – kai sąlyga
tenkinama. Kartais šie žymenys nenurodomi, tuomet paprastai kairioji šaka atliekama, kai sąlyga
netenkinama, o dešinioji – kai tenkinama.
Galima apibr÷žti ir kitokias struktūras. Išpl÷stine D struktūra vadinsime D’ struktūra, jei jai
papildomai priklauso dar šie sakiniai:
1) suprastintas sąlyginis sakinys (su viena šaka);
2) variantinis sakinys;
3) galutin÷s sąlygos ciklas;
4) žinomo kartojimo skaičiaus ciklas;

126
Su struktūrin÷mis schemomis glaudžiai siejasi Bomo ir Jakopinio teorema. Tai grynai
matematin÷ teorema (jos įrodymas pateiktas [Böhm66]). Tačiau šios teoremos rezultatai tur÷jo
didžiulę reikšmę programavimui.
Iš šios teoremos buvo gauta svarbi programavimo kalboms išvada:
Bet kuriai programai galima sudaryti jai ekvivalenčią programą, turinčią tik D
struktūras.
Nors išvadoje sakoma „bet kuriai programai“, tačiau turima galvoje, kad programa atitinka
aukščiau min÷tus paprastus reikalavimus: programa turi vieną į÷jimą ir vieną iš÷jimą, be to
programos struktūrin÷je schemoje per kiekvieną veiksmų stačiakampį eina bent vienas kelias,
jungiantis programos pradžią su pabaiga.
Pastarasis ribojimas yra skirtas begaliniams ciklams ir sakiniams, kurių negalima pasiekti iš
programos pradžios, išvengti.
Ekvivalenčios programos yra tokios, kurios duoda tuos pačius rezultatus esant tiems patiems
pradiniams duomenims.
Suprantama, kad ekvivalenčios programos gali būti pavaizduotos visiškai skirtingomis
struktūrin÷mis schemomis.
Bomo ir Jakopinio teoremos apie valdymo struktūras svarba ta, kad ji garantuoja, jog bet kuri
programa gali būti suprogramuota vartojant tik D struktūras. Jeigu sudarydami programas iš pat
pradžių vartosime tik D struktūras, tai parašytos programos stilius bus pakankamai geras.
Iš kitos pus÷s, jei pasirinkta programavimo kalba turi trijų rūšių valdymo struktūras:
1. Nuoseklią sakinių seką;
2. Sąlyginį sakinį
if sąlyga then sakiniai
else sakiniai
end if;
3. Ciklą
while sąlyga do
sakiniai
end while;
arba joms ekvivalenčias, tai galima parašyti bet kurią programą.
Pavyzdys
program pavyzdys;
{ Ši programa skaito reikšmes ir }
{ spausdina didžiausią iš jų }
kiek, { reikšmių skaičius }
nauja, { nauja reikšm÷ }
nr, { reikšm÷s numeris }
max: integer;

127
begin
readln(kiek);
nr := 0;
max := 0;
while nr < kiek do
read(nauja);
if nauja < max
then max := nauja;
end if;
nr := m + 1;
end while;
writeln(max);
end.
Šios programos struktūrin÷ schema vaizduojama D struktūromis, pateikta 6.1 paveiksle.

6.1 pav. Struktūrin÷ schema, kurioje vartojamos tik D struktūros

128
6.3. Sakinių seka ir sud÷tinis sakinys

Pati paprasčiausia programavimo konstrukcija – nuosekli sakinių seka. Ji tokia paprasta, kad
dažnai laikoma savaime suprantamu dalyku ir tam neskiriama jokio d÷mesio.
Pagal apibr÷žtį nuosekli seka net n÷ra valdymo struktūra (n÷ra sąlygos, kuri valdytų sakinių
atlikimo tvarką).
Nuoseklios sekos sakiniai vykdomi tokia tvarka, kaip jie pateikti tekste. Visose
programavimo kalbose šitai nurodoma tiesiog programos tekstu: kuris sakinys parašytas toliau po
dabar vykdomo, tas ir bus atliekamas.
Pavyzdys
i := i + 1;
j := j + 1;
Su nuoseklia sakiniø seka glaudþiai susijusi sudëtinio sakinio samprata, atsiradusia Algolo-60
kalboje. Sud÷tinis sakinys nusakomas struktūra:
begin
sakinys_1;

sakinys_n;
end;
Sud÷tinis sakinys paverčia sakinių seką vienu sakiniu. Tai yra itin pažangi konstrukcija, kuri
leidžia projektuoti patogias valdymo struktūras.
Nuoseklią sakinių tvarką gali suardyti nukreipimo sakinys. Šiame sakinys valdymą nukreipia į
žyme pažym÷tą programoje esantį sakinį.
Pavyzdys.
goto negerai;

negerai: …
Apie nukreipimo sakinio netinkamumą struktūriniam programavimui, apie sukeliančias
klaidas programose buvo parašyta daug straipsnių. Tod÷l šiuo metu nukreipimo sakinį vartoja
nedaugelis programuotojų, jis tiesiog nebepopuliarus.

6.4. Išrinkimas

6.4.1. Išrinkimo sakinio samprata

Išrinkimo sakinys leidžia pakeisti programos tekste nurodytą sakinių tvarką. Išrinkimo
sakinys yra vienas pagrindinių ir svarbiausių programavimo kalbų konstrukcijų. Jo būtinumą įrod÷
Bomas ir Jakopinis anksčiau min÷toje teoremoje apie valdymo struktūras.

129
Išrinkimo sakinius iš esm÷s galima suskirstyti į dvi pagrindines grupes: išrinkimas iš dviejų
variantų ir išrinkimas iš n variantų.
Projektuojant išrinkimo sakinius programavimo kalbose dažniausiai kyla šie klausimai:
• Kokio pavidalo ir tipo turi būti reiškinys (sąlyga), kuris kontroliuoja išrinkimą?
• Kokie sakiniai gali būti išrenkami: vienas sakinys ar sakinių seka?
• Kaip išrinkimo sakiniai gali įeiti į kitus išrinkimo sakinius?
• Kaip atpažįstama išrinkimo sakinio pabaiga?
Įvairios programavimo kalbos skirtingai sprendžia šiuos klausimus, tačiau esama daug
bendrumų.

6.4.2. Išrinkimas iš dviejų variantų

Paprasčiausias sąlyginio sakinio atvejis, – kai išrenkamas vienas iš dviejų galimų variantų
priklausomai nuo jame užrašyto sakinio reikšm÷s. Toks sakinys dažniausiai vadinamas sąlyginiu
sakiniu ir jo bendras pavidalas yra toks:
if sąlyga
then sakinys
else sakinys
Sąlyginio sakinio struktūrin÷ schema pavaizduota 6.2 paveiksle.

6.2 pav. Sąlyginio sakinio struktūrin÷ schema

Sąlyginis sakinys priklausomai nuo sąlygos išrenka vieną sakinį: einantį po žodžio then arba
po žodžio else, ir jį atlieka.

130
Šitoks sąlyginis sakinys pirmą kartą buvo suprojektuotas Algol-60 kalboje ir nuo to karto yra
beveik visose procedūrin÷se kalbose, skiriasi tik sintaks÷.
Sąlyginio sakinio semantika paprasta: pirmiausia apskaičiuojama sąlyga, jei ji tenkinama,
atliekamas sakinys einantis po then; jei sąlyga netenkinama, atliekamas sakinys, einantis po else.
Pavyzdys
if a>b
then a := a - b
else b := b - a
if m÷nuo = 12
then m÷nuo := 1
else m÷nuo := m÷nuo + 1

Sutrumpintas sąlyginis sakinys. Dažnos programavimo kalbos turi paprastesnį išrinkimo


sakinio atveją, kai t÷ra tik vienas pasirinktinų variantų – tik vienas pasirenkamas sakinys: arba jis
atliekamas, arba ne. Toks sakinys vadinamas sutrumpintu arba suprastintu sąlyginiu sakiniu:
if sąlyga
then sakiniai

6.3 pav. Sutrumpinto sąlyginio sakinio schema

Sutrumpintas sąlyginis sakinys paprastai laikomas atskiru sąlyginio sakinio atveju ir


aprašomas šitaip:
if sąlyga
then sakinys
Pavyzdys.

131
if a <> 0
then x := - b / a
Sutrumpintas sąlyginio sakinio semantika tokia, kad jame esantis sakinys (einantis po then)
atliekamas tik tuo atveju, jei sąlygos reikšm÷ tenkinama.

Realizacija programavimo kalbose


Bendro pavidalo sąlyginis sakinys atsirado Algole-60 (prieš tai buvo bartojamas tik
suprastintas sąlyginis sakinys).
Sąlyginis sakinys Paskalio kalba užrašomas šitaip:
if sąlyga
then sakinys
[else sakinys]
Po žodžių then ir else galima rašyti tik vieną sakinį. Čia gelbsti sud÷tinis sakinys: jei reikia
rašyti kelis sakinius, juos apjungiame į vieną sud÷tinį (apgaubiame žodžiais begin ir end).
C ir JAVA kalboje bendra sąlyginio sakinio forma šitokia:
if (sąlyga) sakiniai;
else sakiniai;
Tiek if, tiek else dalis užbaigiama kabliataškais.
Aiškiausiai sąlyginis sakinys išreiškiamas Ados kalboje:
if sąlyga
then {sakinys};
else {sakinys};
end if;
Analogiškai sąlyginis sakinys užbaigiamas ir Modula-2 bei Oberon-2 kalbose:
if a > b
then sum := sum + a
else sum := sum + b
end
Tod÷l po then arba else galima rašyti ne tik vieną sakinį, bet ir sakinių seką.
if a > b
then sum := sum + a
akiek := akiek + 1
else sum := sum + b;
bkiek := bkiek + 1
end;

132
6.4.3. Išrinkimas išrinkime

Kadangi sąlyginiame sakinyje gali būti bet kokie sakiniai, tai atskiru atveju viename
sąlyginiame sakinyje gali būti parašytas kitas sąlyginis sakinys, jame – v÷l kitas sąlyginis sakinys ir
t. t. Elegantiška struktūra kai nauji sąlyginiai sakiniai įdedami po žodžio else:
if sąlyga then sakinys
else if sąlyga then sakinys
else if sąlyga then sakinys
else ...
Šioje schemoje išrenkamas tik vienas sakinys iš daugelio.
Ši konstrukcija taip įprasta, kad kai kurioje kalbose vartojamas bazinis žodis elseif – žodžių
else ir if junginys .
Galima įterpti naujus sąlyginius sakinius ir then šakoje, bet tuomet gauta konstrukcija būna
šiek tiek painesn÷.
if sąlyga
then if sąlyga
then if sąlyga
then sakiniai
else sakiniai
else sakiniai
else sakiniai
end if;
Apskritai daugkartinis išrinkimo sakinių įd÷jimas į išrinkimo sakinius daro programą
painesne, tod÷l rašant programą reikia gerai pagalvoti, ar tikrai jis čia reikalingas, ir jei taip, surasti
kuo aiškesnę jo formą, t. y. pasistengti išd÷styti taip, kad būtų kuo lengviau skaityti.
1 pavyzdys.
if x > 0
then if y > 0
then s := 1
else s := 2
end if
else s := 3
end if;
2 pavyzdys.
if x > 0
then s := 1

133
else if y > 0
then s := 2
else s := 3
end if
end if

Programavimo kalbose, kurios neturi sąlyginį sakinį užbaigiančio skyrybos ženklo (pvz.,
bazinio žodžio end, end if), sąlyginio sakinio įd÷jimas vienas į kitą, kai vidinis sakinys yra
sutrumpintas, iškelia gana nelengvà problemą: kaip atskirti vidinio sakinio pabaigą arba, kitaip
sakant, kaip nustatyti, kuriam sakiniui, išoriniam ar vidiniam, priklauso else šaka.
Nevienareikšmiškumo problemą galima išspręsti įvairiai. Dažniausiai laikoma, kad else šaka
priklauso artimiausiai esančiam sąlyginiam sakiniui. Taigi galioja taisykl÷: vidiniai sakiniai laikomi
(jei tik galima) nesutrumpintais. Tirti pradedama nuo giliausiai esančio sakinio (dešiniausiojo if).
Pateiktame pavyzdyje sakinys suprantamas taip, kaip atitinka jo teksto išd÷stymas.
if x > 0
then if y > 0
then s := 1
else s := 2

Tokia taisykl÷ galioja Algole-60, Paskalyje ir kitose kalbose.

6.4.4. Variantinis išrinkimas

Variantinis išrinkimas yra toks, kai išsirenkamas vykdyti vienas sakinys arba sakinių seka iš
bet kurio skaičiaus sakinių. Iš tikrųjų tai yra bendriausias išrinkimo sakinio atvejis: juk turint n
variantų sakinį visuomet galima gauti sąlyginį (dviejų variantų) sakinį.
Nors n variantų sakinį galima sumodeliuoti iš sąlyginio sakinio įdedant jame v÷l sąlyginius
sakinius, tačiau tokia konstrukcija yra nevaizdi, sunkiai skaitoma. Tod÷l geriau tur÷ti specialų
variantinį sakinį, kuris išrinktų vieną iš daugelio galimų šakų.
Bendras variantinio sakinio pavidalas:
case reiškinys
žym÷: sakinys;
žym÷: sakinys;

end;

134
Variantinis sakinys sudaromas iš reiškinio, (kartais vadinamo varianto indeksu) ir sakinių
grup÷s. Kiekvienas sakinys (kartais – sakinių seka) pažym÷tas žyme. Žym÷ – tai viena iš galimų
reiškinio reikšmių. Atliekamas tas sakinys, kuris turi žymę, lygią reiškinio reikšmei.
Variantinis sakinys sukelia daug problemų. Tod÷l programavimo kalbose variantinių sakinių
pavidalas ir semantika gana skirtingi, nes kalbų projektuotojai įvairiai band÷ išspręsti iškilusias
problemas.
Pateiksime pagrindinius klausimus, kurie iškyla projektuojant variantinį sakinį.
• Ar sakinys, esantis variantiniame sakinyje, gali būti pažym÷tas ne viena, o keliomis
žym÷mis?
• Ar sakinys gali būti pažym÷tas žymių intervalu?
• Ar reiškinys (varianto indeksas) ir žym÷s turi kokių nors ribojimų?
• Kas bus, jei keli sakiniai bus pažym÷ti ta pačia žyme?
• Kas atsitiks, jei nebus n÷ vienos žym÷s, lygios variantinio sakinio reiškinio reikšmei?
Vieną paprasčiausių variantinio sakinio formų pateik÷ W. Wirthas ir Hoare 1966 metais Algol
W kalboje. Jo schema yra tokia:
case aritmetinis_reiškinys of
begin
1_sakinys
2_sakinys

n_sakinys
end;
Toks sakinys dar vadinamas Hoaro variantiniu sakiniu.
Išrenkamas vykdyti vienas ir tik vienas sakinys priklausomai nuo aritmetinio reiškinio
reikšm÷s. Jei aritmetinio reiškinio reikšm÷ lygi 1, vykdomas pirmasis sakinys, jei lygi 2, vykdomas
antrasis sakinys ir t. t.

Realizacija programavimo kalbose


Variantinis sakinys Paskalio kalboje yra šiek tiek panašus į Hoaro pasiūlytą formą Algol W
kalboje, tačiau nebe toks vaizdus ir galintis tur÷ti kai kada neaiškių situacijų.
Paskalio kalbos variantiniame sakinyje esantys visi sakiniai turi būti pažym÷ti:
case reiškinys of
konstantų_sąrašas: sakinys

konstantų_sąrašas: sakinys
end;

135
Reiškinys variantiniame sakinyje turi būti paprastojo diskrečiojo duomenų tipo (sveikojo,
loginio, simbolinio arba vardinio).
Sakiniai variantiniame Paskalio sakinyje gali bûti bet kokie: paprastieji (tarp jų ir tuštieji) bei
sud÷tiniai (tarp jų ir nauji variantiniai sakiniai). Taigi jeigu tam tikroms varianto indekso reikšm÷ms
nereikia atlikti jokio veiksmo, tai toks veiksmas nurodomas tuščiuoju sakiniu.
Visos žym÷s variantiniame sakinyje turi būti skirtingos.
Jeigu variantinio indekso reikšmë nelygi n÷ vienai žymei, tuomet Paskalio kalboje
traktuojama neapibr÷žta reikšm÷. Įvairios Paskalio kalbos realizacijos šią problemą bando spręsti
savaip. Turbo Paskalyje variantinis sakinys papildytas else dalimi. Tuomet neapibr÷žtumo, būdingo
Paskalio standartui, n÷ra.
C kalboje variantinis sakinys switch suprojektuotas dar paprasčiau:
switch (reiškinys)
{
case reiškinys konstanta: sakiniai;

case reiškinys konstanta: sakiniai;
[ default: sakiniai;]
}
Visi reiškiniai turi būti sveikojo tipo.
Adoje variantinis sakinys apibr÷žiamas panašiai kaip ir C kalboje:
case sąlyga is
when alternatyva => sakiniai;
when alternatyva => sakiniai;

when alternatyva => sakiniai;
end case;
Alternatyvose gali būti naudojamos atkarpos bei login÷ operacija or, žymima simboliu /.

6.4.5. Tiesioginis išrinkimas

Elegančią formą turi išrinkimo sakinys, kurį suprojektavo E. Dijkstra. Angliškai jis vadinamas
guarded sakiniu, nuo to, kad kiekviena jame esanti sąlyga vadinama guard. Šio sakinio bendras
pavidalas:
if sąlyga_1 → sakiniai
sąlyga_2 → sakiniai
...

136
sąlyga_n → sakiniai
fi;
Sakinio semantika: atliekama ta sakinių seka, prieš kurią einanti sąlygos reikšm÷ yra true;
atlikus šią sakinių seką visus išrinkimo sakinius baigia darbą. Reikalaujama, kad šiame sakinyje turi
būti bent viena sąlyga, kurios reikšm÷ būtų true, kitaip sakinys laikomas klaidingu.
Ypač elegantiška šio sakinio forma gaunama, kai jis sukompiliuojamas su kartojimo
(iteraciniu) sakiniu:
do sąlyga_1 → sakiniai
sąlyga_2 → sakiniai
...
sąlyga_n → sakiniai
od
Šiuo atveju kartojimo sakinys atliekamas tol, kol tenkinama bent viena sąlyga.

6.5. Kartojimas

6.5.1. Kartojimo sakinio samprata

Kartojimo sakinys labai svarbus programavimo kalbose: be jų programos būtų milžiniškos ir


užimtų daugybę laiko, kol jas parašytume. Tod÷l visos programavimo kalbos pradedant Fortranu
turi vienokias ar kitokias kartojimo komandas.
Funkcin÷se programavimo kalbose kartojimas dažniausiai išreiškiamas rekursija, o ne
iteracija.
Pirmoji kartojimo komanda programavimo kalbose buvo susijusi su masyvais: reik÷jo
peržiūr÷ti masyvo elementus.
Kalbant apie kartojimo sakinius pirmiausia reik÷tų išsiaiškinti du pagrindinius klausimus:
• Kaip valdomas kartojimo sakinys?
• Kur kartojimo sakinyje rašoma jį valdanti sąlyga?
Kartojimo sakinys dažniausiai valdomas loginiu arba aritmetiniu reiškiniu, arba kombinuojant
abu. Valdančioji sąlyga dažniausiai rašoma kartojimo sakinio pradžioje arba užbaigiant jį.
Kartojimo sakiniai dažnai vadinami ciklo sakiniais arba trumpiau – tiesiog ciklais.
Sakiniai, kurių vykdymą kontroliuoja ciklas, vadinami ciklo kūnu (paraidinis vertimas iš
anglų kalbos žodžio body), nors dažniausiai sakoma: cikle esantys ar ciklą sudarantys sakiniai.
Pirmosiose programavimo kalbose (pavyzdžiui, Fortane) kartojimo sakiniai buvo gana
paprasti, jų atlikimą dažniausiai vald÷ aritmetinis reiškinys. Tai įvairūs ciklo for analogai.
V÷liau imtas vartoti bendresnis kartojimo sakinys – ciklas while. Apie jį ir kalba C. Boehmo
ir G. Jacopini teorema.

137
6.5.2. Nežinomo kartojimo skaičiaus ciklas

Ciklų, kurių atlikimą valdo loginis reiškinys, kartojimo skaičius iš anksto nežinomas: kaskart
priklauso nuo reiškinio, kurio reikšm÷ atliekant ciklą keičiama. Tod÷l dažnai šiuos ciklus vadiname
nežinomo kartojimo skaičiaus ciklais.
Šis ciklas dažniausiai būna dviejų rūšių: priklauso nuo ciklą valdančios sąlygos vietos, ar ji
ciklo pradžioje, ar gale. Kai ciklą valdanti sąlyga yra pradžioje, toks ciklas vadinamas pradin÷s
sąlygos ciklu ir dažniausiai prasideda žodžiu while:
while sąlyga do
sakiniai
end while;
Ciklo semantika: sakinys atliekamas tol, kol ciklo valdančios sąlygos reikšm÷ teisinga; kai tik
šios sąlygos reikšm÷ tampa neteisinga, ciklas baigia darbą ir valdymas perduodamas po ciklo
einančiam sakiniui.
Ciklo while antrašt÷ beveik vienoda visose programavimo kalbose. Tuo tarpu pabaiga
skiriasi. Paprasčiausiu atveju ciklas neturi užbaigiančio žodžio (Algolas-60, Paskalis). Tada ciklo
antrašt÷ valdo tik vieną sakinį. Ciklo pabaigai pažym÷ti vartojami įvairūs baziniai žodžiai: od, end,
end while, endwhile.
Kai ciklą valdanti sąlyga yra ciklo pabaigoje toks ciklas dažniausiai prasideda žodžiu repeat:
repeat
sakiniai
until sąlyga;
Ciklo semantika: sakinys atliekamas tol, kol ciklo valdančios sąlygos reikšm÷ neteisinga; kai
tik sąlygos reikšm÷ tampa teisinga, ciklas baigia darbą ir valdymas perduodamas po ciklo einančiu
sakiniu.
Matome, kad galutin÷s sąlygos ciklas ciklas savo esme priešingas pradin÷s sąlygos ciklui:
pirmasis atliekamas tol, kol sąlyga tenkinama, antrasis – kol netenkinama. Be to, reikia atkreipti
d÷mesį, kad anrtasis ciklas visuomet atliekamas vieną kartą, mat sąlyga tikrinama ciklo gale, po to,
kai vieną kartą buvo atlikti cikle esantys sakiniai.

6.4 pav. Pradin÷s sąlygos ciklo struktūrin÷ schema

138
6.5 pav. Galutin÷s sąlygos struktūrin÷ schema

Ciklo while B do S atliekamus veiksmus galima aprašyti rekursiškai šitokiu semantiškai


ekvivalenčiu sakiniu:
if B then
begin
S;
while B do S
end
end if
Matome, jog loginis reiškinys B nusako kartojimų skaičių. Jis apskaičiuojamas kiekvieną
kartą kartojant ciklą.
Ciklo repeat S until B veiksmus galima aprašyti rekursiškai šitokiu semantiškai ekvivalenčiu
sakiniu:
begin
S;
if not B
then repeat S
until B;
end if;
end;
Galutin÷s sąlygos ciklą visuomet galima pakeisti jam ekvivalenčiu pradin÷s sąlygos ciklu:

139
begin
S;
while not B do
S
end while;
end;
Dar vartojamas ciklas be pabaigos sąlygos. Jo pavidalas šitoks:
loop
S
end loop
Jam užbaigti vartojamas ciklo nutraukimo sakinys, rašomas ciklo viduje. Dažniausiai jis
žymimas baziniu žodžiu exit. Jis gali būti besąlyginis. Tada vartojamas kartu su sąlyginiu sakiniu,
pavyzdžiui,
loop
S1;
if a < b then exit;
S2
end loop
Gali būti ir sąlyginis. Tada sąlyga nurodoma pačiame ciklo nutraukimo sakinyje, pavyzdžiui,
exit(a < b)
Kai ciklo nutraukimo sakinys parašytas ciklo pabaigoje, tai šis sakinys gali modeliuoti
pradin÷s sąlygos ciklo sakinį, kai pradžioje – galutin÷s.
Realizacija programavimo kalbose
Paskalio kalboje yra abu nežinomo kartojimo skaičiaus ciklai.
Galutin÷s sąlygos ciklo sakinys Paskalio kalboje yra ypatingas: jame galima vartoti tiek
sud÷tinį sakinį, tiek sakinių seką. Tai vienintelis toks lankstus Paskalio sakinys. Tai gali būti ir
ortogonalumo trūkumo Paskalyje pavyzdys.
Modula-2 ir C kalba turi analogiškus abu sakinius.
C kalboje šie sakiniai užrašomi šitaip:
while (reiškinys) sakinys
ir
do sakinys while (reiškinys)
Pavyzdžiai.
1) scanf(“&d”,&indat);
while (indcat >= 0)
{sum = sum + indat;
scanf (“&d”,&indat);

140
}
2) do
{indat = indat / 10;
digits = digits + 1;
}while ( indat > 0 );
Ada turi tik vieną nežinomo kartojimų skaičiaus ciklą (su sąlyga pradžioje):
while reiškinys loop
sakiniai;
end loop;
Ados kalboje atskiru atveju gali būti vartojamas kartojimo sakinys, kurio darbas nutraukiamas
iš÷jimo sakiniu exit:
loop
sakiniai
end loop;
Exit sakinio pavidalas Ados kalboje šitoks:
exit [vardas] [when sąlyga];
Šiuo sakiniu nutraukiamas vykdomas kartojimo ciklas ir valdymas perduodamas jį
gaubiančiai struktūrai, jei tokia buvo, arba po ciklo einančiam sakiniui.

6.5.3. Žinomo kartojimų skaičiaus ciklas

Šis ciklas populiarus programavimo kalbose. Šio ciklo esm÷: pradžioje, prieš atliekant ciklą,
suskaičiuojama, kiek kartų bus atliekamas ciklas, ir tiek kartų vykdomi cikle esantys sakiniai.
Pirmosiose kalbose šis ciklas buvo gana nevaizdus.
Nuo Algolo-60 įsigal÷jo vaizdesnis sakinys, kurio pavidalas toks:
for k := elementų_sąrašas do
sakinys
Elementų sąrašas gali būti apibr÷žiamas įvairiai. Vienas labiausiai paplitusių būdų:
reiškinys step reiškinys until reiškinys
Čia visur reiškinys yra aritmetinio tipo.
Pirmasis reiškinys vadinamas ciklo pradine reikšme, antrasis – ciklo žingsniu, trečiasis –
galutine reikšme.
Nors šis sakinys atrodo labai paprastas, tačiau jame slypi daug pavojų. Projektuojant žinomo
kartojimo skaičiaus ciklą reikia apgalvoti šiuos kalusimus:
• Koks turi būti ciklo kintamojo tipas?

141
• Kokia tur÷tų būti ciklo kintamojo reikšm÷, kai ciklas baigia darbą?
• Ar galime ciklo kintamojo reikšmę keisti ciklo viduje, jei taip, ar tai turi įtakos ciklo
atlikimo skaičiui?
• Ar ciklo parametrai turi būti paskaičiuojami vieną kartą ar perskaičiuojami kiekvienai
iteracijai iš naujo?
Taigi žinomo kartojimų skaičiaus ciklas yra gana „kietas riešutas“ programavimo kalbų
projektuotojams, ypač – jas realizuojantiems programuotojams.

Realizacija programavimo kalbose


Bendriausia žinomo kartojimo skaičiaus ciklo forma yra įgyvendinta Algolo-60 kalboje.
Išpl÷stinis BNF aprašas yra:
for_ciklas: := for var := elementų_sąrašas {, elementų sąrašas} do sakinys
elementų_sąrašas: := reiškinys
| reiškinys step reiškinys until reiškinys
| reiškinys while loginis_reiškinys
Bendrumą sudaro tai, kad čia valdanti sąlyga gali būti kombinuojama dvejopai: ir aritmetinis
reiškinys, ir loginis reiškinys kaip pradin÷s sąlygos cikle.
Visų trijų formų paprasčiausi pavyzdžiai:
1 pavyzdys
for kiek := 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 do
masyvas [kiek] := 0
2 pavyzdys
for kiek := 1 step 1 until 10 do
masyvas [kiek] := 0
3 pavyzdys
for kiek := 1, kiek + 1 while (kiek <= 10) do
masyvas [kiek] := 0
Galima sukonstruoti ir sud÷tingą ciklo sakinio pavyzdį Algolo-60 kalboje:
4 pavyzdys
for indeksas := 1, 4, 13,
41 step 2 until 47,
3 + indeksas while indeksas < 1000,
34, 2, -24 do
suma := suma + indeksas
Šiuo sakiniu prie sumos pridedamos reikšm÷s:
1, 4, 13, 41, 43, 45, 47, 141, 423, 34, 2, -24

142
Algolo-60 žinomo kartojimo skaičiaus ciklas yra gana sud÷tingas. Be to visi reiškiniai,
valdantys ciklo kartojimą, yra perskaičiuojami kiekvienai ciklo iteracijai. D÷l to atsiranda netik÷tų ir
sunkiai aptinkamų klaidų. Pavyzdžiui,
i := 1;
for kiek := 1 step kiek until 3 * i do
i := i + 1
Cikle esantis sakinys i := i + 1 keičia ciklo antrašt÷je esančią sąlygą. Kadangi šio sakinio
reikšm÷ did÷ja greičiau negu ciklą nutraukiančio reiškinio (3 * i) reikšm÷, tai užrašytasis ciklas yra
begalinis, nors to nesimato pažvelgus į programą.
Ciklo kintamasis Algol-60 gali būti tiek sveikojo, tiek realiojo tipo. Realiojo tipo kintamojo
vartojimas yra nesuderinamas su žinomo kartojimų skaičiaus id÷ja ir d÷l to yra nepagrįstas.
Adoje for ciklas yra kur kas paprastesnis:
for kintamasis [in |reverse] r÷žiai loop
sakiniai
end loop;
R÷žiai gali būti sveikojo arba vardinio tipo atkarpa.
Įdomiausias dalykas Ados kalboje yra ciklo kintamojo galiojimo srities apribojimas. Šis
kintamasis tiesiog laikomas aprašytu žinomo kartojimo skaičiaus ciklo sakinyje ir nebetenka savo
apibr÷žtumo, kai ciklas užbaigiamas.
Pavyzdys:
k: float := 1.35;
for k in 1..10 loop
suma := suma + k
end loop;
Čia tuo pačiu vardu k yra pažym÷ti du kintamieji. Jie skirtingi. Pirmasis kintamasis yra tipo
float ir negali būti panaudotas cikle, kadangi jo vardą uždengia ciklo kintamasis (jis yra sveikųjų
skaičių atkarpos 1..10 tipo). Kai ciklas baigia darbą, ciklo kintamasis k nustoja galioti. Dabar v÷l
gali būti panaudotas pirmasis float tipo kintamasis.
Adoje ciklo kintamojo reikšm÷ negali būti keičiama ciklo viduje. R÷žius nusakantys
kintamieji gali būti keičiami, tačiau ciklo kartojimų skaičiui tai netur÷s įtakos, nes r÷žiai
paskaičiuojami tik vieną kartą prieš vykdant ciklą.
Panašiai žinomo kartojimo skaičiaus ciklas aprašomas ir Paskalyje.
for kintamasis := pradin÷_reikšm÷ [to|downto] galutin÷_reikšm÷ do
sakiniai
Ciklas yra dviejų rūšių: kai ciklo kintamojo reikšm÷ keičiama did÷jančiai (to atvejas).arba kai
ciklo kintamojo reikšm÷ keičiama maž÷jančiai (downto atvejas).
Kintamojo, taip pat pradin÷s ir galutin÷s reikšm÷s turi būti diskrečiojo duomenų tipo
(sveikojo, simbolinio, loginio arba vardinio). Ciklo kintamasis turi būti aprašytas tame bloke,
kuriame yra ciklas. Jo reikšm÷ negali būti keičiama nei ciklo viduje, nei kituose blokuose
(funkcijose, procedūrose).

143
Pradin÷ ir galutin÷ ciklo kintamojo reikšm÷s apskaičiuojamos tik vieną kartą, ciklo pradžioje.
Jeigu atliekant ciklą ir būtų pakeistos į reiškinius įeinančių kintamųjų reikšm÷s, tai tas nebetur÷tų
įtakos ciklo kartojimų skaičiui.
Algole-68 ciklo kartojimus valdo ne ciklo kintamasis, o ciklo konstanta. Ciklo sintaks÷ yra
tokia, kad jame atsiranda nauja vardų galiojimo sritis. Joje panaudota ciklo konstanta automatiškai
aprašoma kaip sveikasis skaičius ir įgauna eilinę reikšmę. Aprašas galioja vienai iteracijai. Kitos
iteracijos pradžioje ciklo konstantos aprašas vykdomas iš naujo ir konstanta įgyja naują reikšmę.
Šitaip netiesiogiai išsprendžiamos „amžinos“ ciklo kintamojo problemos: reikšm÷s ketimas ciklo
viduje (konstantos reikšm÷s keisti negalima) ir kokia turi būti ciklo kintamojo reikšm÷ ciklui baigus
darbą (už ciklo ribų ciklo konstanta nebegalioja – nebelieka ir problemos). Tos pačios problemos
yra išspręstos ir Adoje. Skiriasi tik sprendimo būdas: Adoje jos išspręstos semantikos priemon÷mis,
o Algole-68 – sintaks÷s priemon÷mis.
Reiama vieKonstantos sintaks÷ yra
C kalboje žinomo kartojimo ciklo sakinio bendras pavidalas:
for (reiškinys_1, reiškinys_2, reiškinys)
sakiniai
Pirmasis sakinys nusako ciklo pradinę reikšmę, jis paskaičiuojamas vieną kartą, pradedant
ciklą. Antrasis reiškinys valdo ciklą ir perskaičiuojamas kiekvieną kartą atliekant ciklą. Paprastai C
kalboje reiškinys, įgyjantis nulinę reikšmę, laikomas false, o įgyjantis kitokias reikšmes – true.
Tod÷l ciklas bus nutraukiamas tik tada, kai reiškinio reikšm÷ lygi nuliui.
Paskutinis reiškinys žinomo kartojimo skaičiaus cikle skirtas ciklo žingsniui keisti ir
skaičiuojamas keikvieną kartą atliekant ciklą.
Tipiškas ciklo pavyzdys C kalboje yra
for (i = 0; i <= 10; i ++)
suma = suma + i;
Visi reiškiniai C kalbos žinomo kartojimo skaičiaus cikle yra pasirenkamieji. Jei n÷ra antrojo
reiškinio, jis laikomas true, vadinasi, gaunamas begalinis ciklas.
C kalboje n÷ra išreikštinio ciklo kintamojo ar parametrų. Kintamieji, sudarantys reiškinius,
gali būti keičiami ciklo viduje. Taigi C kalbos ciklas for iš tikrųjų yra while ciklo santrumpa ir
laikytinas nežinomo kartojimų skaičiaus ciklu.

6.6. Nukreipimo sakinys

Nukreipimo sakinys perduoda vykdymą į nurodytą programos vietą.


7-ajame dešimtmetyje buvo užsiliepsnojusi aštri diskusija, ar nukreipimo sakinys gali būti
aukšto lygio programavimo kalbos konstrukcija, ir jei gali, kokie jam turi būti ribojimai.
Nors nukreipimo sakinys yra galingas (juo galima modeliuoti visas valdymo struktūras),
tačiau drauge jis labai pavojingas. Vartojant jį be jokių ribojimų programos būna nevaizdžios,
sunkiai skaitomos. D÷l to pasidaro sunkiau programą patikrinti ir joje lieka daugiau klaidų.

144
Programa lengviau skaitoma, kai atliekamų sakinių tvarka yra artima programos tekstui.
Tod÷l vienas būdingiausių ribojimų nukreipimo sakiniui – valdymą perduoti tik žemyn – yra gana
natūralus.
E. Dijkstra pirmasis atkreip÷ d÷mesį į nukreipimo sakinio pavojingumą. 1968 metais jis raš÷,
kad nukreipimo sakinys yra klaidų ir painiavos šaltinis. Tais laikais tai buvo netik÷tas pareiškimas.
To to daugelis mokslininkų pasisak÷ už nukreipimo sakinio išmetimą iš programavimo kalbų arba
bent jo veikimo apribojimą.
Keletas programavimo kalbų buvo sukurta be nukreipimo sakinio, pavyzdžiui, Modula-2,
CLU, Euklidas (Euclid), Džipsi (Gypsy). Tačiau daugelis programavimo kalbų tebeturi šį sakinį,
dažniausiai apribodamos jo vartojimą. Pavyzdžiui, leidžiama valdymą nukreipti tik į priekį,
neleidžiama valdymo nukreipti į valdymo struktūros (ciklo, sąlyginio sakinio) vidų. Reikalavimas
aprašyti žymes Paskalio kalboje taip pat savotiškai disciplinuoja programuotoją.

Pratimai

1. Pateikite pavyzdį Paskalio kalba, kai sintaksiškai vienodos konstrukcijos gali būti
semantiškai skirtingos.
2. Tarkime, kad programavimo kalba turi loginį tipą bei logines konstantas false ir true, bet
neturi loginių operacijų. Aprašykite inversiją, konjunkciją ir disjunkciją realizuojančias funkcijas
pasinaudodami sąlyginiu sakiniu.
3. Perrašykite šį programos fragmentą taip, kad jame nebūtų nukreipimo sakinių.
k := (j+1)/5;
žym÷: if k > 10 then goto viskas
k := k + 1;
i := 3*k-1;
goto žym÷;
viskas: ...
Programos fragmentą užrašykite Paskalio kalba, po to C kalba. Palyginkite juos.
4. Paskalio kalbos valdymo struktūrą
while p do
S
pakeiskite kitomis struktūromis, kuriose nebūtų ciklo while.
5. Variantinį sakinį
case r of
r1: S1;
r2: S2;
...;
rn: Sn
end

145
visada galima pakeisti šitokia sąlyginių sakinių struktūra:
if r = r1 then S1
else if r = r2 then S2
...;
else if r = rn then Sn
Ar visada galimas atvirkščias pakeitimas?
6. Pateikite argumentus „už“ ir „prieš“ apie sakinių sekos ir pabaigos nurodymą baziniais
žodžiais (kitaip sakant pagrįskite sud÷tinio sakinio reikalingumą arba nereikalingumą).
7. Išnagrin÷kite Paskalio ir Javos (arba C) kalbų valdymo struktūras. Kokie panašumai ir
skirtumai? Kokie privalumai ir trūkumai?
8. Kokį pakeitimą paprasčiau atlikti:
a) programą, kurioje vartojami tik nukreipimo sakiniai pakeisti programa, kurioje vartojami
tik ciklai ir sąlyginiai sakiniai;
a) programą, kurioje vartojami tik ciklai ir sąlyginiai sakiniai pakeisti programa, kurioje
vartojami tik nukreipimo sakiniai.
Atsakymą motyvuokite.

146
7. PROCEDŪROS IR FUNKCIJOS

7.1. Valdymo struktūrų abstrakcijos samprata

Programavimo kalbose vartojamos dvi pagrindin÷s abstrakcijos rūšys: duomenų abstrakcija


ir veiksmų abstrakcija. Veiksmų abstrakcija buvo įgyvendinama jau pirmose aukšto lygio
programavimo kalbose. Apie duomenų abstrakciją giliau imta kalb÷ti tik 8-ojo dešimtmečio
pabaigoje.
Veiksmų abstrakcija – tai tam tikras būdas veiksmus aprašančioms programos dalims
sugrupuoti, kad būtų galima jas atlikti daugelį kartų, be to parametrizuojant jų elgesį. Dažniausiai
vartojamos veiksmų abstrakciją išreiškiančios konstrukcijos yra procedūra ir funkcija. Abi
konstrukcijos kartais vadinamos vienu žodžiu – paprogram÷. Tačiau šis terminas asocijuojasi su
žemo lygio programavimo kalbomis ir d÷lto praktiškai nevartojamas kalbant apie aukšto lygio
kalbas. Mes abi konstrukcijas vadinsime procedūra, nes taip daroma daugelyje programavimo
kalbų.
Su procedūra siejamos dvi programavimo kalbos konstrukcijos: aprašas ir kreipinys. Apraše
aprašomas procedūros vardas, formalieji parametrai (jeigu jie yra), o taip pat apibr÷žiami jos
veiksmai. Veiksmai užrašomi taip pat, kaip ir programos veiksmai – sakiniais. Tik tie veiksmai yra
pasyvūs ir kol kas nevykdomi. Toje programos vietoje, kur reikia atlikti procedūros veiksmus,
rašomas kreipinys į ją. Kreipinyje nurodomas procedūros vardas ir faktiniai parametrai (jeigu jie
yra).
Kreipiniai į procedūras yra beveik vienodi visose programavimo kalbose. Tuo tarpu aprašai
skiriasi. Apie tai pakalb÷sime žemiau.
Veiksmų abstrakcijos požiūriu funkcija ir procedūra yra lygiavert÷s ir beveik nesiskiriančios
konstrukcijos. Skiriasi tik jų panaudojimo būdas.
Procedūra – tai veiksmų seka, kurią galima būtų pakeisti vienu sakiniu. Iš tikrųjų taip ir yra:
ten, kur reikalinga atlikti procedūros veiksmus, rašomas kreipinys į ją. Kreipinys į procedūrą yra
sakinys.
Funkcijos paskirtis kiek kitokia – atlikti skaičiavimus ir gautą vieną (pabr÷žiame: vieną!)
rezultatą pateikti kaip funkcijos reikšmę. Kreipinį į funkciją galima rašyti į reiškinį. Kreipinys į
funkciją ir yra reiškinys. D÷l to su kreipiniu, o tuo pačiu ir su visa funkcija, siejama duomenų tipo
sąvoka.
Jeigu programavimo kalboje n÷ra skirtumo tarp sakinio ir reiškinio, tai išnyksta skirtumas
tarp funkcijos ir procedūros. Pavyzdžiui, Algole-68 skirtumą tarp reiškinio ir sakinio galima
pavadinti kiekybiniu: reiškinio tipas yra įprastas duomenų tipas, o sakinio tipas yra void, turintis
vienintelę reikšmę empty. Tod÷l šios kalbos procedūrą, kurios rezultato tipas yra void laikome
įprasta procedūra, o tą, kurios rezultatas yra kokio nors realaus duomenų tipo – funkcija.
D÷l to, kad funkcijos rezultatas yra reikšm÷, lyginant su procedūra, funkcijos apraše turi būti
nurodoma informacija apie funkcijos reikšm÷s tipą ir būdas tai reikšmei užfiksuoti: pasakyti, kad
štai šita reikšm÷ bus funkcijos reikšm÷. Funkcijos tipas dažniausiai nurodomas jos antrašt÷je,
dažniausiai tokiu pat pavidalu, kaip ir kintamojo tipas, pavyzdžiui šitaip:

147
function f (a: integer): boolean
Funkcijos reikšm÷ fiksuojama įvairiais būdais. Panagrin÷sime keletą dažniau vartojamų.
Reikšm÷s priskyrimas funkcijos vardui. Rašomas priskyrimo sakinys, kurio kair÷je pus÷je
yra funkcijos vardas, o dešin÷je – reiškinys, kurio reikšm÷ taps funkcijos rezultatu.
Pavyzdys.
function f (a, b: integer): integer;
begin
f := a+b
end;
Kair÷je priskyrimo sakinio pus÷je parašytas funkcijos vardas yra traktuojamas kaip
savotiškas kintamasis – funkcijos reikšm÷s saugotojas. Tuo tarpu bet kur kitur parašytas vardas
(pvz., dešin÷je priskyrimo sakinio pus÷je) bus traktuojamas kaip rekursinis kreipinys į funkciją.
Reikšm÷s priskyrimas specialiam kintamajam. Aprašomas šiam tikslui skiriamas
kintamasis.
function f (a, b: integer) ff: integer;
begin
ff := a+b
end;
Nuo ankstesnio būdo skiriasi tuo, kad kintamojo ff reikšmę galima vartoti ir funkcijos
apraše, pavyzdžiui, kai reikia pakeisti jau apskaičiuotą funkcijos reikšmę.
Reikšm÷s priskyrimas grįžimo sakiniu. Dažniausiai reikšmei perduoti vartojamas grįžimo
sakinys, dažniausiai žymimas žodžiu return, po kurio, paprastai skliaustuose, rašoma funkcijos
reikšm÷.
Pavyzdys.
function f (a, b: integer): integer;
begin
return(a+b)
end;
Grįžimo sakinys paprastai nurodo ir procedūros veiksmų pabaigą. Tokiu atveju jis
atliekamas tik vieną kartą. Yra kalbų, kuriose šis sakinys neužbaigia procedūros veiksmų, o tik
perduoda apskaičiuotą funkcijos reikšmę (tokiu atvejų jį būtų tiksliau vadinti grąžinimo sakiniu).
Jeigu jis nenurodo veiksmų pabaigos, tai gali būti atliekamas keletą kartų, o funkcijos reikšm÷ yra
ta, kurią ji įgavo, kai buvo atliktas paskutinis grįžimo sakinys.

Realizacija programavimo kalbose


Paskalis, Modula-2. Procedūros aprašas pradedamas baziniu žodžiu procedure, o funkcijos
– function. Tuo norima pabr÷žti skirtumą tarp funkcijos ir procedūros. Funkcijos duomenų tipas
gali būti tik paprastasis. Paskalyje funkcijos reikšm÷ priskiriama funkcijos vardui, o Moduloje-2
vartojamas grįžimo sakinys RETURN r; (čia r – reiškinys, kurio reikšm÷ priskiriama funkcijai).

148
Ados kalboje procedūros antrašt÷ apibr÷žiama šitaip
procedure vardas (parametrai) is
Algolas-68. Visos paprogram÷s vadinamos procedūromis, bet nurodomas jos tipas. Jeigu
tipas void, tai ji atlieka procedūros vaidmenį, jei kitoks – funkcijos.
C. Visos paprogram÷s vadinamos funkcijomis. Jeigu jos tipas void, tai ji atlieka procedūros
vaidmenį.

7.2. Parametrai

7.2.1. Formalieji ir faktiniai parametrai

Procedūros aprašą sudaro blokas. Bloke yra matomi globalieji vardai. Taigi galima
panaudoti jais įvardytus duomenis. Šitaip, per globaliuosius kintamuosius, procedūra iš ją
gaubiančių blokų gali gauti pradinius duomenis ir jiems atiduoti rezultatus, t. y. palaikyti ryšį su
išore. Tačiau procedūros ir funkcijos turi geresnes priemones pradiniams duomenims gauti ir
rezultatams atiduoti – parametrus. Parametrų vartojimas yra žymiai lankstesnis programavimo
būdas. Procedūra, vartojanti parametrus, parametrizuoja skaičiavimus, t. y., atlieka veiksmus su tais
duomenimis, kurie yra nurodyti kreipinyje į ją.
Gero programavimo stiliaus teikia pirmenybę parametrams prieš globaliuosius vardus
procedūrose, nes dažnas, ypač nepagrįstas, globaliųjų kintamųjų vartojimas mažina programos
teksto skaitomumą. Globaliųjų kintamųjų vardai matomi ir ten, kur jų reikia, ir ten, kur nereikia. O
funkcijose išvis nepageidautina vartoti globaliuosius kintamuosius, kadangi jie sukelia šalutinį
poveikį, programuotojams gerai žinomą kaip sunkiai aptinkamų klaidų šaltinį.
Paprogram÷s antrašt÷je esantys parametrai vadinami formaliaisiais parametrais. Parametrai,
esantys kreipinyje, vadinami faktiniais parametrais. Kreipimosi į procedūrą metu faktiniai
parametrai susiejami su formaliaisiais. Šitaip keičiamasi duomenimis tarp kreipinio į procedūrą
terp÷s ir procedūros.
Yra du būdai faktiniams parametrams susieti su formaliaisiais: pozicinis ir raktinis.
Pozicinis parametrų susiejimo būdas vartojamas nuo pat pirmųjų programavimo kalbų. Tai
labai geras ir vaizdus metodas, ypač kai parametrų n÷ra daug. Ryšys nustatomas pagal parametrų
išd÷stymą procedūros antrašt÷je ir kreipinyje į šią procedūrą: pirmasis, esantis faktinių parametrų
sąraše, parametras atitinka pirmąjį formaliųjų parametrų sąrašo parametrą, antrasis faktinis
parametras – antrąjį formalųjį parametrą ir t. t.
Faktinių ir formaliųjų parametrų ryšio schema būtų šitokia:

abc (faktinis1, faktinis2, faktinis3, …)

procedure abc (formalus1, formalus2, formalus3, …)

149
Raktinis parametrų susiejimo būdas. Kai parametrų daug, pozicinis metodas n÷ra
patogus: galima lengvai sumaišyti jų tvarką. Tod÷l buvo sugalvotas raktinis susiejimo būdas. Jo
esm÷ – kreipinyje greta faktinio parametro nurodomas vardas formaliojo parametro, su kuriuo reikia
susieti tą faktinį parametrą. Raktinių parametrų būdas buvo įgyvendintas Ados kalboje. Pavyzdžiui,
jei Ados procedūros antrašt÷ yra
procedure data(metai, m÷nuo, diena) is
tai kreipiniai gali būti:
a) data (metai ⇒ šie_metai,
m÷nuo ⇒ šis_m÷nuo,
diena ⇒ ši_diena);
b) data (metai ⇒ šis_m÷nuo,
m÷nuo ⇒ šie_metai,
diena ⇒ ši_diena);
c) data (metai ⇒ ši_diena,
m÷nuo ⇒ šis_m÷nuo,
diena ⇒ šie_metai);
Raktinių parametrų metodo trūkumas yra tas, kad reikia žinoti formaliųjų parametrų vardus.
Abiem atvejais faktinių parametrų skaičius kreipinyje turi sutapti su formaliųjų parametrų
skaičiumi procedūros antrašt÷je.
Projektuojant parametrų perdavimo ryšius buvo sugalvota dar viena gudryb÷: numatytieji
parametrai. Tokių parametrų reikšm÷s apibr÷žiamos procedūros antrašt÷je, tod÷l kreipiantis juos
atitinkantys faktiniai parametrai gali būti praleisti. Tai nebūtini parametrai.
Numatytieji parametrai atsirado Ada ir C++ kalbose. Štai procedūros antrašt÷, užrašyta Ados
kalba:
procedure mokestis ( pajamos: float;
sankaupos: integer := 100;
išlaidos: float;
...) is
Kreipiantis į šią procedūrą, formalųjį parametrą sankaupos atitinkantis faktinis parametras
gali būti praleistas. Tokiu atveju procedūroje bus naudojama jos antrašt÷je nurodyta parametro
reikšm÷: 100. Kreipinys gal÷tų būti šitoks:
mokestis (1000.0, išlaidos ⇒ 800.0, …)
Parametrų perdavimo būdai. Viena svarbiausių problemų, susijusių su parametrais, yra ta,
kaip paprogram÷ paima iš išor÷s jai reikalingus pradinius duomenis ir kaip perduoda į išorę
rezultatus. Tai nusakoma parametrų perdavimo būdais. Dažniausiai vartojami penki parametrų
perdavimo būdai:
1) reikšme;
2) rezultatu;

150
3) reikšme ir rezultatu;
4) adresu;
5) vardu.
Parametrais gali būti perduodami ne tik duomenys, bet ir procedūrų bei funkcijų vardai.
Aptarsime kiekvieną perdavimo būdą atskirai.

7.2.2. Perdavimas reikšme

Paprogram÷s darbo pradžioje formaliajam parametrui priskiriama jį atitinkančio faktinio


parametro reikšm÷. Toliau sąsaja tarp abiejų parametrų nutrūksta. Formalusis parametras iš tikrųjų
yra vidinis, procedūroje aprašytas, kintamasis.
Parametro perdavimo metu įvyksta reikšm÷s priskyrimo veiksmas
formalusis := faktinis
Tod÷l ir parametrai derinami taip, kaip ir priskyrimo sakinyje: faktinis parametras turi būti
reiškinys, o jo tipą logiška derinti su formaliojo parametro tipu pagal priskyrimo sakinio taisykles.
Tai pats paprasčiausias perdavimo būdas. Juo galima perduoti tik pradinius duomenis. Jeigu
procedūros viduje ir būtų pakeista formaliojo parametro reikšm÷, toks pakeitimas netur÷tų įtakos
faktiniam parametrui.
1 pavyzdys.
procedure pp (a, b, c: integer);
var suma: integer;
begin
suma := a + b + c;
a := 22;
....
end;
Čia laikoma, kad visi trys parametrai a, b, c perduodami reikšme.
Tuomet po kreipinio į šią procedūrą:
x := 10;
pp (x, 15, 25)
kintamojo x reikšm÷ liks tokia, kokią jis tur÷jo prieš atliekant kreipinį, t. y. 10.
2 pavyzdys.
Tarkime, kad norime atlikti gana dažnai prireikiantį veiksmą – sukeisti kintamųjų reikšmes
vietomis.
program perdavimas_reikšme;
var i: integer;

151
a: array [1. .3] of integer;
procedure keisti (x, y: integer);
{ parametrai x ir y perduodami reikšme }
var tarp: integer;
begin
tarp := x;
x := y;
y := tarp;
end;
begin
i := 3;
a[i] := 6;
writeln(i, a[3]);
keisti(i, a[i]);
writeln(i, a[3]);
end.
Parinkome šiek tiek sud÷tingesnį programos pavyzdį (su masyvu) tam, kad jis v÷liau tiktų ir
kitiems ir kitiems parametrų perdavimo būdams pademonstruoti.
Procedūros parametrai x ir y perduodami reikšme. Kreipiantis į procedūrą faktiniai
parametrai naudojami tik kaip pradin÷s formaliųjų reikšm÷s, procedūra negali pakeisti jų reikšmių.
Tod÷l procedūra neatlieka norimo veiksmo – nesukeičia kintamųjų reikšmių.
Atlikus šią programą abu spausdinami rezultatai sutampa:
i=3 a[3] = 6
i=3 a[3] = 6

7.2.3. Perdavimas rezultatu

Šis perdavimo būdas skirtas duomenims perduoti iš paprogram÷s į išorę. Tod÷l jis
naudojamas paprogram÷s rezultatams perduoti.
Tai atvirkščias būdas, negu perdavimas reikšme. Reikšm÷s priskyrimas įvyksta kai
paprogram÷ baigia darbą ir priešinga kryptimi.
faktinis := formalusis.
Formalusis parametras vartojamas kaip vidinis, paprogram÷je aprašytas kintamasis. Kai
paprogram÷ pradeda darbą, jo reikšm÷ būna neapibr÷žta (kaip ir kitų paprogram÷je aprašytų
kintamųjų). Kai baigia darbą, jo reikšm÷ yra tokia, kokia buvo paskutinį kartą jam priskirta. Tokia ji
ir priskiriama faktiniam parametrui.

152
Faktinis parametras turi būti toks, kuriam galima priskirti reikšmę, t. y. kintamasis.
Naudojant parametrų perdavimo rezultatu metodą, susidaro įdomi situacija, kai kreipinyje
yra parametrai su tais pačiais vardais, sakysime
pp (x1, x1).
Be abejo, atitinkami procedūros formalieji parametrai yra skirtingų vardų, jie gali įgyti
skirtingas reikšmes. Kurią iš šių reikšmių įgis kintamasis x? Kadangi priskyrimas paprastai
atliekamas iš eil÷s, tai x1 įgis paskutiniąją priskirtą reikšmę. Taigi faktinio parametro priskyrimo
tvarka apibr÷žia rezultatą.
Dar viena problema, susijusi su parametrų perdavimu rezultatu. Programuotojas turi žinoti,
kuriuo laiko momentu nustatomas formaliojo parametro adresas: kreipimosi metu ar grįžimo iš
procedūros metu. Pavyzdžiui, jeigu faktinis parametras yra indeksuotas kintamasis, tai jo indekso
reikšm÷ gali būti pakeista.
Pavyzdys. V÷l panagrin÷kime tą patį kintamųjų reikšmių sukeitimo pavyzdį.
program perdavimas_rezultatu;
var i: integer;
a: array [1. .3] of integer;
procedūra keisti (x, y: integer);
{ parametrai x ir y perduodami rezultatu }
var tarp: integer;
begin
tarp := x;
x := y;
y := tarp;
end;
begin
i := 3;
a[i] := 6;
writeln(i, a[3]);
keisti(i, a[i]);
writeln(i, a[3]);
end.
Šiame pavyzdyje laikysime, kad procedūros parametrai x ir y perduodami rezultatu. Tod÷l
kreipiantis į procedūrą jokių faktinių kintamųjų reikšmių nebus perduodama (nesvarbu, ar faktiniai
parametrai tur÷jo reikšmes, ar ne). Formaliesiems parametrams bus perduodami tik faktinių
parametrų adresai, kuriais reik÷s įrašyti rezultatus, kai procedūra baigs darbą.
Tačiau pirmasis priskyrimo sakinys
tarp := x
neturi prasm÷s, kadangi kintamojo x reikšm÷ neapibr÷žta.

153
Vadinasi, atlikus šią programą tur÷tų būti gauti rezultatai
i=3 a[3] = 6
Galime įžvelgti parametrų perdavimo reikšme ir rezultatu dualumą: pirmuoju atveju faktinis
parametras yra reikšm÷, kuri perduodama formaliajam parametrui. Antruoju atveju faktinis
parametras yra kintamasis, kuriam perduodama formaliojo parametro reikšm÷.

7.2.4. Perdavimas reikšme ir rezultatu

Šis būdas gaunamas sujungus abu pirmesnius: parametrų perdavimą reikšme ir parametrų
perdavimą rezultatu.
Faktinio parametro reikšm÷ perduodama procedūrai – ji tampa atitinkamo formaliojo
parametro pradine reikšme. Formalius parametrus toliau veikia tarytum vidinis kintamasis. Baigiant
vykdyti procedūrą formaliojo parametro reikšm÷ perduodama atgal faktiniam parametrui.
Perduodant parametrus reikšme ir rezultatu, analogiškai kaip perduodant vien rezultatu,
atitinkami faktiniai parametrai gali būti tik kintamieji.
Pavyzdys.
program perdavimas_reikšme_ir_rezultatu;
var i, c: integer;
a: array [1. .6] of integer;
procedūra keisti (x, y: integer);
{ parametrai x ir y perduodami reikšme ir rezultatu }
var tarp: integer;
begin
tarp := x;
x := y;
y := tarp;
end;
begin
i := 3;
a[i] := 6;
c: = a[i];
writeln(i, c);
keisti (i, c);
writeln(i, c);
end.

154
Procedūros parametrai x ir y perduodami reikšme ir rezultatu. Kreipiantis į procedūrą,
formaliesiems parametrams perduodamos faktinių parametrų reikšm÷s. Šios reikšm÷s tampa
procedūros vidinių kintamųjų reikšm÷mis. Atlikus procedūroje užrašytus veiksmus, formaliųjų
parametrų reikšm÷s grąžinamos faktiniams, t. y. pakeičiamos anksčiau jų buvusios reikšm÷s. Tod÷l
programa tur÷tų spausdinti teisingą atsakymą:
i=3 c=6 {a[3]}
i=6 c=3 {a[3]}

7.2.5. Perdavimas adresu

Šis parametrų perdavimo būdas sutinkamas dažniausiai, nes jo realizacija paprasčiausia.


Faktinis parametras apdorojamas prieš kreipiantis į procedūrą: jei jis n÷ra kintamasis ar
konstanta, tai apskaičiuojama jo reikšm÷ ir patalpinama laikinoje vietoje. Po to apskaičiuojamas
adresas (kintamojo, konstantos ar laikinos vietos) ir perduodamas iškviestai procedūrai. Procedūra
visus veiksmus atlieka naudodama perduotąjį adresą. Pavyzdžiui, tarkime, kad turime procedūrą
procedure P (x…);
begin
x := x + 5

end;
Kreipkim÷s į ją
P(A[i])
Prieš procedūros atlikimą bus paskaičiuotas A[i] adresas ir jis perduodamas procedūrai.
Procedūroje atliekamas priskyrimo sakinys x := x + 5, kuris pakeis masyvo elemento A[i]
reikšmę – bus pridedama 5.
Perdavimas adresu yra efektyviausia laiko ir atminties požiūriu. Nei atminties dubliavimo,
nei kopijavimo čia neprireikia.
Vienas rimtesnių šio metodo trūkumų – persidengimų (angl. aliases) atsiradimas.
Persidengimas gali įvykti keliais skirtingais būdais.
Pirma, gali iškilti nesusipratimų tarp faktinių parametrų. Pavyzdžiui, sakysime pp procedūra
turi du parametrus, kurie perduodami adresu:
procedure pp (pirmas, antras: integer);
Jei bus kreipiamasi dukart panaudojant tą patį kintamąjį
pp (suma, suma),
tai kintamųjų pirmas ir antras reikšm÷s persidengs.
Antra, masyvo elementų susidūrimas taip pat gali sukelti persidengimą. Pavyzdžiui,
sakykime, kad į min÷tą procedūrą kreipiam÷s nurodydami du masyvo elementus:

155
pp(elem[i], elem[j])
Jeigu i = j, tai kintamieji pirmas ir antras v÷l persidengs.
Trečia, masyvo elementų susidūrimas su pačiu masyvu, perduodamu masyvo vardu, v÷l gali
sukelti persidengimą.
Apskritai persidengimai įvyksta tuomet, kai programavimo kalba (arba jos realizacija)
leidžia daugiau pri÷jimų prie kintamųjų reikšmių negu kad jų reikia. Pavyzdžiui, panagrin÷kime šį
programos fragmentą:
procedure ppp;
var visuotinis: integer;
procedure p (vietinis: integer);
{ perduodama adresu }
begin

end; { p }
begin

p (visuotinis);

end; { ppp }
Procedūros p viduje, kintamieji visuotinis ir vietinis persidengia – jų vardai rodo tą pačią
atminties vietą.
Persidengimas mažina programų skaitomumą, o taip pat pasunkina verifikavimą.
Dar viena, itin subtili klaida, susijusi su parametrų perdavimu adresu atsiranda tuomet, jeigu
tuo nebuvo pasirūpinę transliatoriaus projektuotojai.
Sakykime, programoje yra dvi nuorodos į konstantą 10, pirmoji jų yra faktinis parametras,
perduodamas procedūrai. Toliau sakykime, kad procedūra klaidingai pakeit÷ parametro reikšmę:
vietoj 10 įraš÷ 5. Kompiliatorius šiuo atveju tur÷tų sukurti atskirą vietą, kurioje buvo konstanta 10,
kad būtų galima naudotis ankstesn÷mis nuorodomis, kurioms reikia 10. Tačiau grįžus iš procedūros,
visos faktinių parametrų nuorodos tur÷tų rodyti į 5. Ši problema itin sunkiai diagnozuojama
V÷l aptarsime analogišką kintamųjų reikšmių sukeitimo pavyzdį.
Pavyzdys.
program perdavimas_adresu;
var i: integer;
a: array [1. .3] of integer;
procedure keisti (x, y: integer);
{ parametrai x ir y perduodami adresu }
var tarp: integer;
begin

156
tarp := x;
x := y;
y := tarp;
end;
begin
i := 3;
a[i] := 6;
writeln(i, a[3]);
keisti(i, a[i]);
writeln(i, a[3]);
end.
Šiame pavyzdyje laikysime, kad procedūros parametrai perduodami adresu. Pirmiausia
paskaičiuojami faktinių parametrų adresai ir jie perduodami procedūros formaliesiems
parametrams. Taigi procedūra žino faktinių parametrų adresus ir čia atlieka visus savo veiksmus.
Atkreipiame d÷mesį, kad faktinių parametrų, perduodamų adresu, adresai paskaičiuojami iš karto,
prieš pat procedūros pirmojo sakinio vykdymą. Daugiau jie neperskaičiuojami.
Ši programa kaip tik atlieka tai, ko mums reikia – sukeičia kintamųjų reikšmes vietomis.
Taigi atlikus programą bus gauta:
i=3 a[3] = 6
i=6 a[3] = 3
Perdavimas adresu panašus į perdavimą reikšme-rezultatu tuo, kad duomenimis keičiamasi
abiem krypim. Skiriasi sąsajos tarp procedūros ir kreipinio trukm÷. Kai perduodama reikšme
rezultatu sąsaja būna tik procedūros pradžioje, kai perduodama reikšm÷ iš kreipinio į procedūrą, po
to ji nutrūksta ir v÷l atsinaujina procedūros pabaigoje, kai perduodamas rezultatas. Tuo tarpu
perdavimo adresu sąsaja išlieka visą laiką ir tod÷l galimi persidengimai. D÷l to klaidas daryti
lengviau, kai parametrai perduodami adresu.

7.2.6. Perdavimas vardu

Šį būdą lengviausia suprasti, tačiau jo realizacija gana sunki. Tai visiškai skirtingas nuo
anksčiau aptartų būdas.
Kai perduodama vardu, procedūros formalusis parametras visose procedūros vietose
pakeičiamas faktiniu parametru (tiesiog įrašant jo vardą procedūros tekste).
1 pavyzdys.
procedure p(x, i);
begin
i := 2;

157
x := 5;
i := 3;
x := 1
end;
Jei į ją kreipiamasi
p(b[j * 2], j)
kai parametrai perduodami vardu, tuomet tiesiog perrašome procedūrą ir gauname
procedure p(b[j * 2], j);
begin
b[j * 2] := 2;
j := 5;
j := 3;
b[j * 2] := 1;
end;
Į formaliojo parametro vietą perkeliamas faktinio parametro tekstas kartu su kontekstu. Tai
reiškia, kad procedūra su perkeltu parametru atlieka veiksmus taip, lyg tas parametras būtų
kreipinio terp÷je. D÷l to pateiktame pavyzdyje faktinis parametras, kuris atitinka formalųjį
parametrą x, keičiasi visuomet, kai tik keičiasi kintamojo j reikšm÷.Vadinasi, nepakanka faktinio
parametro adresą apskaičiuoti vieną kartą ir toliau jį naudoti: reikia jį apskaičiuoti kiekvieną kartą,
kai tik kas nors daroma su procedūros formaliuoju parametru. Tod÷l perdavimą vardu
sud÷tingiausia realizuoti bei pasiekti, kad realizacija būtų efektyvi.
Iš esm÷s perdavimo vardu metodas priklauso nuo faktinio parametro pavidalo. Jei faktinis
parametras yra konstanta, tuomet perdavimas vardu yra ekvivalentus perdavimui reikšme. Jei
faktinis parametras yra masyvo elementas, perdavimas vardu gali būti skirtingas nuo visų kitų
metodų, kadangi indekso reiškinio reikšm÷ gali keistis atliekant procedūrą. Tuo atveju formalusis
parametras, esantis įvairiose procedūros vietose, gali nurodyti skirtingus masyvo elementus.
Jei faktinis parametras yra reiškinys, sudarytas iš paprastųjų kintamųjų, perdavimas vardu
v÷lgi skirtingas, nes kiekvienu metu gali keistis reiškinio reikšm÷.
Panagrin÷kime keletą pavyzdžių.
2 pavyzdys.
procedure ppp;
var visuotinis: integer;
a: array[1..2] of integer;
procedure p (param);
begin
param := 3;
visuotinis := visuotinis + 1;
param := 5;

158
end;
begin
a[1] := 2;
a[2] := 2;
visuotinis := 1;
p (a[visuotinis])
end;
Atlikus šią procedūrą, masyvo a elementai įgis reikšmes 3 ir 5. Antras masyvo elementas
bus pasirinktas tuomet, kai procedūroje p atliksime sakinį
visuotinis := visuotinis + 1,
t. y. bus gauta reikšm÷ 2.
3 pavyzdys.
program perdavimas_vardu;
var i: integer;
a: array [1. .3] of integer;
procedure keisti (x, y: integer);
{ parametrai x ir y perduodami vardu }
var tarp: integer;
begin
tarp := x;
x := y;
y := tarp;
end;
begin
i := 3;
a[i] := 6;
writeln(i, a[3]);
keisti (i, a[i]);
writeln(i, a[3]);
end.
Šiame pavyzdyje laikysime, kad procedūros parametrai perduodami adresu. Čia gausime
stulbinančius rezultatus. Suprantama, pirmiausia priskiriamos kintamiesiems i ir a[i] reikšm÷s. Tada
atliekamas kreipinys
keisti (i, a[i]).
Kreipiantis į procedūrą, visi joje esantys formalieji parametrai pakeičiami faktiniais.
Perrašome procedūros veiskmus.

159
tarp := i;
i := a[i];
a[i] := tarp.
Toliau atliekame skaičiavimus. Gauname rezultatus
i=3 a[3] = 6
i=6 a[6] = 3
Masyvo elemento a[3] reikšm÷ lieka nepakitusi.
Parametrų perdavimas vardu yra gana įdomus ir sud÷tingas. Jam galima sukurti įvairių
pavyzdžių, kurių sprendimas verčia stipriai pasukti galvą.
Vieną įdomesnių galvosūkių sukūr÷ J. Jensenas 1960 m., kuris iki šiol tebevadinamas
Jenseno pavyzdžiu. Jenseno id÷ja buvo perduoti reiškinį su vienu ar daugiau kintamųjų, kurie yra
tame reiškinyje kaip procedūros parametrai. Kai bent vienas iš kintamųjų, perduodamų parametrais,
pakeičiamas procedūroje, šis pakeitimas turi įtakos visiems kitiems formaliems parametrams,
kuriuos atitinka faktinis parametras.
Panagrin÷sime Jenseno pavyzdį (jis užrašytas Algolu-60).
real procedure suma (tarp, indeksas, ilgis);
value ilgis;
real laiksuma;
integer indeksas, ilgis;
begin
real laiksuma;
laiksuma := 0.0;
for indeksas := 1 step 1 until ilgis do
laiksuma := laiksuma + tarp;
suma := laiksuma
end;
Jei a yra paprastas kintamasis, tai kreipinys
suma (a, i, 100)
paprasčiausiai apskaičiuoja reikšmę 100 * a, t. y. šimtą kartų sudeda a reikšmę.
Tarkime, kad a yra masyvas, turintis 100 realiųjų skaičių, ir į procedūrą kreipiamasi
suma (a[i], i, 100)
Tuomet ciklo sakinys tampa
for i := 1 step 1 until 100 do
laiksuma := laiksuma + a[i]
Taigi šiuo kreipiniu susumuojami masyvo elementai. Jei nor÷tume susumuoti masyvo
elementų kvadratus, reik÷tų kreiptis:
suma (a[i] * a[i], i, 100)

160
Analogiškai galima sudauginti du vektorius, kurių kiekvieno ilgis 100:
suma(a[i] * b[i], i, 100)
Šitoks procedūros suma panaudojimas skirtingiems uždaviniams spręsti rodo, kad
perdavimo vardu mechanizmas yra labai lankstus: ta pati procedūra gali būti vartojama daugeliui
uždavinių spręsti.
Tačiau tokias procedūras sunku skaityti. Parametrų perdavimą sunku realizuoti
transliatoriuje.

7.2.7. Funkcijų ir procedūrų vardų perdavimas parametrais

Parametru gali būti procedūros (arba funkcijos) vardas. Tokiu atveju procedūroje arba
funkcijoje rašomi kreipiniai į formaliojo parametro vardu pavadintą procedūrą (arba funkciją). Kuri
iš tikrųjų bus procedūra ar funkcija, nurodoma faktiniu parametru. Taigi parametrais galima
persiųsti ne tik duomenis bet ir veiksmus.
Vienas būdingiausių šitokių parametrų panaudojimo pavyzdžių yra matematin÷s funkcijos.
Pavyzdžiui, procedūra, kuri taikydama skaitmeninius metodus integruoja įvairias funkcijas. Šitokia
procedūra turi tikti bet kuriai duotai funkcijai, vadinasi, jos parametru turi būti perduodama pati
funkcija.
Parametru perduodama procedūra arba funkcija tampa panaši į duomenis. Vadinasi ji turi
tur÷ti ir duomenų tipą. Koks jos tipas?
Procedūros duomenų tipą apsprendžia kiekvieno jos parametro tipas bei perdavimo būdas, o
prie funkcijos tipo apibr÷žties dar prisideda ir jos pačios duomenų tipas. Jeigu, pavyzdžiui, turime
procedūrą su šitokia antrašte:
procedure p (a, b: integer; var c: real);
tai jos duomenų tipą reik÷tų išreikšti kaip trijų duomenų tipų visumą:
integer, integer, var real.
Taigi, viskas kas yra procedūros antrašt÷je, išskyrus formaliųjų parametrų vardus, ir
apibūdina procedūros (duomenų) tipą.
Pavyzdys.
function eilut÷ (n: integer;
function f (n: integer): integer): real;
var k: integer; { eilut÷s nario numeris }
suma: real; { eilut÷s narių suma }
begin
suma := 0;
for i := 1 to n do
suma := suma + 1/f(k);
eilut÷ := suma

161
end;
Funkcija eilut÷ skirta šitokio pavidalo eilut÷ms sumuoti:
1 1 1
–––– + –––– + ... + ––––
f(1) f(2) f(n)

Jeigu į programą įtrauktume funkciją

function fakt (n: integer): integer;


var i, f: integer;
begin
f := 1;
for i := 1 to n do
f := f * i;
fakt := f
end;
ir parašytume kreipinį
eilut÷(10, fakt),
tai funkcija eilut÷ skaičiuotų šitokios eilut÷s narių sumą:
1 1 1
–––– + –––– + ... + ––––
1! 2! 10!
Jeigu parašytume kitą funkciją ir jos vardą įrašytume į kreipinį – funkcija eilut÷ skaičiuotų
kitos eilut÷s narių sumą.
Procedūriniai duomenys. Kai procedūrų ir funkcijų vardai perduodami parametrais, tai jie
traktuojami kaip duomenys. Tod÷l būtų logiška šį traktavimą pratęsti ir toliau – tur÷ti procedūrin÷s
reikšm÷s, procedūrin÷s konstantos bei procedūriniai kintamieji.
Procedūros reikšm÷ programos tekste yra procedūros aprašas, o kompiuteryje procedūros
reikšm÷ yra jos programa, pavaizduota kompiuterio kodu. Tai procedūrin÷ konstanta. Vadinasi,
aprašydami procedūrą, aprašome jos vardą ir suteikiame jam reikšmę. Taigi, galima sakyti, kad
visos programavimo kalbos, turinčios procedūras, turi ir procedūrines konstantas.
Procedūrinius kintamuosius turi nedaugelis programavimo kalbų. Kintamasis aprašomas
nurodant jo tipą. V÷liau jam gali būti priskiriamos procedūrin÷s reikšm÷s, kurias gali tur÷ti kitas
tokio pat tipo procedūrinis kintamasis arba procedūrin÷ konstanta.
Procedūrinių duomenų tipų paskirtis yra tik tipų kontrol÷. Procedūros tipas neapsprendžia,
kiek vietos atmintin÷je reik÷s procedūros reikšmei saugoti. Tuo jis panašus į rodykl÷s tipą. Matyt
d÷l to ankstesn÷se programavimo kalbose, jau tur÷jusiose duomenų tipų kontrol÷s mechanizmą,

162
buvo laikoma, kad visos rodykl÷s priklauso tam pačiam duomenų tipui (pvz., PL/I), visos
procedūros taip pat yra vieno tipo (pvz., Algole 60).

7.2.8. Parametrų perdavimo būdai programavimo kalbose

Parametrų perdavimas įvairiose kalbose realizuotas skirtingai. Kiekvienas būdas nurodomas


sutartiniais žymenimis, dažniausiai tam tikrais baziniais žodžiais, rašomais prie parametro.
Parametrų perdavimas reikšme vartojamas daugelyje programavimo kalbų, pavyzdžiui,
Paskalyje, Moduloje, Algole 68, Snobole. Didžiausia problema yra susijusi su reikšmių priskyrimu
– kopijavimu. Ypač kai perduodami didel÷s duomenų struktūros.
Įsivaizduokime, kad turime procedūrą su dviem parametrais, pirmasis jų yra tūkstančio
elementų masyvas, o antrasis – sveikojo tipo kintamasis Sakykime, pirmasis keičiamas reikšme, o
antrasis rezultatu. Tegu procedūros veiksmai labai paprasti – susumuoja masyvo elementus ir gautą
rezultatą perduoti antruoju parametru.
Prieš sumuojant perkopijuojamas visa masyvas. Tam reikia laiko ir vietos atmintin÷je. O
perkopijuoti reikia d÷l to, kad bendru atveju procedūroje gali būti keičiamos masyvo elementų
reikšm÷s.
Ši problema gražiai išspręsta Ados kalboje. Jos parametrų perdavimo būdas, žymimas
baziniu žodžiu in, yra panašūs į perdavimą reikšme, tačiau neatliekamas kopijavimas. Dirbama
tiesiogiai su faktinio parametro reikšme, esančia kreipinio terp÷je. Tačiau procedūroje parametras
traktuojamas kaip vidin÷ konstanta, tod÷l jos reikšm÷s pakeisti negalima.
Paskalio ir Modula-2 kalboje vartojami du parametrų perdavimo metodai: reikšme ir
kintamuoju. Perdavimas reikšme yra numatytasis. Perdavimas kintamuoju yra perdavimo adresu
modifikacija, kai faktinis parametras gali būti tik kintamasis. Jis nurodomas baziniu žodžiu var,
rašomu prieš formalųjį parametrą. Pavyzdžiui:
procedure pp (a, b: integer;
var suma: integer);
Parametrų perdavimas adresu – labiausiai paplitęs programavimo kalbose būdas. Šį metodą
naudoja Fortranas, Kobolas, PL/I, C, JAVA. Šio metodo populiarumas susijęs su jo realizacijos
paprastumu ir apskritai id÷ja, jog kiekvienas kintamasis turi savo vietą, iš kurios jo reikšm÷s gali
būti paimamos arba keičiamos.
Taigi perduodant adresu svarbiausia realizacijos dalis – paskaičiuoti perduodamų faktinių
kintamųjų adresus, kurie tur÷s būti susieti su atitinkamais formaliaisiais parametrais.
Jau min÷jome, kad perduodant adresu, iškyla kintamųjų reikšmių persidengimo pavojus.
Dar viena problema, susijusi su šiuo būdu, yra ta, kad perduodamas adresu parametras turi
būti kintamasis. Jei parametras yra reiškinys, tai jo reikšm÷ turi būti apskaičiuojama ir laikoma
kurioje nors atminties vietoje. Tada kyla klausimas, kod÷l šis adresas negal÷tų būti panaudojamas
ne tik parametro reikšmei pasiimti, bet ir rezultatui perduoti. Taigi šis klausimas, ar leisti adresu
perduoti reiškinius, gali būti diskutuotinas.
Algole-60 parametrams perduoti vartojamas perdavimo vardu būdas. Jis perimtas iš gana
seno matematinio metodo, vadinamo λ-skaičiavimais. Šis būdas leidžia realizuoti atskiru atveju

163
perdavimą reikšme. Kadangi perdavimas vardu sunkiai realizuojamas, labai mažai kalbų tenaudoja
jį, žinomiausių būtų Simula-67.
Algole-68 teoriškai turi vieną parametrų perdavimo būdą – reikšme. Tačiau šioje kalboje
formalieji parametrai, kaip ir kiti kintamieji, apibūdinami duomenų rūšimi. Jeigu parametras neturi
n÷ vieno žodžio ref, tai jis gali gauti tik duomens reikšmę. Vadinasi, tokiu atveju parametras
perduodamas reikšme. Jeigu parametras turi vieną ref, tai tada jis gauna faktinio parametro adreso
reikšmę, kas atitinka parametro perdavimą adresu.
Pavyzdys
proc p = (ref int a, int b, ref real c)…
Abu parametrai a ir c yra perduodami adresu, o b – reikšme.
Analogiškai padaryta ir C bei Java kalbose, nes jose netiesiogiai realizuotas duomenų rūšių
modelis.
Wirtho ir Hoare 1966 m. sukurtoje Algol W kalboje įgyvendintas parametrų perdavimo
rezultatu metodas kaip alternatyva neefektyviam perdavimo vardu metodas. Čia taip pat yra ir
perdavimas adresu.
Ados kūr÷jai aiškiai išskyr÷ tris semantinius modelius: į, iš ir į_iš. Jie žymimi baziniais
žodžiais in, out ir in out. Pavyzdžiui:
procedure p (a: in out integer;
b: in integer;
c: out float) is
Ados formaliems parametrams, aprašytiems in, galima tik priskirti reikšmes, negalima jų
perduoti; aprašytiems out – galima tik perduoti reikšmes, negalima jiems priskirti, aprašytiems in
out – galima taikyti abu būdus, ir priskirti reikšmes, ir perduoti rezultatus. Tod÷l galima sakyti, kad
Adoje naudojami trys parametrų perdavimo būdai: reikšme, rezultatu bei reikšme ir rezultatu.

Pratimai
1. Panagrin÷kite … psl. pateiktą pavyzdį. Pakeiskite masyvo elementams priskiriamas
reikšmes:
a[1] := 3;
a[2] := 1.
Apskaičiuokite pakeistą programą ir palyginkite gautąsias masyvo elementų reikšmes, kai
parametrų perdavimas vyksta skirtingais būdais:
a) reikšme;
b) adresu;
c) vardu;
d) reikšme ir rezultatu.

2. Išnagrin÷kite programą:
program pavyzdys;

164
var i: integer;
b: array [1..2] of integer;
procedure Q(x);
begin
i := 1;
x := x + 2;
b[i] := 10;
i := 2;
x := x + 2;
end;
begin
b[1] := 1;
b[2] := 1;
i := 1;
Q(B[i])
end;
Apskaičiuokite kintamųjų b[1] ir b[2] reikšmes penkis kartus, laikydami, kad parametrai
perduodam:
1) reikšme,
2) rezultatu,
3) reikšme ir rezultatu,
4) adresu,
5) vardu.
Nurodymas: vieną kartą gausite neapibr÷žtą reikšmę, kitais atvejais visos reikšm÷s
skirtingos.

3. Ką išspausdins duotoji Paskalio programa?


program pr;
var i: integer;
m: array [1..3] of integer;
procedure p ( x: integer);
begin
i := 1;
m[i] := 7;
i := 2;

165
x := x + 2;
m[i] := 10;
i := 3;
x := x + 2
end;
begin
m[i] := 1; m[2] := 1; m[3] := 1;
i := 2;
p(m[i]);
writeln(m[1], ' ', m[2], ' ', m[3])
end.

4. Turime procedūros aprašo schemą, kurioje išrašytos visos formaliųjų parametrų


panaudojimo vietos (praleistuose, daugtaškiuose pažym÷tose, procedūros dalyse formaliųjų
parametrų n÷ra)
procedure proc (a, b, c, d, e, f, g ,h , i);

begin
… := a;
… := b;
… := c;
… := d;
… := e;
… := f;

d := …;
e := …;
f := …;
g := …;
h := …;
i := …
end;
ir kreipinį į procedūrą:
proc(x, x-1, m[k+1], y, y-1, m[k-1], z, z-1, m[k])
Parašykite apie kiekvieną parametrą:

166
a) kurie iš 5 parametrų keitimo būdų (vardu, reikšme, adresu, rezultatu, reikšme ir
rezultatu) yra galimi ir
b) kurie iš galimų keitimo būdų šiuo atveju yra netinkami (nors sintaksiškai leistini).

5. Procedūra keisti skirta dviejų kintamųjų reikšm÷ms sukeisti vietomis.


procedure keisti ({vardu} pirmas, antras: integer);
var tarp: integer;
begin
tarp := pirmas;
pirmas := antras;
antras := tarp;
end;
Išnagrin÷kite kaip ši procedūra veiks, jeigu parametrai perduodami vardu, o faktiniai
parametrai yra paprastieji kintamieji. Ar viskas gerai? Tada pabandykite sukeisti du masyvo
elementus. Ar pavyks? Paaiškinkite, kas atsitinka, kai vardu keičiami paprasti kintamieji ir kai jie
yra masyvo elementai.

6. Su funkcija yra susiję dvi duomenų tipų sąvokos. Paaiškinkite jas.

167
8. PROGRAMAVIMO KALBŲ STANDARTAI IR
REALIZACIJOS

8.1. Oficialūs kalbų aprašymai

Programavimo kalba atsiranda ne iš karto. Ji projektuojama, realizuojama kompiuteryje, ja


bandoma rašyti programas. Kalbos projektai pateikiami konferencijose, skelbiami spaudoje. Dažnai
būna diskusijų apie vieną ar kitą kalbos konstrukciją. Kalba taisoma, tobulinama. Pagaliau ateina
laikas užbaigti diskusijas ir kalbą sunorminti. Tada kalbos autoriai kalbą dokumentuoja ir pateikia
galutinį jos variantą, kuris dažniausiai paskelbiamas kaip oficialus dokumentas, kuris tampa
vieninteliu dokumentu, apibr÷žiančiu kalbą, lyg ir kalbos biblija. Pirmasis toks dokumentas
programavimo kalbų istorijoje buvo 1961 m. pradžioje paskelbtas Algolo-60 kalbos aprašas
[Naur61]. Jis galiojo iki 1963 metų, kai buvo paskelbtas naujas, pakoreguotas aprašas [Naur63].
Apie programavimo kalbų biuletenių oficialumo statusą galima spręsti ir iš to, kad jų
vertimuose dažniausiai greta pateikiamas tekstas ir originalo kalba.
Kita programavimo kalbų dokumentavimo forma yra valstybiniai bei tarptautiniai standartai.
Pavyzdžiui, kai kalbame apie Paskalio standartą, dažniausiai turime omeny jo tarptautinį standartą
ISO 7185, nors egzistuoja ir Didžiosios Britanijos bei JAV standartai [ANSI83]. Min÷ti standartai
beveik sutampa, šiek tiek skiriasi tik toje valstyb÷je priimta standarto pateikimo forma bei vienas
kitas terminas. Daugelį kalbų norminančiais dokumentais laikomi JAV standartai ANSI (ANSI yra
žodžių American National Standard Institute santrumpa) [ANSI].
Stambios programin÷s įrangos firmos taip kuria savus programavimo kalbos variantus.
Tokiu pavyzdžiu gali būti firma Borland, sukurusi Turbo Paskalį, kuris paplito po visą pasaulį ir
tapo netgi daugiau žinomas, negu tarptautinis standartas. Lietuvoje beveik vien tik Turbo Paskalis ir
tevartojamas.
Firmos linkę dažniau keisti bei papildyti kalbas, nes naujos versijos patraukia naujų pirk÷jų.

8.2. Kalbų dialektai

Kai oficialiai apibr÷žtą programavimo kalbą daug kas ima vartoti, išryšk÷ja jos privalumai ir
trūkumai. Rimtesni programuotojai greitai pastebi kalbos trūkumus ir nori ją taisyti, tobulinti,
žodžiu, geriau pritaikyti savo reikm÷ms. Standartas neužkerta kelio kalbos vystymuisi. Tai n÷ra
privaloma norma, kurios besąlygiškai reikia laikytis. Tačiau jis yra tarsi orientyras visiems, kas
kalbą realizuoja kompiuteriuose tam, kad kiekvieną nukrypimą nuo standarto gerai apgalvotų ir jį
įdiegtų tik tada, kai jis būtinai reikalingas. D÷l to standartas patogus konkrečiai realizacijai
(dialektui) apibūdinti – pakanka pasakyti kuo ji skiriasi nuo standarto.
Kalbų dialektai atsiranda ir d÷l to, kad standartai apibr÷žia tik gerai apgalvotas, tobulas
kalbos konstrukcijas ir vengia į kalbą įtraukti mažiau ištirtų konstrukcijų, kurių vartojimas ar
realizacija yra abejotini, kuriuos sunku vienareikšmiškai apibr÷žti. Šią mintį puikiai perteikia
Algolo-60 oficialaus aprašo moto, išreikštas Liudviko Vitgenšteino (Ludwig Witgenstein) žodžiais
„Tai, apie ką galima kalb÷ti, turi būti pasakyta aiškiai, apie ką neįmanoma aiškiai pasakyti, reikia

168
tyl÷ti“. Šia pasakyti, kad šios taisykl÷s laikosi ir kitų kalbų autoriai. Tuo tarpu programuotojai
praktikai yra linkę kalboje tur÷ti daugiau, nors ir prastesnių, dar neišbaigtų kalbos išraiškos
priemonių. D÷l to dialektai dažnaiusiai papildo kalbas, nors tie papildymai ne visada būna
korektiški. Vieni dialektai papildo kalbą vienaip, kiti kitaip. Geresni sprendimai išlieka, blogesni
atmetami. Šitaip susikaupia laiko bandymus išlaikiusios medžiagos naujoms kalbos versijoms.
Kai susikaupia pakankamai daug pagrįstų naujovių kalboje, keičiamas ir standartas.
Retai būna realizacijų, tiksliai atitinkančių kalbos standartą. Praktiškai kiekviena realizacija
jau ir yra naujas kalbos dialektas.
Kai kalbama apie programavimo kalbą, tai turimas omeny jos standartas. D÷l to darant
išvadas apie kalbą patikimiau remyis pirminiais šaltiniais – oficialiais jos dokumentais, o ne
eksperimentai su konkrečia realizacija.

8.3. Interpretatoriai, transliatoriai ir kompiliatoriai

Programavimo kalbą realizuoti kompiuteryje galima įvairiais būdais: interpretatoriumi


transliatoriumi arba kompiliatoriumi.
Interpretatoriaus skaito programos tekstą ir interpretuoja ją, t. y. analizuoja paeiliui vieną
po kito programos sakinį ar kitokią frazę ir čia pat atlieka tai, kas ten parašyta. Darbas paprastas, bet
neekonomiškas: kai sakinių veiksmai kartojami (pvz., jie yra cikle), tai kiekvieną kartą reikia iš
naujo juos analizuoti ir atpažinti, ką jie reiškia, nes ankstesni analiz÷s rezultatai neišsaugomi. D÷l to
interpretoriai naudojami retai. Ypač jie neekonomiški mūsų nagrin÷toms kalboms, kuriose daug ką
galima padaryti statiškai, dar neatliekant programos, bet analizuojant visą jos tekstą.
Interpretatoriai tinka dinamin÷ms kalboms, kai iš anksto negalima nuspręsti, kokius
veiksmus turi atlikti vienas ar kitas sakinys (t. y. kai tokie veiksmai (ne jų rezultatai!) priklauso nuo
kitų sakinių veiksmų rezultatų). Tokių kalbų pavyzdžiai – Lispas, Beisikas.
Transliatorius – tai vert÷jas. Jis išverčia programavimo kalba parašytą tekstą (tai
transliatoriaus pradiniai duomenys) į kompiuterio kodus (tai jo rezultatai) (8.1 pav.).

Programos tekstas Programa kompiuterio kalba


Transliatorius
(pradiniai duomenys) (rezultatas)

8.1 pav. Transliatorius išverčia programą iš aukšto lygio programavimo kalbos į kompiuterio kalbą

Iš transliatoriaus gautas rezultatas – programa pateikiama kompiuteriui ir vykdoma 8.2


pav.). Programos kodas toliau gali būti vartojamas kaip programa autonomiškai. Transliatoriaus
funkcijos pasidaro nebereikalingos.

Programos Programa Programos


pradiniai duomenys kompiuterio kalba rezultatai

169
8.2 pav. Atliekama į kompiuterio kalbą išversta programa

Kartais vartojama mišri interpetavimo-transliavimo schema: programa išverčiama ne į


kompiuterio kodą, o į tarpinę programavimo kalbą, o po to tarpin÷s kalbos programą atlieka
interpretatorius.
Tarpin÷ kalba dažniausiai būna panaši į asemblerio kalbą, tačiau jos komandos artimesn÷s
transliuojamai programavimo kalbai (pvz., ciklų komandos, kreipinių į procedūras komando). Yra
plačiai žinomas Paskalio kalbos tarpin÷ kalba – P-kodas bei Ados tarpin÷ kalba – A-kodas.
Yra transliatorių, verčiančių programos tekstą iš vienos kalbos į kurią nors kitą
programavimo kalbą, pavyzdžiui, iš Paskalio kalbos į C kalbą. Antroji kalba (į kurią verčiama)
paprastai būna žemesnio lygio, t. y. artimesn÷ kompiuteriui ir jau turinti gerą transliatorių į
kompiuterio kalbą. Taigi vertimas yra dviejų etapų, per kalbą – tarpininkę. D÷l to atsiranda tam
tikrų nepatogumų tokios schemos vartotojui, nes šiuo atveju tarpin÷ kalba vartojama ne pagal
paskirtį ir d÷l to atsiranda įvairių nesklandumų. Pavyzdžiui, „išlenda“ diagnostiniai pranešimai
tarpin÷s kalbos terminais. D÷l to ši schema vartojama retai, nebent tik eksperimentavimo tikslams.
Kompiliatorius ne tik išverčia programos tekstą, bet ir į išverstą programą įjungia
(įkompiliuoja) tam tikras iš anksto parengtas programas arba programų fragmentus. Kompiliatoriai
dažniausiai ir vartojami.

8.4. Programavimo terp÷

Dirbti su interpretatoriumi, transliatoriumi arba kompiliatoriumi, atliekančiu vien pagrindinę


funkciją, t. y. vien tik interpretavimą, transliavimą arba kompiliavimą būtų nelabai patogu. Reik÷tų
parašyti programos tekstą naudojantis kokia nors tekstų rengykle, po to jį pateikti, pavyzdžiui,
kompiliatoriui, vykdyti sukompiliuotą programą, v÷l su kokia nors tekstų rengykle analizuoti į
failus surašytus programos rezultatus. Reik÷tų operuoti daugeliu duomenų ir programų failų.
Tam, kad mažiau rūpesčių keltų darbas su failais, pateikiamas ne vien kompiliatorius, bet
visa programuotojui reikalinga terp÷ – programavimo sistema. Kompiliatorius yra svarbiausias
sistemos komponentas. Tod÷l kartais visa programavimo sistema sutapatinama su kompiliatoriumi.
Kitas svarbus programavimo sistemos komponentas yra programos tekstų rengykl÷. Tod÷l
nereikia atskirai kokia nors kita rengykle rinkti bei taisyti programos tekstą. Prad÷jus darbą su
sistema, iškart įsijungia rengykl÷ ir kompiuterio ekrane galima rinkti programos tekstą.
Programavimo kalbos rengykle su tekstu galima atlikti tokias pat operacijas, kaip ir bet kuri kita
tekstų rengykl÷: jį rinkti, taisyti, išbraukti, kopijuoti, įterpti į kitą tekstą, įkelti iš failo, užrašyti į
failą ir pan.
Programavimo terp÷ paslepia ir patį transliavimo procesą. Ja naudojantis susidaro įspūdis
lyg ir kompiuteris suprastų programą, parašytą programuotojo vartojama kalba ir čia pat ją atliktų.
Operacin÷ sistema yra taip pat programavimo terp÷. Programavimo kalbos terp÷ yra kita
terp÷, veikianti operacin÷s sisremos terp÷je. Patogiausia programuoti, kai programavimo kalbos
terp÷ yra suderinta su operacin÷s sistemos terpe, t. y. integruota į ją.

170
8.5. Programavimo kalbų suderinamumas

Būna atvejų, kai atskiros uždavinio dalys programuojamos skirtingomis programavimo


kalbomis. Kartais būtų ekonomiška į uždavinio programą įtraukti kita (-omis) programavimo kalba
(-omis) parašytus programų fragmentus, d÷l to kad tik tokius juos turime. D÷l to iškyla kalbų
suderinamumo problema.
Teoriškai programos, parašytos skirtingomis kalbomis, yra nesuderinamos. Mat kiekviena
programavimo kalba yra autonomiška. Joje apibr÷žiamos tik jos pačios sintaks÷ ir semantika,
nepriklausomai nuo realizacijos. Tod÷l galima kalb÷ti ne apie kalbų suderinamumą, o apie jų
realizacijų (kompiliatorių) suderinamumą. Reikia skirti du suderinamumo lygius: valdymo
suderinamumą ir duomenų suderinamumą.
Valdymo suderinamumas. Į programą galima įterpti fragmentus, parašytus kita kalba. Kai
projektuojami kompiliatoriai, dažniausiai yra numatoma galimyb÷ daryti įtarpus, parašytus žemo
lygio kalba (asembleriu). Kadangi kompiliatorius rašomas konkrečiam kompiuteriui arba konkrečiai
operacinei sistemai, tai jos asemleris yra žinomas ir tokį suderinamumą galima realizuoti.
Duomenų suderinamumas. Pragrama, parašyta viena kalba, gali pasinaudoti kitos
programos rezultatais.
Jeigu kalbama apie kalboje vartojamus duomenų tipus, tai suderinamumas yra tiek pat
problematiškas, kaip ir valdymo lygyje. Net ir paprastųjų duomenų tipų (pavyzdžiui, loginio)
reikšm÷s skirtingose kalbose koduojamos skirtingai. Gal tik skaičių kodavimas yra panašesnis, nes
paprastai vartojami operacin÷je sisstemoje priimti skaičių vaizdavimo formatai. Bet ir jų yra keletas.
Pavyzdžiui, sveikieji skaičiai koduojami 1, 2, 4 arba 8 baitais. Programavimo kalbose skaičių
kodavimo formatai dažniausiai neapibr÷žiami. Pavyzdžiui, vienoje tos pačios kalbos realizacijoje to
paties sveikojo tipo skaičiai gali būti koduojami dviem baitais, kitoje – keturiais. Tod÷l v÷l tenka
kalb÷ti ne apie kalbų, o apie konkrečių jų realizacijų suderinamumą.
Dar daugiau skiriasi duomenų struktūrų vaizdavimas, kadangi čia operacin÷s sistemos
diktuojamų normų n÷ra, išskyrus tekstų failus, kurios vartojamos ir operacin÷se sistemose, ir
programavimo kalbose, tiksliau – visose toje sistemoje esančių kalbų realizacijose. D÷l to
duomenis, pateiktus tekstų failuose, vienodai supranta įvairių kalbų kompiliatoriai. Tai realiai
veikiantis ir praktiškai naudojamas programavimo kalbų suderinamumas duomenų lygyje.

Literatûra

Ada81
The programming language Ada: Reference Manual. − Berlin, Heidelberg, New York:
Springer−Verlag, 1981.

Ada83
Ada Programming Language. Technical Report ANSI/MIL-STD-1185, Amecican National
Standard Institute, Washington DC, 1983.

171
ANSI66
American National Standard Institute. American National Standard Programming Language
Fortran. ANSI, New York, 1966.
ANSI74
American National Standard Institute. American National Standard Programming Language Cobol.
ANSI X3.23-1974, New York, 1966.

ANSI76
American National Standard Institute. American National Standard Programming Language PL/I.
ANSI X3.53-1976, New York, 1976.
ANSI78
American National Standard Institute. American National Standard Programming Language
Fortran. ANSI X3.9-1977, New York, 1978.

ANSI83
American National Standard Institute. American National Standard Programming Language
PASCAL. ANSI/IEEE 770 X3.97-1983, New York, 1983.

ANSI66
American National Standard Institute. American National Standard Programming Language
Fortran. New York, 1966.

Appleby91
Appleby D. Programming Languages – Paradigm and Practice. McGraw-Hill, 1991.
Bal94
Bal H. E. Programming Language Essentials. Addison-Wesley, 1994.

Bauer72
Bauer F. L., Wösner H. The "Plankalkül" of Konrad Zuse: a foreruner of today's programming
languages. Comm. ACM, v. 29, 1972, N.7, 678-685.
Bauer85
Bauer F. L. et al. The Wide Spectrum Language CIP-L. LNCS, Springer, 1985.

Böhm66
Böhm C., Jacopini G. Flow Diagrams, Turing Machines, and Languages With Only Two Formation
Rules. Comm. ACM, v. 9, 1966, N. 5, 366-371.
Burns88
Burns A. Programming in Occam. Addison-Wesley, 1988.

Christian86
Christian K. A Guide to Modula-2. Springer-Verlag, 1986.

Dahl68
Dahl O.-J., Myhrhaug B., Nygaard K. Simula 67. Common Base Language. Norwegian Computing
Center, 1968. (Yra vertimas á rusø kalbà: Mir, 1969).

Dijkstra68
Dijkstra E. W. Goto Statement Considered Harmful. Comm. ACM, v. 11, 1968, N.3, 147-148.

Ellis91
Ellis M. A. Stroustrup B. The Annotated C++ Manual. Addison-Wesley, 1991.

172
Ghezzy87
Ghezzy C., Jazayeri M. Programming Language Concepts. 2nd. ed. John Wiley, 1987.
Goldberg83
Goldberg A. Robson D. Smalltalk-80: The Language and its Implementationt. Addison-Wesley,
Reading, Massachusetts, 1983.
Goldberg84
Goldberg A. Smalltalk-80: The Interactive Programming Environment. Addison-Wesley, Reading,
Massachusetts, 1984.

Jensen75
Jensen K, Wirth N. Pascal User Manual and Report. Springer Verlag, 1975.

Kernighan88
Kernighan B.W., Ritchie D.M. The C Programming Language 2 nd ed. Prentice Hall, 1988. (Yra
vertimas á rusø kalbà: M.: Finansy i statistika, 1992).
Krasner84
Krasner G. (Ed.). Smalltalk-80: Bits of History. Words of Advice. Addison-Wesley, Reading,
Massachusetts, 1984.

Lalonde93
Lalonde W., Pugh J. Smalltalk/V. Practice and Experience. Prentice Hall, 1993.
Liskov81
Liskov B. et al. CLU Reference Manual. Springer-Verlag, 1981.
Louden93
Louden K. C. Programming Languages: Principles and Practice. Boston MA: PWS Publishing Co.,
1993.
Marcotty86
Marcotty M. Ledgard H. Programming Language Landscape: Syntax, Semantcs and
Implementation. Science Res. Ass., 1986.
Marcotty87
Marcotty M. Ledgard H. The World of Programming Languages. Springer, 1987.
McCarthy62
McCarthy J. et. al. Lisp 1.5 Programmer's Manual. MIT Press, Cambridge, Mass., 1962.
Milner90
Milner , Tofte M., Harper R. The Definition od Standard ML. Cambridge MA: MIT Press, 1990.
Mossenbock95
Mossenbock H. Object-Oriented Programming in Oberon-2. − Springer, 1995.
Naur61
Naur P (Ed.). Report on the Algorithmic Language Algol-60. Numerishe Mathematic, v. 2, 1961, N.
2, 106-136.
Naur63
Naur P. (Ed.). Revised Report on the Algorithmic Language Algol-60. Comm. ACM., v. 3, 1963,
N. 1, 1-17.
Richards79
Richards M., Whitby-Stevens C. BCPL – The Language and its Compiler. Cambridge University
Press, Cambridge, 1979.
Sammet69
Sammet J. Programming Languages: History and Fundamentals. Englewood Cliffs NJ: Prentice-
Hall, 1969.

173
Sebesta93
Sebesta R. W. Concepts of Programming Languages. The Benjamin/Cummings Pub., 1993.
Stroustrup91
Stroustrup B. The C++ Programming Language. 2nd ed. Addison-Wesley, Reading, Massachusetts,
1991.

Stroustrup86
Stroustrup B. The C++ Programming Language. Addison-Wesley Publ. Co., 1986. (Yra vertimas á
rusø kalbà: M.: Radijo i sviaz, 1991).
Tumasonis93
Tumasonis V. Paskalis ir Turbo Paskalis 7.0. − Vilnius: Ûkas, 1993.

Turner86
Turner D. A. An overview of Miranda. ACM SIGPLAN Notices, v. 21, 1996, N. 12, 158-166.

Watt90
Watt. D. A. Programming Language Concepts and Paradigms. New York NY: Prentice Hall, 1990.
Watt91
Watt. D. A. Programming Language Syntax and Semantics. New York NY: Prentice Hall, 1991.

Watt93
Watt. D. A. Programming Language Processors. New York NY: Prentice Hall, 1993.
Welsh86
Welsh J., Hay A. A Model Implementation of Standard Pascal. Prentice/Hall, 1986.

Winston89
Winston P. H. Horn B. K. P. LISP. 3rd ed. Reading MA: Addison-Wesley, 1989.

Vidþiûnas92
Vidþiûnas A. Programavimas Turbo Paskalio aplinkoje. K., VDU, 1992.
Vidþiûnas97
Vidþiûnas A. Blonskis J. Turbo Paskalis 7.0. Vartotojo vadovas. K., UAB Sekasoft, VDU, 1997.

Wegmann86
Wegmann A. Object-Oriented Programming Using Modula-2. Journal of Pascal, Ada and Modula-
2, v. 5, N.3, 1986, p. 5-17.
Wijngaarden75
Wijngaarden A. van et al. Revised Report in the Algorithmic Language Algol 68. Acta Informatica,
v. 5, 1975, p. 1-326. (Yra vertimas á rusø kalbà: M.: Mir, 1978).

Wirth85
Wirth N. Programming in Modula-2. 3rd ed.Springer-Verlag, 1982.
Weber96
Weber J. Using Java. 2nd ed. QUE Corp., 1996.

174

You might also like