Denis Gaev. Kserion: yazyk i tehnologiya programmirovaniya
Versiya ot: 03.05.2002
Napisat' avtoru: mailto:dgaev@mail.ru
Annotaciya
Nastoyashchij dokument predstavlyaet soboj kratkij neformal'nyj obzor
osnovnyh vozmozhnostej yazyka programmirovaniya Kserion (po sostoyaniyu na vesnu
2002 g.). On ne pretenduet na formal'nuyu strogost' i polnotu izlozheniya i
ostavlyaet za predelami rassmotreniya mnogie tonkosti i "temnye mesta" yazyka.
Ne rassmotreny standartnye yazykovye biblioteki, sovershenno ne zatronuty
realizacionnye aspekty Kserion-sistemy i mnogoe drugoe. Tem ne menee, on
predstavlyaetsya avtoram vpolne udovletvoritel'nym v kachestve nachal'nogo
oznakomitel'nogo kursa.
Predpolagaetsya, chto chitatel' imeet predstavlenie o bazovyh principah
ob®ektno-orientirovannogo programmirovaniya, i v minimal'noj stepeni znakom s
kakim-libo iz sovremennyh OO-yazykov (predpochtitel'no, C++ ili Java).
Soderzhanie
o Vvedenie
o Leksicheskij sostav yazyka
o Primitivnye tipy i operacii nad nimi
o Massivy, ili vektornye tipy
o Ukazateli i ssylki
o Funkcionaly i funkcii
o Drugie raznovidnosti opisanij
o Instrukcii i potok upravleniya
o Ob®ekty i klassy
o Opredelenie operacij
o Import i eksport. Pragmaty
o Perspektivnye vozmozhnosti yazyka
o Zaklyuchenie
Kserion: yazyk i tehnologiya programmirovaniya
Esli kserion brosit' v rasplavlennuyu med', poluchitsya serebro. Esli v
serebro, to -- zoloto.
Esli v nikel', to -- palladij. Esli v palladij, to -- platina...
A. Lazarchuk, M. Uspenskij
"Posmotri v glaza chudovishch"
Vvedenie
Kserion -- eto sovremennyj, polnofunkcional'nyj
ob®ektno-orientirovannyj yazyk programmirovaniya. Pri razrabotke yazyka
osnovnymi istochnikami idej posluzhili: C i C++, Paskal' (vklyuchaya ego
ob®ektno-orientirovannye dialekty, takie kak Delphi) i Java. Pomimo
perechislennyh, v opredelennoj stepeni na yazyk takzhe okazali vliyanie
Algol-68, Simula, BCPL, CLU, Eiffel i nekotorye drugie menee izvestnye
yazyki.
Kserion yavlyaetsya gibridnym (ili procedurno-ob®ektnym) yazykom
programmirovaniya, napominaya v etom otnoshenii C++ i (v men'shej stepeni) Java.
On ne yavlyaetsya "chistym" ob®ektno-orientirovannym yazykom, podobnym SmallTalk
ili Actor: v yazyke ne sushchestvuet ponyatij "metaklassa", "metodov klassov" i
mehanizmov dlya dinamicheskogo sozdaniya klassov vo vremya vypolneniya programmy.
Bol'shaya chast' atributov dlya ob®ektov klassov zhestko zadaetsya vo vremya
kompilyacii i ne mozhet byt' izmenena vo vremya vypolneniya programmy.
Kserion -- strogo tipizovannyj yazyk. |to oznachaet, chto bol'shaya chast'
proverok tipov osushchestvlyaetsya vo vremya kompilyacii, i lish' otdel'nye
specificheskie atributy ob®ektnoj tipizacii mogut proveryat'sya pri vypolnenii.
Sistema tipizacii yazyka osnovana na chetkom razdelenii, provodimym mezhdu
primitivnymi (prostymi), proizvodnymi i ob®ektnymi tipami dannyh.
Kserion ne yavlyaetsya yazykom "sverhvysokogo urovnya", t.e. ne soderzhit v
yavnom vide takih vysokourovnevyh struktur dannyh, kak spiski, kortezhi,
mnozhestva, associativnye massivy i t.p.. Odnako, vse perechislennye mehanizmy
mogut byt' effektivno realizovany sredstvami samogo yazyka (i ih realizaciya,
bezuslovno, budut predostavlyat'sya standartnymi bibliotekami).
Kserion obladaet ryadom vazhnyh osobennostej, specifichnyh dlya etogo yazyka
ili realizovannyh v nem luchshe, chem vo mnogih drugih yazykah programmirovaniya.
Dlya yazyka v celom harakterny:
-- moshchnost' i gibkost'. V yazyke prisutstvuyut prakticheski vse
vozmozhnosti, harakternye dlya tradicionnyh procedurnyh yazykov, takih kak C++
ili Paskal', bez proizvol'nyh ogranichenij na ih ispol'zovanie, harakternyh,
naprimer, dlya Java. Pri etom mnogie iz etih vozmozhnostej stanovyatsya namnogo
moshchnee i potencial'no cennee. Naprimer, v Kserione dopustimo dinamicheskoe
opredelenie razmerov massivov, proizvol'nye inicializatory dlya massivov,
argumentov funkcij i komponent klassov i mnogoe drugoe.
-- ierarhicheskomu podhodu k razrabotke i realizacii programmy vo mnogom
sodejstvuet prinyatyj v yazyke princip lokal'nosti. Lyubuyu Kserion-programmu
mozhno rassmatrivat' kak ierarhiyu vlozhennyh drug v druga oblastej dejstviya, a
lyuboe opisanie (deklaraciya) imeet lokal'nyj harakter, t.e. dejstvuet tol'ko
v predelah samoj vnutrennej iz soderzhashchih ee oblastej dejstviya. Samoj
vneshnej vsegda yavlyaetsya global'naya oblast' dejstviya, no ee ispol'zovanie
luchshe maksimal'no ogranichit'. Pravil'nyj podhod k ispol'zovaniyu principa
lokal'nosti, predpolagayushchij opisanie peremennyh, funkcij, tipov dannyh,
klassov i t.p. tol'ko tam, gde oni nuzhny, yavlyaetsya vazhnym faktorom uluchsheniya
kak nadezhnosti, tak i effektivnosti programmy.
-- voprosam effektivnosti pri razrabotke yazyka udelyalos' osoboe
vnimanie. Tak, kontrol' nad takimi principial'nymi dlya effektivnosti
momentami, kak raspredelenie pamyati, nahoditsya v rukah razrabotchika.
Nekotorye sredstva yazyka, takie, kak specificheskie atributy ukazatel'nyh
tipov static i strict, dayut programmistu yavnuyu vozmozhnost' uluchshat'
effektivnost' programmnogo koda za schet ego obshchnosti. Ne menee vazhno nalichie
agregatnyh operacij prisvaivaniya i sravneniya dlya vektornyh i ob®ektnyh
tipov.
-- nadezhnost'. YAzyk yavlyaetsya nadezhnym v tom smysle, chto na nem
nevozmozhno napisat' "sbojnyj" kod. Naprimer, obespechivaetsya polnaya proverka
diapazona pri obrashcheniyah k massivam, proverka validnosti ispol'zuemyh
ukazatelej na ob®ekty dannyh i ssylok na funkcii, predusmotren nadezhnyj
mehanizm preobrazovaniya mezhdu rodstvennymi ob®ektnymi tipami i t.p. Vse
oshibki podobnogo roda, vyyavlennye pri vypolnenii programmy, vozbuzhdayut
isklyuchitel'nye situacii, kotorye mogut byt' perehvacheny i korrektno
obrabotany.
-- posledovatel'nost' i yasnost'. Predpolozhitel'no, mnogie konstrukcii
yazyka imeyut bolee posledovatel'nyj i kompaktnyj sintaksis, chem ih analogi v
Paskale, Java i C++. Mozhno skazat', chto dlya sintaksisa yazyka harakterna
bol'shaya "ortogonal'nost'", chem dlya mnogih drugih yazykov. Esli nekaya
konstrukciya yazyka sintaksicheski pravil'na, ona pochti vsegda imeet kakuyu-to
razumnuyu semantiku i mozhet byt' polezna v opredelennoj situacii. Krome togo,
yazyk minimiziruet ili polnost'yu isklyuchaet neobhodimost' v dubliruyushchih
opisaniyah: kazhdyj ob®ekt yazyka dolzhen byt' opisan tol'ko odin raz. Mnogie
chasto upotreblyaemye yazykovye konstrukcii mogut byt' zapisany bolee
kompaktno. Aktivnoe ispol'zovanie makroopredelenij let takzhe mozhet
znachitel'no "uplotnit'" programmu (vozmozhno, v ushcherb ee ponyatnosti).
-- perenosimost' realizacii -- odin iz samyh vazhnyh aspektov yazyka.
Rezul'tatom raboty kompilyatora yavlyaetsya vnutrennij kod Kserion-sistemy
(zdes' ne opisannyj). |tot kod mozhet byt' vypolnen v rezhime interpretacii s
dostatochno vysokoj effektivnost'yu na lyuboj 32-bitovoj platforme, no
orientirovan v osnovnom na translyaciyu v mashinnyj kod celevogo processora.
Leksicheskij sostav yazyka
Na samom bazovom urovne lyubaya Kserion-programma mozhet rassmatrivat'sya
prosto kak potok leksem. Poslednie podrazdelyayutsya na: ogranichiteli,
razdeliteli i znaki operacij, klyuchevye slova, identifikatory, literaly i
kommentarii. V promezhutkah mezhdu leksemami mogut prisutstvovat' probel'nye
simvoly (probely, koncy strok i bol'shinstvo upravlyayushchih simvolov),
ignoriruemye pri kompilyacii.
V tom meste, gde dopustim probel'nyj simvol, vsegda mozhet vstretit'sya i
kommentarij. Kommentarii opredelyayutsya dvumya sposobami:
· kak (nepustaya) posledovatel'nost' simvolov, ogranichennaya dvumya
simvolami ‘!';
· kak posledovatel'nost' ot dvuh simvolov ‘!' do konca tekushchej stroki.
V sluchae, kogda primenyayutsya kommentarii pervogo tipa, rekomenduetsya
ispol'zovat' vnutri nih paru dopolnitel'nyh skobok
("()","[]","{}","<>" ili chto-nibud' v etom rode), chtoby nachalo i konec
kommentariya legko razlichalis'. Vot primery:
!! dopustimyj kommentarij
! i eto tozhe ... !
!{ no takaya forma predpochtitel'nej }!
Identifikatory -- eto simvolicheskie imena, kotorye imeyut vse ob®ekty (v
shirokom smysle) yazyka: peremennye, konstanty, tipy dannyh, funkcii, klassy,
makroopredeleniya, metki i pr. K identifikatoram pred®yavlyayutsya prakticheski te
zhe trebovaniya, chto i v C: eto posledovatel'nosti latinskih bukv, desyatichnyh
cifr i znakov podcherkivaniya, nachinayushchiesya ne s cifry. Kak minimum pervye 127
simvolov identifikatora yavlyayutsya znachashchimi; zaglavnye i strochnye bukvy
razlichayutsya. Pomimo etogo, identifikator dolzhen otlichat'sya ot klyuchevogo
slova. Sleduyushchie identifikatory yavlyayutsya klyuchevymi slovami:
abstract, alloc, assert
bool, break
case, char, class, conceal, const, constructor, continue
destructor, do, double
else, enum, export
false, float, for
goto
if, import, inline, instate, int, interface
keyword
label, let, limited, long, loop
mediator
native, nil
opdef
package, pragma, private, protected
qual, quad
realloc, return
shared, short, static, strict, switch
tiny, this, true, type
u_int, u_long, u_short, u_tiny, unless, until
virtual, void
w_char, with, while
Pomimo etogo, zakonnym identifikatorom yavlyaetsya posledovatel'nost'
simvolov, zaklyuchennaya v obratnye kavychki. Vnutri kavychek dopustimy
proizvol'nye simvoly, v t.ch. nacional'nye, upravlyayushchie i probel'nye i t.p.
Sami kavychki -- ogranichiteli, a ne chast' identifikatora (alpha i `alpha` --
eto odin i tot zhe identifikator).
!! primery identifikatorov:
x;
ABACUS:
file_12;
ActiveApplet;
`Predel'nyj dopusk`
Dlya predstavleniya znachenij bol'shinstva primitivnyh tipov v yazyke est'
literaly. Celye literaly po umolchaniyu predstavlyayut soboj posledovatel'nosti
desyatichnyh cifr. Literaly mogut byt' ne tol'ko desyatichnymi: prefiks ‘$o'
ukazyvaet na vos'merichnyj literal, ‘$h' (ili ‘$x') -- na shestnadcaterichnyj,
‘$b' -- na dvoichnyj. Vse celye literaly mogut takzhe imet' suffiks, yavno
zadayushchij ih tip (sm. sleduyushchij razdel): 't' (dlya u_tiny), 's' (dlya u_short),
'i', (dlya u_int, po umolchaniyu), 'l' (dlya u_long). Literaly s plavayushchej
tochkoj opredeleny kak v C/C++, no takzhe mogut imet' yavnyj suffiks tipa: 'f'
(dlya float, po umolchaniyu), 'd' (dlya double), 'q' (dlya quad). Simvol'nye
literaly (tip char) ogranicheny prostymi kavychkami. Strokovye literaly (tip
char [], massiv iz simvolov) ogranicheny dvojnymi kavychkami. V otlichie ot
C/C++, oni mogut soderzhat' fizicheskie upravlyayushchie simvoly (takie, kak
perevod stroki) i ne zavershayutsya avtomaticheski nulevym bajtom (poslednee ne
trebuetsya, t.k. bibliotechnye sredstva yazyka opredelyayut dlinu massivov po
drugomu). Vot primery literalov (v skobkah dany ih tipy):
37t; !! 37 (u_tiny)
37s; !! 37 (u_short)
$b100101; !! 37 (u_int)
$o45; !! to zhe, chto i vyshe
$x25; !! to zhe, chto i vyshe
3.14159; !! 3.14159 (float)
3.14159d; !! 3.14159 (double)
true; !! istina (bool)
false; !! lozh' (bool)
'@'; !! simvol ‘@' (char)
"|to stroka"; !! stroka (char [])
"|to -- eshche odin primer stroki,
kotoraya zajmet neskol'ko strok
pri vyvode" !! eshche odna stroka (char [])
Primitivnye tipy i operacii nad nimi
Primitivnye tipy dannyh igrayut fundamental'nuyu rol' v sisteme tipov
yazyka, poskol'ku oni yavlyayutsya temi prostejshimi "kirpichikami", iz kotoryh
stroitsya vse ostal'noe. Ih mozhno razdelit' na chislovye, simvol'nye,
logicheskij i pustoj. V svoyu ochered', chislovye tipy predstavleny vosem'yu
celochislennymi i tremya "plavayushchimi" tipami. Celochislennye tipy dannyh -- eto
chetyre vida znachenij, imeyushchih znak (tiny, short, int, long) i ih bezznakovye
analogi (u_tiny, u_short, u_int, u_long). "Znakovye" znacheniya predstavlyayut
celye chisla v dopolnitel'nom kode i razlichayutsya razryadnost'yu: tip tiny
obespechivaet tol'ko 8 dvoichnyh razryadov, short -- 16, int -- 32 i long --
64. Sootvetstvuyushchie im tipy bez znaka imeyut tu zhe razryadnost', no
predstavlyayut tol'ko neotricatel'nye chisla. Tri tipa predstavlyayut znacheniya s
plavayushchej tochkoj (v sootvetstvii so standartom IEEE-754): float -- plavayushchee
so standartnoj tochnost'yu, double -- s dvojnoj tochnost'yu i quad
(zarezervirovan na budushchee, v nastoyashchee vremya s tochki zreniya realizacii
neotlichim ot double). Dva prostyh tipa prednaznacheny dlya raboty s simvolami:
tip char predstavlyaet 8-bitovye simvoly nabora ASCII/ISO, a tip w_char --
16-bitovye nabora Unicode. Logicheskij (bulevskij) tip bool predstavlyaet lish'
dva logicheskih znacheniya: istinu (true) i lozh' (false). V zavershenie upomyanem
tip void (pustoj), voobshche ne imeyushchij znachenij i prednaznachennyj, v osnovnom,
dlya opisaniya funkcij-procedur, ne vozvrashchayushchih kakogo-libo rezul'tata.
Lyubaya peremennaya v yazyke dolzhna byt' opisana (deklarirovana) pered
ispol'zovaniem. Dlya prostejshih tipov sintaksis deklaracij prost (i, v
osnovnom, C-podoben):
!! I, J, K -- bezznakovye celye peremennye
u_int I = 1, J = 2, K = 3;
!! X, Y, Z -- plavayushchie peremennye standartnoj tochnosti
float X, Y, Z = 0.001;
!! DONE -- logicheskaya peremennaya
bool DONE = false
Iz etih primerov takzhe vidno, chto opisanie peremennoj mozhet
soprovozhdat'sya ee inicializaciej (i eto rekomenduemaya praktika). Esli
peremennaya primitivnogo tipa ne inicializirovana yavno, ona budet soderzhat'
neopredelennoe znachenie (musor).
Naryadu s obychnymi peremennymi, v yazyke prisutstvuyut konstanty --
peremennye, znachenie kotoryh posle opisaniya ne mozhet byt' izmeneno. Opisanie
konstanty predvaryaetsya klyuchevym slovom const. Ponyatno, chto konstanta
nepremenno dolzhna byt' inicializirovana:
!! space -- simvol'naya konstanta
char const space = ‘ ‘;
!! factor -- plavayushchaya konstanta
float const factor = 2 * PI * PI;
!! median -- celaya konstanta
int const median = (low + high) // 2
Naryadu s konstantnost'yu, vazhnym atributom peremennoj yavlyaetsya rezhim
razmeshcheniya, ukazyvayushchij kakim imenno obrazom peremennaya budet sozdana, i
skol'ko vremeni ona prosushchestvuet. Po umolchaniyu, rezhim razmeshcheniya opredelyaet
kontekst opisaniya: v zavisimosti ot togo mesta, gde vstretilos' opisanie,
peremennaya mozhet byt' ob®yavlena global'noj, lokal'noj (v funkcii) ili
komponentnoj (v klasse). Odnako, lyubaya peremennaya ili konstanta mozhet byt'
yavno opisana kak staticheskaya (static), t.e. imeyushchaya vremya zhizni, sovpadayushchee
so vremenem vypolneniya programmy:
u_int i, j, static counter = 0
Zamet'te, chto klyuchevoe slovo static -- atribut opisyvaemoj peremennoj
(v dannom sluchae, counter), a ne opisaniya v celom, kak prinyato v C.
Dlya primitivnyh tipov dannyh opredeleno mnozhestvo operacij. Podrobno
rassmatrivat' sistemu operacij my ne budem, tak kak ona vo mnogom
pozaimstvovana iz C. Otmetim lish' naibolee sushchestvennye razlichiya. Tak, v
otlichie ot C, v Kserione razlichayutsya operacii plavayushchego ('/') i
celochislennogo ('//') deleniya (a vzyatie ostatka ot deleniya vypolnyaetsya
operaciej '-/'). Naryadu s privychnymi dlya C-programmista operaciyami bitovyh
sdvigov vpravo i vlevo ('<<' i '>>'), sushchestvuyut takzhe binarnye
operacii bitovogo vrashcheniya ('<.<' i '>.>'), i unarnaya operaciya
transpozicii ('>.<'). (Poslednyuyu operaciyu mozhno opisat' kak
"perestanovku polovinok": dlya znacheniya tipa u_tiny ona menyaet mestami
starshie i mladshie 4 bita, dlya u_short - starshie i mladshie 8 bitov i t.d.)
Razumeetsya, predusmotreny privychnye dlya C-programmista operacii inkrementa
('++') i dekrementa ('-- ') v prefiksnoj i postfiksnoj forme.
Vse operacii sravneniya vozvrashchayut znachenie tipa bool. Dlya vseh tipov
opredeleny operacii sravneniya na ravenstvo ('--') i neravenstvo
('<>'), a dlya mnogih tipov dannyh opredeleny takzhe sravneniya na
uporyadochennost' ('<', '<=', '>', '>='). V chastnosti, vse
primitivnye tipy yavlyayutsya uporyadochennymi. (Dlya chislovyh tipov eto
samoochevidno, simvol'nye tipy uporyadocheny v sootvetstvii s vnutrennej
kodirovkoj, a dlya logicheskih znachenij prinyato false < true). Krome togo,
dlya vseh primitivnyh tipov opredeleny binarnye operacii "maksimum" ('?>')
i "minimum" ('?<'), vozvrashchayushchie, sootvetstvenno, bol'shij i men'shij iz
svoih operandov.
Obychnye binarnye arifmetiko-logicheskie operacii "i" ('&'), "ili"
('|') i "isklyuchayushchee ili" ('~') primenimy kak k logicheskim, tak i k celym
znacheniyam (v poslednem sluchae oni vypolnyayutsya pobitno). |to zhe spravedlivo i
dlya unarnoj operacii "ne" ('~'). Tol'ko dlya tipa bool opredeleny
uslovno-logicheskie operacii "i" i "ili" ('&&' i '||'), kotorye, kak
i v C, po vozmozhnosti izbegayut vychisleniya vtorogo operanda. Est' i
C-podobnaya ternarnaya operaciya vybora: X ? Y : Z ponimaetsya kak "esli X
(vyrazhenie tipa bool), to Y, inache Z". Nakonec, operaciya prisvaivaniya ('=')
kak i v C vozvrashchaet prisvoennoe znachenie v kachestve rezul'tata (ona
opredelena ne tol'ko dlya primitivnyh tipov, no ob etom pozzhe). Est' takzhe
operacii prisvaivaniya, sovmeshchennogo s bol'shinstvom binarnyh operacij, takie
kak '+=', '-=', '*=', '/=' i t.p.
V otlichie ot C i Java, v yazyke otsutstvuet binarnaya operaciya ','
(posledovatel'nost'). No vmesto nee imeetsya bolee moshchnoe sredstvo: vyrazhenie
mozhet dopolnyat'sya vstroennym blokom koda, vypolnyayushchimsya do ili posle ego
vychisleniya:
!! "vstroennyj blok", prefiksnaya forma
({ STMT_LIST } EXPR);
!! "vstroennyj blok", postfiksnaya forma
(EXPR { STMT_LIST })
V obeih formah eto vyrazhenie vozvrashchaet znachenie vyrazheniya EXPR.
Odnako, pri etom eshche i vypolnyayutsya instrukcii iz STMT_LIST -- do vychisleniya
EXPR (v prefiksnoj forme) ili posle nego (v postfiksnoj). K sozhaleniyu, blok
instrukcij ne mozhet napryamuyu vernut' znachenie, kotoroe mozhno bylo by
ispol'zovat' v vyrazhenii.
Sistema operacij yazyka vsem perechislennym ne ogranichivaetsya, no
operacii, opredelennye dlya proizvodnyh i ob®ektnyh tipov my rassmotrim chut'
pozzhe. Nakonec, v yazyke imeyutsya binarnye operacii vvoda ('<:') i vyvoda
(':>'), kotorye neobychny tem, chto voobshche ne imeyut nikakoj
predopredelennoj semantiki, i prednaznacheny isklyuchitel'no dlya
pereopredeleniya (naprimer, dlya operacij vvoda-vyvoda v sistemnyh
bibliotekah).
Sistema prioritetov neskol'ko otlichaetsya ot prinyatoj v C. Skazhem,
operacii sdvigov i vrashcheniya schitayutsya mul'tiplikativnymi, t.e. imeyut tot zhe
prioritetnyj uroven', chto i umnozhenie i delenie. Prioritet logicheskih i
uslovno-logicheskih operacij odinakov (i bolee nizok, chem u sravnenij). Vse
binarnye operacii, krome operacij prisvaivaniya, imeyut levuyu associativnost'.
Znacheniya primitivnyh tipov mogut neyavno preobrazovyvat'sya drug v druga,
no pravila etih preobrazovanij prinyaty bolee zhestkie, nezheli v C. Dopustimy
lish' te preobrazovaniya, kotorye ne privodyat k potere informacii. Tak,
mladshie celochislennye tipy mogut obobshchat'sya do starshih (tiny → short
→ int → long), tak zhe, kak i vse plavayushchie (float → double
→ quad) i vse simvol'nye (char → w_char), a celochislennye
znacheniya neyavno obobshchayutsya do plavayushchih. Drugie neyavnye preobrazovaniya
zapreshcheny: v chastnosti, nel'zya neyavno ispol'zovat' simvol'nye i logicheskie
znacheniya v kachestve celyh (i naoborot). Bol'shinstvo operacij takzhe ne
pozvolyayut smeshivat' celye operandy so znakom i bez znaka: oni dolzhny byt'
privedeny k edinoj znakovosti vo izbezhanie vozmozhnoj neodnoznachnosti.
Kogda neyavnye preobrazovaniya ne rabotayut, mozhno pribegnut' k operacii
yavnogo privedeniya tipov, imeyushchej takoj vid:
:TYPE EXPR !! preobrazovat' vyrazhenie EXPR k tipu TYPE
Semantika podobnogo preobrazovaniya takzhe ne tait v sebe osobyh
syurprizov: plavayushchie znacheniya preobrazuyutsya v celye putem otbrasyvaniya
drobnoj chasti, simvol'nye v chislovye -- v sootvetstvii so svoej kodirovkoj,
a logicheskie znacheniya false i true schitayutsya ekvivalentnymi 0 i -1. Ochen'
vazhno zametit', chto eta operaciya preobrazovaniya opredelena tol'ko dlya
primitivnyh tipov, i k proizvodnym, v otlichie ot C, ona neprimenima.
Massivy (vektornye tipy)
Massivy -- odnorodnye nabory znachenij edinogo tipa, obespechivayushchie
proizvol'nyj dostup k lyubomu iz etih znachenij (elementov) po celochislennomu
indeksu -- eto odno iz principial'no vazhnyh sredstv yazyka. V otlichie ot Java
i mnogih drugih yazykov, massivy ne yavlyayutsya ob®ektami v smysle OOP. Oni
mogut imet' te zhe svojstva i atributy (rezhim razmeshcheniya, konstantnost' i
pr.), chto i peremennye primitivnyh tipov.
Vot primery opisanij massivov:
!! intvec -- massiv iz LENGTH celyh
int [LENGTH] intvec;
!! text -- matrica simvolov, (HEIGHT strok) * (WIDTH stolbcov)
char [WIDTH][HEIGHT] text
Obratite vnimanie na to, chto sintaksis opisaniya massivov -- prefiksnyj:
konstrukciya vida [SIZE] nazyvaetsya prefiksom opisaniya (deklaratorom)
massiva. Ona oznachaet, chto tip deklariruemyh dalee ob®ektov menyaetsya s TYPE
na TYPE [SIZE] (massiv iz SIZE elementov tipa TYPE). Vkladyvaya vektornye
deklaratory drug v druga, mozhno opisyvat' dvuh- i bolee mernye massivy.
Strogo govorya, ponyatie "mnogomernyj massiv" v yazyke otsutstvuet -- ih s
uspehom zamenyayut massivy, sostoyashchie iz massivov, i tak dalee. Imenno eto my
budem podrazumevat', govorya ob n-mernyh massivah (pri etom chislo n my budem
nazyvat' razmernost'yu, ili rangom massiva). Odnako, nikakimi special'nymi
svojstvami mnogomernye massivy ne obladayut (t.e. semantika vseh operacij nad
nimi vyvoditsya iz semantiki operacij nad odnomernymi massivami).
Zametim, chto prefiksnyj sintaksis v opisaniyah massivov -- eto ne
isklyuchenie. Vse proizvodnye tipy yazyka vvodyatsya s pomoshch'yu analogichnyh
prefiksnyh konstrukcij, blagodarya chemu dazhe samye slozhnye i zaputannye
opisaniya chitayutsya dostatochno legko i edinoobrazno -- sprava nalevo (ot
peremennoj ili drugogo opisyvaemogo ob®ekta k "kornyu" opisaniya). Kak i v C,
prefiks(y) opisanij imeyut bolee vysokij prioritet, chem zapyataya, razdelyayushchaya
deklaracii v spiske:
int [10] aa, bb !! aa -- massiv iz 10 celyh, bb -- celoe
Odnako, chasto neobhodimo opisat' neskol'ko massivov odinakovoj
razmernosti. Togda prefiks massiva (kak i lyuboj obshchij prefiks proizvodnogo
tipa) mozhno "vynesti za skobki" (figurnye). |tot priem, nazyvaemyj
faktorizaciej, ochen' uproshchaet slozhnye opisaniya:
int [10] { aa, bb } !! aa i bb -- massivy iz 10 celyh
Faktorizaciyu mozhno primenyat' i rekursivno:
int i, [10] { v, [20] { vv, [30] vvv } };
!! bolee gromozdkaya forma predydushchego opisaniya:
int i, [10] v, [10][20] vv, [10][20][30] vvv
V kachestve razmera massiva trebuetsya nekoe vyrazhenie tipa u_int. Na
nego ne nakladyvaetsya drugih ogranichenij -- v chastnosti, ne trebuetsya, chtoby
ono bylo vychislyaemym vo vremya kompilyacii. V obshchem sluchae razmer massiva
opredelyaetsya tol'ko pri vypolnenii programmy. Odnako, on fiksirovan v tom
smysle, chto vychislyaetsya odin raz, posle chego uzhe ne mozhet izmenit'sya (v
yazyke net nastoyashchih gibkih massivov, razmery kotoryh mozhno menyat' "na
letu"). V zhestkoj sisteme tipov yazyka razmery massivov rassmatrivayutsya kak
osobyj sluchaj: vse, chto svyazano s nimi, obychno proveryaetsya tol'ko pri
vypolnenii programmy. Massiv mozhet dazhe okazat'sya pustym, t.k. nulevoj
razmer ne schitaetsya oshibkoj.
Dlya massivov opredelen ryad operacij. Tak, poskol'ku razmer massiva
vsegda izvesten kompilyatoru i ispolnyayushchej sisteme yazyka, ego netrudno uznat'
s pomoshch'yu unarnoj postfiksnoj operacii '#'. Dlya peremennyh, opisannyh vyshe:
intvec#; !! vozvrashchaet znachenie LENGTH (u_int)
text#; !! vozvrashchaet znachenie HEIGHT (u_int)
vvv# !! vozvrashchaet 30 (u_int)
CHasto rabota s massivom osushchestvlyaetsya poelementno. Binarnaya operaciya
indeksirovaniya pozvolyaet v lyuboj moment poluchit' dostup k lyubomu elementu
massiva. Tak zhe, kak i v C, otschet indeksov vedetsya s nulya:
!! pervyj element massiva intvec (int)
intvec [0];
!! poslednij element massiva intvec (int)
intvec [LENGTH - 1];
!! "verhnyaya" stroka matricy text (char [WIDTH])
text [0];
!! "nizhnyaya" stroka matricy text (char [WIDTH])
text [HEIGHT - 1];
!! "levyj verhnij" simvol matricy text (char)
text [0][0];
!! "pravyj nizhnij" simvol matricy text (char)
text [HEIGHT - 1][WIDTH - 1]
Operaciya indeksirovaniya vsegda proveryaet korrektnost' indeksa, ne
pozvolyaya obratit'sya k nesushchestvuyushchemu elementu. Esli pri vychislenii A [I] ne
soblyudaetsya uslovie I < A#, normal'noe vypolnenie programmy prervetsya i
budet vozbuzhdena isklyuchitel'naya situaciya (ArraySizeException). Zametim
takzhe, chto hotya pri opisanii massiva my ispol'zovali prefiksnyj sintaksis,
dlya dostupa k elementu ispol'zuetsya privychnaya postfiksnaya notaciya.
(Izvestnyj po yazyku C princip "deklaraciya imitiruet ispol'zovanie" v
Kserione veren "s tochnost'yu do naoborot": opisateli dlya massivov, ukazatelej
i funkcionalov ispol'zuyut prefiksnyj sintaksis, no sootvetstvuyushchie operacii
nad etimi tipami (indeksirovanie, razymenovanie, vyzov funkcii) -- tol'ko
postfiksnyj).
Vozmozhen ne tol'ko poelementnyj dostup k massivam: v yazyke opredelen
ryad agregatnyh operacij, pozvolyayushchih rabotat' s massivami, kak s edinym
celym. No prezhde zametim, chto tam, gde mozhno rabotat' s massivom,
dopuskaetsya rabota i s lyubym ego nepreryvnym fragmentom (otrezkom).
Ternarnaya operaciya vzyatiya otrezka -- A [FROM..TO] -- vozvrashchaet otrezok
massiva A ot (vklyuchitel'no) elementa s indeksom FROM do (ne vklyuchaya)
elementa s indeksom TO (t.e. spravedlivo tozhdestvo: A [FROM..TO]# -- TO --
FROM). Razumeetsya, korrektnost' indeksov proveryaetsya (esli narusheno uslovie
FROM <= TO && TO <= A#, vozbuzhdaetsya znakomoe nam isklyuchenie
ArraySizeException). Vprochem, otrezok nulevoj dliny dopustim, takzhe kak i
massiv.
V [0 .. N] !! otrezok: nachal'nye N elementov massiva V
V [V#-N .. V#] !! otrezok: konechnye N elementov massiva V
V otlichie ot indeksirovaniya, operaciya polucheniya otrezka nikogda ne
ponizhaet rang massiva: rezul'tat vsegda imeet tu zhe razmernost', chto i
operand. Otrezok dlinoj 1 -- eto massiv dliny 1, a ne odin element.
Vsledstvie etogo, pri rabote s mnogomernym massivom mozhno poluchit' otrezok
tol'ko po samomu vneshnemu izmereniyu, t.k. vse vnutrennie dlya etoj operacii
nedostupny. Nakonec, otmetim, chto operacii vzyatiya indeksa i otrezka
sohranyayut takie osobennosti svoego operanda, kak konstantnost' i L-kontekst
(t.e. esli massiv konstanten, to lyuboj ego element takzhe yavlyaetsya konstantoj
i t.p.).
Zavershaya razgovor ob indeksirovanii massivov, sleduet upomyanut' osobuyu
operaciyu "pustoj indeks". Ona polezna v osnovnom dlya polucheniya vnutrennih
razmerov mnogomernyh massivov:
text [0]# !! vozvrashchaet WIDTH
text []# !! to zhe samoe
Vtoraya zapis' nemnogo koroche, a glavnoe -- yavno podcherkivaet, chto
operaciya indeksirovaniya zdes' nosit fiktivnyj harakter, t.k. nam nuzhen ne
opredelennyj element massiva text, a lish' dostup k obshchemu tipu ego
elementov. Rezul'tat, vydavaemyj operaciej [] -- t.n. neopredelennoe
vyrazhenie, imeyushchee tip, no ne znachenie. Podrobnee o semantike neopredelennyh
vyrazhenij, i sluchayah, kogda oni mogut potrebovat'sya, my pogovorim pozzhe.
Dlya massivov, kak i dlya primitivnyh tipov, dostupno prisvaivanie:
float [25] { VA, VB };
VA = VB !! skopirovat' vse elementy iz massiva VA v massiv VB
Dlya prisvaivaniya massivov trebuetsya, chtoby tipy ih elementov tochno
sovpadali (t.k. neyavnye privedeniya, dostupnye dlya primitivnyh tipov, ne
obobshchayutsya na massivy iz nih). Pomimo etogo, dolzhny sovpadat' i razmery
prisvaivaemyh massivov (po vsem izmereniyam, esli oni mnogomernye). Zamet'te,
chto v privedennom sluchae ih sovpadenie ochevidno, i potomu proverka perioda
vypolneniya budet opushchena. Odnako, vot primer bolee obshchej situacii:
char [SIZE1] str1, [SIZE2] str2;
str1 = str2
Zdes' pered prisvaivaniem proizojdet proverka usloviya SIZE1 -- SIZE2,
i, esli ono okazhetsya lozhnym, budet vozbuzhdeno vse to zhe isklyuchenie
ArraySizeException.
Ne menee vazhno to, chto massivu mozhno prisvoit' skalyar. V etom sluchae
ego znachenie (vychislennoe odin raz) budet "razmnozheno" i prisvoeno vsem
elementam massiva. |tot priem nazyvaetsya vektorizaciej i obobshchaetsya na
mnogomernye massivy: massivu mozhet byt' prisvoen massiv men'shego ranga --
pri etom on "razmnozhaetsya" po odnomu ili bol'shemu chislu izmerenij. Kak i pri
obychnom prisvaivanii, trebuetsya identichnost' bazovyh tipov massivov, a vse
"vnutrennie" razmery obyazatel'no budut provereny na ravenstvo:
!{ Prisvaivaet str1 vsem HEIGHT strokam matricy text
(predvaritel'no ubedivshis', chto text []# -- str1#,
t.e. WIDTH -- SIZE1) }!
text = str1
Poryadok prisvaivaniya elementov v massive schitaetsya neopredelennym.
CHasto eto dejstvitel'no ne principial'no, odnako pri prisvaivanii
perekryvayushchihsya otrezkov odnogo i togo zhe massiva on okazyvaetsya
sushchestvennym. Poetomu sushchestvuyut dve special'nye formy operacii
prisvaivaniya: inkrementnaya ('=#') i dekrementnaya ('=#@') (oni opredeleny
tol'ko dlya massivov):
A [10..19] =# A [15..24]; !! inkrementnoe prisvaivanie
A [10..19] =#@ A [15..24] !! dekrementnoe prisvaivanie
Zdes' operandami yavlyayutsya dva perekryvayushchihsya otrezka massiva A. V
pervom sluchae prisvaivanie budet osushchestvlyat'sya ot pervogo elementa k
poslednemu, t.e. budet nerazrushayushchim i vse elementy "uceleyut" pri
kopirovanii. Vo vtorom sluchae, kopirovanie proizojdet v obratnom poryadke,
pri etom otrezok chastichno perezapishet sam sebya. |to ne obyazatel'no oshibka.
Naprimer, esli neobhodimo "razmnozhit'" nebol'shoj otrezok na vsyu dlinu
massiva, prisvaivanie s odnovremennoj "avtomaticheskoj" perezapis'yu yavlyaetsya
vpolne dopustimym (i ves'ma effektivnym) tehnicheskim priemom.
Kak i peremennye primitivnyh tipov, massivy mogut byt' (a konstantnye
-- i dolzhny byt') inicializirovany. Konechno, vse, chto mozhet byt' prisvoeno
massivu, yavlyaetsya i zakonnym inicializatorom dlya nego. Odnako, pomimo etogo,
dopuskaetsya eshche odna forma inicializacii massiva -- spiskovaya.
int [5] List1 = { 1, 2, 3, 4, 5 };
int [5] List2 = { 1, List1[2]*3, List1[0]*List1[4] + 2, 4, List1# }
Kak legko videt' iz vtorogo primera, inicializatory -- lyubye vyrazheniya,
sootvetstvuyushchie tipu elementov massiva. Oni vychislyayutsya tol'ko pri
vypolnenii inicializacii. Stol' zhe gibkij podhod dopustim i pri
inicializacii mnogomernyh massivov. Vot vpolne zakonnyj, hotya i neskol'ko
nadumannyj primer:
int [3][5] Matrix = {
!! stroka #0: zadadim spiskom
{ 100, 200, 300 },
!! stroka #1: voz'mem iz List1
List1 [0..3],
!! stroka #2: zadadim spiskom
{ List1[0]*List2[2], List1[1]*List2[1], List1[2]*List2[0] },
!! stroka #3: voz'mem iz List2
List2 [2..5],
!! stroka #4: vektorizuem 100 na 3 elementa
100
}
Spiskovye inicializatory massivov -- primer t.n. inicializiruyushchih
vyrazhenij, opredelennyh i dlya nekotoryh drugih tipov. Ih mozhno ispol'zovat'
tol'ko v kontekste inicializacii dlya peremennoj dannogo tipa, t.e.
ispol'zovat' spisok elementov, skazhem, kak prisvaivaemoe znachenie, nel'zya:
List1 = { 10, 20, 30, 40, 50 } !! oshibka!
V yazyke imeetsya ne tol'ko agregatnoe prisvaivanie, no i agregatnoe
sravnenie. Dlya togo, chtoby dva massiva byli sravnimymi, trebuetsya, kak i pri
prisvaivanii, tochnoe sovpadenie ih bazovyh tipov. Odnako, razlichie v
razmerah pri sravnenii ne schitaetsya fatal'noj oshibkoj. Proshche vsego opisat'
semantiku sravnenij na ravenstvo/neravenstvo: dva massiva schitayutsya ravnymi,
esli ravny ih razmery i sootvetstvuyushchie elementy poparno; v protivnom sluchae
oni ne ravny:
str1 -- str2; !! istinno, esli str1# -- str2#
!! I str1 [I] -- str2 [I] dlya lyubogo I
str1 <> str2 !! v protivnom sluchae
Esli zhe bazovyj tip massivov uporyadochen (naprimer, yavlyaetsya primitivnym
tipom), dopustimo takzhe sravnenie massivov na uporyadochennost'. Pri etom
semantika sravneniya opredelena analogichno leksikograficheskomu ("slovarnomu")
sravneniyu simvol'nyh strok. Vot strogoe opredelenie operacij "bol'she" i
"men'she" dlya massivov:
str1 < str2; !! istinno, esli sushchestvuet takoe N, chto
!! 1) str1 [0..N] -- str2 [0..N]
!! 2) str1#
-- N && str2# > N
!! ILI ZHE
!! str1 [N]
< str2 [N]
str1 > str2 !! istinno, esli sushchestvuet takoe N, chto
!! 1) str1 [0..N] -- str2 [0..N]
!! 2) str1# > N &&
str2# -- N
!! ILI ZHE
!! str1 [N]
> str2 [N]
Drugimi slovami: massiv str1 men'she [bol'she] massiva str2, esli pervyj
otlichayushchijsya element massiva str1 men'she [bol'she] sootvetstvuyushchego elementa
massiva str2, ili zhe esli vse elementy str1 ravny elementam str2, a dlina
str1 men'she dliny str2 [... vse elementy str2 ravny elementam str1, a dlina
str2 men'she dliny str1].
Pravila sravneniya massivov rekursivno obobshchayutsya na massivy bolee
vysokih razmernostej. Esli odin iz operandov sravneniya imeet men'shij rang,
chem drugoj, on neyavno podvergaetsya vektorizacii po vsem "nedostayushchim"
vneshnim izmereniyam. Prodemonstriruem vse eto na primerah:
str1 -- ‘ ‘ !! istinno, esli vse simvoly str1 -- probely
str1 <> ‘ ‘ !! istinno, esli hotya by odin simvol str1 otlichen ot
probela
str1 -- text !! istinno, esli str1# -- text []#
!! I vse stroki text sovpadayut s str1
str1 <> text !! istinno, esli str1# <> text []#
!! ILI hotya by odna stroka text otlichna ot str1
Vozmozhnost' sravneniya massivov, bezuslovno, cenna, no ne menee vazhno
znat', v kakom imenno meste oni razlichayutsya. Dlya etogo predusmotreny
operacii skaniruyushchego sravneniya (skanirovaniya). Dlya kazhdoj iz operacij
prostogo sravneniya ('--', '<>', '<', '>' ...) imeetsya
sootvetstvuyushchaya operaciya inkrementnogo ('--#', '<>#', '<#', '>#'
...) i dekrementnogo ('--#@', '<>#@', '<#@', '>#@' ...)
skanirovaniya. Vo mnogom oni podobny sootvetstvuyushchim im operaciyam sravneniya,
v chastnosti, oni pred®yavlyayut absolyutno te zhe trebovaniya k tipam operandov i
vypolnyayutsya prakticheski takim zhe obrazom. Glavnoe otlichie -- vozvrashchaemoe
imi znachenie imeet ne tip bool, a tip u_int -- i oznachaet ono,
sootvetstvenno ne istinnost'/lozhnost' operacii sravneniya v celom, a chislo
elementov massiva (nachal'nyh dlya inkrementnyh operacij, konechnyh -- dlya
dekrementnyh), dlya kotoryh sootvetstvuyushchee uslovie udovletvoryaetsya. Tak, dlya
skanirovaniya na ravenstvo:
!! v inkrementnoj forme:
VAL -- A --# B; !! oznachaet, chto:
!! A [0..VAL] -- B [0..VAL]
!! I
!! A [VAL] <> B [VAL]
!! (esli oni sushchestvuyut).
!! v dekrementnoj forme:
VAL -- A --#@ B; !! oznachaet, chto:
!! A [A#-VAL..A#] -- B [B#-VAL..B#]
!! I
!! A [A#-VAL-1] <> B [B#-VAL-1]
!! (esli oni sushchestvuyut).
Kak i pri sravnenii, operandy skanirovaniya mogut podvergat'sya
vektorizacii. Takim obrazom, skanirovanie mozhno ispol'zovat' i v kachestve
operacii poiska elementa v massive:
!! najti pervyj probel v massive str1:
if (first_count = str1 <># ‘ ‘) -- str1#
{ !( probely ne najdeny ... )! }
else { !( str1 [first_count] -- pervyj probel )! }
!! najti poslednij probel v massive str1:
if (last_count = str1 <>#@ ‘ ‘) -- str1#
{ !( probely ne najdeny ... )! }
else { !( str1 [str# - last_count - 1] -- poslednij probel )! }
Rezyumiruya zametim, chto sistema vektornyh operacij yazyka mozhet ponachalu
pokazat'sya dovol'no slozhnoj. Tem ne menee, vozmozhnost' otnositel'no
kompaktnoj zapisi dovol'no slozhnyh operacij nad massivami slishkom cenna,
chtoby eyu prenebregat'. Krome togo, vse agregatnye operacii realizovany
maksimal'no effektivno, i ih ispol'zovanie mozhet dat' ves'ma sushchestvennyj
vyigrysh, osobenno v bibliotekah i drugih sistemno-znachimyh komponentah.
Ukazatel'nye i ssylochnye tipy
Realizaciya netrivial'nyh struktur dannyh, takih, kak linejnye i
kol'cevye spiski, derev'ya, grafy i seti byla by prakticheski nereal'na bez
ukazatelej. V tom ili inom vide takoj mehanizm predusmotren v lyubom yazyke.
Dazhe v Java, gde deklarirovan otkaz ot ukazatelej, eta koncepciya neyavno
prisutstvuet, t.k. vse massivy i ob®ekty dostupny tol'ko cherez ssylki. V
Kserione podhod yavlyaetsya bolee tradicionnym: kak i v C i Paskale, dostupny
ukazateli na peremennye lyubyh tipov. Pravda, v otlichie ot C, v ispol'zovanie
ukazatelej vnesen ryad ogranichenij, prodiktovannyh soobrazheniyami
bezopasnosti.
Vse ukazatel'nye tipy dannyh vvodyatsya s pomoshch'yu prefiksnogo opisatelya
'^'. Naprimer:
int ^ip; !! ip - ukazatel' na celoe
int ^^ipp !! ipp - ukazatel' na ukazatel' na celoe
|ti dva opisaniya legko ob®edinit' s pomoshch'yu faktorizacii:
int ^{ ip, ^ ipp } !! to zhe, chto i vyshe
Prefiks '^' mozhet predvaryat'sya klyuchevymi slovami const, limited i
strict, smysl kotoryh my rassmotrim chut' pozzhe. Dlya vseh ukazatel'nyh tipov
opredelen edinstvennyj literal -- nil, oznachayushchij otsutstvie ssylochnogo
znacheniya.
S ukazatelyami pryamo svyazany dve operacii: imenovanie i razymenovanie.
Tak, L-vyrazhenie lyubogo tipa legko prevratit' v ukazatel' na etot tip s
pomoshch'yu operacii imenovaniya (postfiks '@'):
int a; double b;
a@; !! ukazatel' na peremennuyu a (int ^)
b@ !! ukazatel' na peremennuyu b (float ^)
Obratnaya operaciya -- razymenovanie (postfiks '^') -- pozvolyaet perejti
ot ukazatelya k peremennoj (konstante), na kotoruyu on ukazyvaet (rezul'tat
etoj operacii -- L-vyrazhenie). Ponyatno, chto popytka razymenovaniya znacheniya
nil vyzovet oshibku perioda vypolneniya (NilDerefException).
ip^; !! razymenovat' ip (int)
ipp^; !! razymenovat' ipp (int ^)
ipp^^ !! razymenovat' ipp dvazhdy (int)
Tradicionno ukazateli schitayutsya dovol'no opasnym yazykovym mehanizmom.
Po etoj prichine v Kserione imeetsya ryad ogranichenij na ih ispol'zovanie.
Prezhde vsego, v otlichie ot primitivnyh tipov, dlya ukazatel'nyh tipov
dejstvuet prinuditel'naya inicializaciya: esli ukazatel'naya peremennaya