ne
inicializirovana yavno, ona inicializiruetsya znacheniem nil, blagodarya chemu
ukazateli vsegda soderzhat nekoe osmyslennoe znachenie. |to pravilo, konechno,
rasprostranyaetsya i na massivy iz ukazatelej.
Dalee, sistema tipov yazyka nadezhno obespechivaet tipobezopasnost'
ukazatelej. V otlichie ot C, ne sushchestvuet nikakoj operacii, pozvolyayushchej
privodit' ukazatel' na odin tip k ukazatelyu na drugoj (krome mehanizma qual,
obespechivayushchego bezopasnoe preobrazovanie ukazatelej na rodstvennye
ob®ektnye tipy, kotoryj my rassmotrim pozzhe).
Pomimo tipizacionnogo kontrolya, vsegda dejstvuet i kontrol'
aktual'nosti ukazatelej. |tot mehanizm perioda kompilyacii ne pozvolyaet
prisvoit' ssylku na peremennuyu ukazatelyu, imeyushchemu bolee shirokuyu oblast'
sushchestvovaniya, preduprezhdaya takim obrazom opasnost' poyavleniya "visyachih"
ssylok.
int iv1, ^ip1;
{
int iv2, ^ip2;
ip1 = iv1@; !! zakonno
ip2 = iv2@; !! zakonno
ip1 = iv2@; !! oshibka!
ip2 = iv1@; !! zakonno
ip1 = ip2; !! oshibka!
ip2 = ip1 !! zakonno
}
Predusmotren takzhe kontrol' konstantnosti, svyazannyj s ponyatiem
konstantnyh ukazatelej. Ukazatel', deklarirovannyj kak konstantnyj (const),
mozhet ukazyvat' tol'ko na konstantnye znacheniya. Rezul'tat imenovaniya
konstanty porozhdaet konstantnyj ukazatel', a rezul'tat razymenovaniya
konstantnogo ukazatelya -- konstantnoe znachenie. Esli prisvaivanie obychnogo
ukazatelya konstantnomu dopustimo, to obratnoe zapreshchaetsya. Takim obrazom,
obojti konstantnost' znacheniya nel'zya, dazhe pribegaya k ukazatelyam.
Nakonec, nemalovazhnuyu rol' igraet otsutstvie potencial'no opasnyh
operacij nad ukazatelyami. Tak, v protivopolozhnost' C, dlya ukazatelej ne
opredeleny inkrement, dekrement, additivnye operacii i dazhe sravneniya na
uporyadochennost'. Pomimo imenovaniya i razymenovaniya dlya ukazatelej dostupny
tol'ko inicializaciya, prisvaivanie, i sravnenie na ravenstvo/neravenstvo. V
obshchem sluchae dlya prisvaivaniya i/ili sravneniya ukazatelej trebuetsya tochnoe
sovpadenie vseh promezhutochnyh tipov (za otdel'nymi melkimi poslableniyami, na
kotoryh my podrobno ostanavlivat'sya ne budem).
Ukazateli osobenno vazhny kak sredstvo dlya raboty s dinamicheskimi
peremennymi, sozdavaemymi vo vremya vypolneniya programmy. Dlya sozdaniya
podobnoj peremennoj ispol'zuetsya special'nyj term opisaniya -- allokator,
effekt vypolneniya kotorogo sostoit v sozdanii dinamicheskoj peremennoj s
nemedlennym sohraneniem ukazatelya na nee. Privedem primer:
!! sperva nado deklarirovat' ukazateli ...
int ^ip, [4] ^ivp;
!! teper' sozdadim ob®ekty, na kotorye oni budut ukazyvat' ...
int alloc (ip) = 5, [4] alloc (ivp) = { 0, 10, 20, 30 };
!! ... posle chego ih mozhno ispol'zovat':
ip^; !! 5 (int)
ivp^#; !! 4 (u_int)
ivp^ [3]; !! 30 (int)
Ispol'zuemyj sintaksis mozhet pokazat'sya neprivychnym. Esli by v Kserione
byl C++ podobnyj operator new, eti dejstviya zapisyvalis' by primerno tak:
ip = new int;
ip^ = 5;
ivp = new int [4];
ivp^ = { 0, 10, 20, 30 }
Sintaksicheski konstrukciya alloc (PTR) yavlyaetsya termom opisaniya, t.e.
ona mozhet byt' ispol'zovana vezde, gde dopustimo opisanie obychnoj peremennoj
ili konstanty. Esli tip konteksta opisaniya TYPE, to operand allokatora PTR
-- proizvol'noe L-vyrazhenie tipa TYPE ^, igrayushchee rol' "priemnika" dlya
ukazatelya na sozdannuyu dinamicheskuyu peremennuyu. Pri etom allokator -- chisto
ispolnyaemaya konstrukciya, ne imeyushchaya nikakogo deklarativnogo effekta.
Blagodarya tomu, chto ona pomeshchena v kontekst opisaniya, k dinamicheskoj
peremennoj mozhno primenyat' inicializatory, imeyushchie privychnyj sintaksis.
Sozdannaya dinamicheskaya peremennaya iznachal'no dostupna tol'ko cherez
ukazatel' PTR. Operacii, obratnoj alloc, ne sushchestvuet i ne trebuetsya,
poskol'ku upravlenie pamyat'yu v yazyke osushchestvlyaetsya dinamicheski. Ispolnyayushchaya
sistema podderzhivaet schetchik aktual'nyh ssylok na dinamicheskie peremennye.
Kogda poslednyaya ssylka teryaet aktual'nost', peremennaya avtomaticheski
unichtozhaetsya.
Sushchestvuyut ogranichennye ukazateli, pri opisanii kotoryh zadavalsya
atribut limited. Oni sposobny ukazyvat' tol'ko na ob®ekty s lokal'nym ili
staticheskim razmeshcheniem, no ne na dinamicheskie. Vvedenie v yazyk takih
"nepolnocennyh" ukazatelej prodiktovano soobrazheniyami effektivnosti: oni
trebuyut men'she mesta (32 bita vmesto 64) i bol'shinstvo operacij nad nimi
vypolnyaetsya nemnogo bystree. Prisvaivanie ogranichennyh ukazatelej obychnym
vsegda dopustimo, no obratnoe prisvaivanie mozhet vyzvat' isklyuchenie: esli
pri vypolnenii programmy proishodit popytka prisvoit' ogranichennomu
ukazatelyu ssylku na dinamicheskuyu peremennuyu, vozbuzhdaetsya isklyuchenie
PointerDomainException.
Sushchestvuet eshche odin tonkij aspekt ukazatelej, svyazannyj s ukazatelyami
na massivy. V kontekste ukazatel'nogo tipa massiv mozhet byt' "bezrazmernym"
(polnost'yu ili chastichno), t.e. kakie-to iz ego razmerov mogut byt' yavno ne
zadany:
float [] ^fv, [][] ^fvv
Zdes' fv i fvv -- ukazateli na odnomernyj i dvumernyj massivy iz
plavayushchih, imeyushchih proizvol'nye razmery. Nikakie proverki razmerov pri etom
ne otmenyayutsya -- prosto informaciya o nih budet hranit'sya vmeste s samimi
ukazatelyami. Esli fv prisvoit' ukazatel' na kakoj-nibud' massiv, informaciya
ob ego dline budet takzhe sohranena v otdel'nom pole fv, a pri razymenovanii
fv ona budet izvlechena ottuda dlya proverki. Takim obrazom, za
universal'nost' "bezrazmernyh" ukazatelej na massivy prihoditsya platit' tem,
chto kazhdoe "propushchennoe" izmerenie uvelichivaet razmer ukazatelya na 32 bita
(i nemnogo umen'shaet effektivnost' raboty s nim). Odnako, bez "bezrazmernyh"
ukazatelej sozdanie mnogih bibliotek funkcij i klassov obshchego naznacheniya
(skazhem, simvol'nyh strok) bylo by prosto nevozmozhnym.
V zavershenie neobhodimo upomyanut' o special'noj raznovidnosti
ukazatelej -- ssylkah. V obshchem-to ssylki otlichayutsya ot obychnyh ukazatelej v
dvuh aspektah: pri inicializacii ssylki k inicializatoru neyavno primenyaetsya
operaciya imenovaniya, a pri ispol'zovanii ssylki v lyubom kontekste ona neyavno
razymenovyvaetsya. Vo vseh ostal'nyh otnosheniyah ssylki analogichny ukazatelyam,
i mogut imet' te zhe svojstva i atributy. Pri opisanii ssylok vmesto prefiksa
'^' ispol'zuetsya prefiks '@'. Vot primer raboty s ssylkami:
char ch1 = ‘A', ch2 = ‘B'; !! simvol'nye peremennye
char ^pc = ch1@; !! pc: ukazatel' na ch1
pc^ = ‘C'; !! teper' ch1 -- ‘C'
char @rc = ch1; !! rc: ssylka na ch1
rc = ‘D'; !! teper' ch1 -- ‘D'
Ssylki Kseriona ves'ma pohozhi na analogichnyj mehanizm C++, no ne menee
vazhny i razlichiya. Esli v C++ ssylki -- special'nyj yazykovyj mehanizm (strogo
govorya, oni ne peremennye), to v Kserione im sootvetstvuyut obychnye
peremennye (ili konstanty), imeyushchie ssylochnyj tip. On mozhet ispol'zovat'sya
kak lyuboj drugoj proizvodnyj tip (dopustimy dazhe ssylki na ssylki i t.p.).
Nakonec, v otlichie ot C++, ssylka ne immutabel'na: esli ssylochnaya peremennaya
ne konstantna, ee mozhno izmenit' (t.e. zastavit' ssylat'sya na drugoj ob®ekt
podhodyashchego tipa), ispol'zuya tot fakt, chto operaciya imenovaniya dlya ssylki
vozvrashchaet L-vyrazhenie, podhodyashchee dlya prisvaivaniya:
rc@ = ch2@; !! teper' rc ssylaetsya na ch2
rc = ‘E'; !! teper' ch2 -- ‘E'
V Kserione ssylki i prostye ukazateli polnost'yu vzaimozamenyaemy. V
obshchem i celom, ssylki mozhno schitat' "arhitekturnym izlishestvom" -- odnako
oni, kak i v C++, predstavlyayut soboj sushchestvennoe notacionnoe udobstvo vo
mnogih sluchayah -- naprimer pri ispol'zovanii funkcij, ozhidayushchih parametr(y)
ukazatel'nyh tipov.
Funkcional'nye tipy i funkcii
Kak i v lyubom yazyke programmirovaniya, v Kserione imeetsya mehanizm
funkcij, i blizko svyazannoe s nimi ponyatie funkcional'nyh tipov dannyh
(funkcionalov). |to eshche odin mehanizm sozdaniya proizvodnyh tipov dannyh,
predstavlyayushchih fragmenty programmy, k kotorym mozhno obratit'sya (vyzvat' ih).
Vazhnejshimi atributami funkcional'nogo tipa yavlyayutsya spisok parametrov (s
opredelennymi imenami i tipami), peredavaemyh funkcionalu pri vyzove i
znachenie opredelennogo tipa, vozvrashchaemoe kak rezul'tat ego vypolneniya.
Funkcional'nyj tip vvoditsya kak proizvodnyj ot tipa vozvrashchaemogo
znacheniya s pomoshch'yu prefiksnogo opisatelya, imeyushchego vid ‘(' <spisok
parametrov> ‘)':
!! int_op - funkcional s dvumya celymi
!! parametrami (a, b), vozvrashchayushchij int
int (int a, b) int_op;
!! f_func -- funkcional s tremya parametrami raznyh tipov,
!! vozvrashchayushchij float
float (float [] ^farray; char ch1, ch2; bool flag) f_func;
Spisok parametrov -- eto posledovatel'nost' standartnyh opisanij,
razdelennaya tochkami s zapyatoj. Vse peremennye i konstanty, opisannye v
deklaratore, priobretayut status parametrov funkcionala. Obratite vnimanie na
to, chto opisannye zdes' int_op i f_func -- peremennye funkcional'nyh tipov
(ne "prototipy funkcij", kak mogli by podumat' znakomye s S++). Konechno, v
sushchestvovanii funkcional'nyh peremennyh i konstant ne bylo by smysla, esli
by v yazyke ne bylo sobstvenno funkcij:
int (int a, b) op_add { return a + b }; !! summa parametrov
int (int a, b) op_sub { return a -- b } !! raznost' parametrov
Esli term opisaniya imeet vid <imya> ‘{‘ <spisok instrukcij>
‘}', on opisyvaet funkciyu <imya>, imeyushchuyu sootvetstvuyushchij tip (on
dolzhen byt' funkcional'nym) i vypolnyayushchuyu blok instrukcij. Kak legko videt',
funkcii op_add i op_sub vozvrashchayut summu i raznost' svoih parametrov (hotya
instrukciyu return my eshche "ne prohodili", smysl ee vpolne ocheviden). Eshche raz
podcherknem, chto opisanie funkcii -- chastnyj sluchaj terma opisaniya, t.e.
mozhet vstretit'sya vezde, gde dopustimo opisanie peremennoj, i mozhet
sochetat'sya s drugimi opisaniyami, osnovannymi na tom zhe tipe (no ne pytajtes'
opisat' "funkciyu" ne funkcional'nogo tipa -- eto, konechno, semanticheskaya
oshibka). Dopustimy i obychnye priemy, takie, kak faktorizaciya v opisanii:
!! mozhno dobavit' umnozhenie i delenie ...
int (int a, b) { op_mul { return a * b }, op_div { return a // b } }
Identifikator funkcii yavlyaetsya literalom sootvetstvuyushchego
funkcional'nogo tipa. Operacii, dostupnye dlya funkcionalov, pomimo vyzova,
vklyuchayut prisvaivanie, inicializaciyu i sravnenie (tol'ko na
ravenstvo/neravenstvo). Vot primery:
op_add (6, 5); !! 11
int_op = op_add; !! teper' int_op -- eto op_add
int_op (5, 4); !! 9
int_op -- op_add; !! true
int_op = op_mul; !! teper' int_op -- eto op_mul
int_op (10, 5); !! vozvrashchaet 50
int_op <> op_add; !! true
int_op -- op_mul !! true
op_sub = int_op !! oshibka! (op_sub -- literal, a ne peremennaya)
Obratite vnimanie: pri ispol'zovanii funkcional'nogo tipa ne nuzhno
kakih-libo yavnyh operacij imenovaniya/razymenovaniya. Konechno, tehnicheski
funkcional'nyj tip realizovan kak ukazatel' na nekij blok koda, odnako
programmist ne obyazan zadumyvat'sya nad etim. Koe-chto, bezuslovno, rodnit
funkcional'nye tipy s ukazatelyami i ssylkami. Tak, k nim takzhe primenimo
znachenie nil (otsutstvie ssylki) i, podobno ukazatelyam, vse funkcional'nye
peremennye i massivy neyavno inicializiruyutsya im. Konechno, popytka "vyzvat'"
nil vyzyvaet isklyuchenie pri vypolnenii programmy (NilInvokeException). Kak i
v sluchae ukazatelej, dlya prisvaivaniya i sravneniya funkcional'nyh tipov
trebuetsya ih polnaya tipizacionnaya sovmestimost': dva funkcionala sovmestimy,
esli sovmestimy vozvrashchaemye imi znacheniya, kolichestvo i tipy ih parametrov.
Imeetsya i analog "prototipov funkcij" v yazykah C i C++. Term opisaniya
vida ‘#'<imya> -- eto predeklarirovanie (predopisanie) funkcii
<imya>. Ono zadaet spisok parametrov i tip vozvrashchaemogo znacheniya,
predpolagaya, chto realizaciya dannoj funkcii budet vypolnena pozdnee. Vot
primer predopisaniya:
float (float x, y) #power; !! predeklariruem funkciyu power
Hotya funkciya power eshche ne realizovana, ee uzhe mozhno ispol'zovat':
float result = power (x, 0.5) !! kvadratnyj koren' iz x
V konce koncov, predeklarirovannuyu funkciyu neobhodimo realizovat' (v
toj zhe oblasti dejstviya, gde byla ee predeklaraciya) s pomoshch'yu konstrukcii
vida ‘#'<imya><telo funkcii>. Naprimer:
#power { return exp (y * log (x)) }
Obratite vnimanie na to, chto pri realizacii ne nado povtorno zadavat'
spisok parametrov i vozvrashchaemyj tip -- kompilyatoru oni uzhe izvestny. Bolee
togo, popytka polnost'yu opisat' uzhe predeklarirovannuyu funkciyu power byla by
oshibkoj, t.k. vosprinimalas' by kompilyatorom kak popytka pereopredelit' ee!
Zdes' soblyuden odin iz principov yazyka: kazhdyj ob®ekt dolzhen byt' opisan
tol'ko odnazhdy, a dublirovanie opisanij ne nuzhno i ne dopuskaetsya. V sluchae
predeklarirovannoj funkcii, strogo govorya, my imeem delo ne s dvumya
opisaniyami, a s edinym, razbitym na dve chasti: deklarativnuyu i
realizacionnuyu. V dannom sluchae yavnoj neobhodimosti ispol'zovat'
predeklarirovanie net, poskol'ku mozhno bylo by napisat' srazu:
float (float x, y) power { return exp (y * log (x)) }
No bez predeklarirovaniya nevozmozhno obojtis', kogda opisyvaetsya
semejstvo vzaimno-rekursivnyh funkcij, kazhdaya iz kotoryh vyzyvaet (pryamo ili
kosvennym obrazom) vse drugie.
Sintaksis i semantiku vyzova funkcionalov sleduet rassmotret'
podrobnee. Obychno vyzov yavlyaetsya N-arnoj operaciej, imeyushchej pervym operandom
vyzyvaemoe znachenie funkcional'nogo tipa. Dalee sleduet spisok argumentov,
kazhdyj iz kotoryh zadaet znachenie dlya odnogo iz parametrov funkcionala.
Tradicionno sootvetstvie mezhdu nimi ustanavlivaetsya po pozicionnomu
principu, t.e. poryadok argumentov vyzova sootvetstvuet poryadku parametrov v
deklaracii funkcional'nogo tipa:
void (float x, y; bool p, q) z_func;
z_func (0.5, 1.5, true, true)
!! (t.e. x ← 0.5, y ← 1.5, p ← true, q ← true)
Odnako, dopustim takzhe i imennoj princip, kogda imya parametra dlya
tekushchego argumenta zadaetsya yavno s pomoshch'yu prefiksa vida <parametr>
‘:'. Naprimer, kak zdes':
z_func (p: false, q: true, x: 0.0, y: 1.0)
!! (x ← 0.0, y ← 1.0, p ← false, q ← true)
Oba vida specifikacii mozhno kombinirovat' v odnom vyzove. Zadanie
argumenta bez prefiksa oznachaet, chto on otnositsya k sleduyushchemu po poryadku
parametru (k samomu pervomu, esli predshestvuyushchih ne bylo). Nakonec, element
spiska argumentov mozhet byt' pustym, chto oznachaet propusk sootvetstvuyushchego
parametra (kotoryj mozhet byt' zapolnen pozzhe):
z_func (3.14, , false, false, y: 8.9)
!! (x ← 3.14, y ← 8.9, p ← false, q ← false)
Pri neostorozhnom sochetanii vseh etih priemov vpolne mozhet okazat'sya
tak, chto pri vyzove funkcii parametr ostavlen bez znacheniya, ili zhe
inicializirovan dva (ili bolee) raza. Vtoroe yavlyaetsya bezuslovnoj oshibkoj, a
vot pervoe mozhet schitat'sya dopustimym. Delo v tom, chto k parametram funkcii,
kak i k lyubym peremennym, mozhet byt' primenena inicializaciya po umolchaniyu.
Lyuboj yavno zadannyj argument "vytesnyaet" neyavnoe znachenie parametra.
Analogichnaya vozmozhnost' imeetsya i v C++, no tam inicializaciya po umolchaniyu
mozhet otnosit'sya lish' k poslednim argumentam v spiske, a inicializatorami
obyazany byt' literal'nye znacheniya. V Kserione oba etih ogranicheniya
otsutstvuyut. Bolee togo, odin neochevidnyj (no ves'ma poleznyj) aspekt
opisanij sostoit v tom, chto inicializator dlya parametra mozhet soderzhat'
drugie parametry, opisaniya kotoryh predshestvuyut emu. Primenenie etogo metoda
luchshe pokazat' na primere:
!! Zamet'te, chto zdes' tri opisaniya nel'zya ob®edinit'
void (int a = 5; int b = a; int c = a + b) x_func;
x_func (11, 12, 13); !! vse argumenty zadano yavno
!! (a ← 11, b ← 12, c ← 13)
x_func (10, 20); !! a i b zadany, c po umolchaniyu
!! (a ← 10, b ← 20, c ← 30)
x_func (10); !! a zadano, b i c po umolchaniyu
!! (a ← 10, b ← 10, c ← 20)
x_func (); !! vse po umolchaniyu
!! (a ← 5, b ← 5, c ← 10)
Dazhe v kachestve razmerov parametrov-massivov mogut ispol'zovat'sya
vyrazheniya, soderzhashchie ranee deklarirovannye parametry. |to tozhe mozhet
okazat'sya poleznym:
!! matrichnoe proizvedenie: C = A (*) B
void (u_int L, M, N; double [L][M] @A, [M][N] @B, [L][N] @C) MatrixProduct {
! ... ! }
Semantika peredachi argumentov -- eto vsegda semantika inicializacii,
t.e. dopustimy ne tol'ko prostye vyrazheniya, no i lyubye inicializatory,
podhodyashchie po tipu. To zhe otnositsya k znacheniyu, vozvrashchaemomu instrukciej
return. Zametim, chto parametry-massivy (v otlichie ot C, C++ i Java) takzhe
peredayutsya (i vozvrashchayutsya) po znacheniyu, chto mozhet byt' ves'ma dorogim
udovol'stviem. Kak pravilo, massivy luchshe peredavat' cherez ukazatel' ili
ssylku, a peredachu po znacheniyu ispol'zovat' lish' v teh sluchayah, kogda eto
dejstvitel'no opravdano. Pomimo svoih parametrov, funkcii dostupna vsya
vneshnyaya sreda -- t.e. vse peremennye i konstanty (nezavisimo ot rezhima ih
razmeshcheniya) i prochie vidy opisanij, dostupnye v tochke, gde dano opisanie
funkcii.
V yazyke ne sushchestvuet peregruzhennyh (overloaded) funkcij, podobnyh
imeyushchimsya v C++. Imya kazhdoj funkcii v svoej oblasti dejstviya dolzhno byt'
unikal'no (kak i dlya lyubogo drugogo sub®ekta opisaniya).
V zaklyuchenie otmetim, chto funkcional'nyj tip dopuskaet otdel'nuyu formu
inicializatora, pryamo zadayushchego telo bezymyannoj funkcii. (Nekotorye yazyki
programmirovaniya nazyvayut podobnoe "lyambda-notaciej"). Neyavnyj inicializator
imeet vid ‘#' <telo funkcii>. Imena i tipy parametrov i vozvrashchaemogo
znacheniya yavno ne zadayutsya, a opredelyayutsya avtomaticheski, ishodya iz konteksta
inicializacii. Naprimer:
int (float a, b, c) t_func = #{ return :int (a * b * c) };
t_func (2, 3, 4) !! 24 (int)
Dopolnitel'nye raznovidnosti opisanij
CHtoby zavershit' razgovor ob opisaniyah, my rassmotrim nekotorye
special'nye deklarativnye konstrukcii. Vse oni imeyut skoree vspomogatel'noe,
chem principial'noe znachenie, no vse-taki oni polezny pri sozdanii real'nyh
programm.
Prezhde vsego, v Kserione imeetsya svoj analog opisaniya typedef v C,
pozvolyayushchij vvodit' novye tipy. Odnako, eto ne samostoyatel'naya konstrukciya,
a lish' eshche odin vid terma opisaniya (type <imya tipa>), kotoryj, kak
vsegda, mozhet sovmeshchat'sya s drugimi termami. Naprimer:
!! flt -- sinonim float,
!! pflt -- ukazatel' na float
!! ppflt -- ukazatel' na ukazatel' na float
float type flt, ^ type pflt, ^^ type ppflt
Klyuchevoe slovo type slishkom gromozdko, poetomu ego mozhno sokratit' do
simvola ‘%' (chto obychno na praktike i delaetsya). Dlya togo, chtoby
ispol'zovat' novoopredelennyj tip v kachestve kornya opisaniya, on tozhe dolzhen
predvaryat'sya slovom type (ili simvolom ‘%'):
%flt x, y, z; !! t.e. float x, y, z
%pflt p1, p2; !! t.e. float ^ {p1, p2}
%ppft pp1, pp2, pp3 !! t.e. float ^^ {pp1, pp2, pp3}
S tochki zreniya semantiki podobnaya zapis' -- ne bolee, chem sredstvo
sokratit' dlinnye opisaniya. V otlichie ot ob®ektnyh tipov, nikakimi
principial'no novymi svojstvami tip, vvedennyj cherez opisanie type, obladat'
ne budet.
Privedennye vyshe opisaniya -- eto chastnyj sluchaj bolee obshchego podhoda,
pozvolyayushchego ispol'zovat' v kachestve kornya opisaniya ne tol'ko opredelennyj
programmistom tip, no i proizvol'noe vyrazhenie, imeyushchee smysl. Vot neskol'ko
trivial'nyh primerov:
%(2 * 2) xx, yy, zz; !! t.e. u_int xx, yy, zz
%(10 < 20) pp, qq; !! t.e. bool pp, qq
%("text" []) cc !! t.e. char cc
Vyrazhenie v korne opisaniya (esli eto ne prosto identifikator, ono
dolzhno byt' zaklyucheno v skobki) vychislyaetsya, no ego znachenie ignoriruetsya, i
v kachestve bazy opisaniya ispol'zuetsya tol'ko ego tip. Nakonec, otmetim, chto
imena opredelennyh pol'zovatelem (no ne vstroennyh!) tipov -- eto takzhe
zakonnye (no neopredelennye) vyrazheniya. Vse eto otkryvaet vozmozhnosti dlya
mnogih poleznyh tryukov. Tak, ispol'zovanie imen proizvodnyh tipov v
vyrazheniyah (i vyrazhenij -- v kornyah opisanij) daet prostoj mehanizm
tipizacionnoj dekompozicii, t.e. perehoda ot proizvodnyh tipov k ih bazovym.
Vot primer togo, kak eto mozhno ispol'zovat' na praktike:
!! esli v_type -- vektornyj tip:
%(v_type []) %v_type_elem; !! v_type_elem -- eto tip elementov v_type
!! esli p_type -- ukazatel'nyj tip:
%(p_type ^) %p_type_ref; !! p_type_ref -- eto tip,
!!
poluchaemyj razymenovaniem p_type
!! esli f_type -- funkcional'nyj tip:
%(f_type ()) %f_type_result !! f_type_result -- eto tip znacheniya,
!!
vozvrashchaemogo f_type pri vyzove
Sushchestvuet eshche odna vazhnaya forma opisanij -- eto makroopredeleniya
(let-opredeleniya). V osnovnom, oni primenimy dlya teh zhe celej, chto i
opredeleniya #define v C/C++, t.e. kak makropodstanovki povtoryayushchihsya
fragmentov ishodnogo koda programmy. No ne menee vazhny i razlichiya. Esli
sredstva C-preprocessora -- eto nadstrojka nad yazykom, to let-opredeleniya --
eto chast' yazyka Kserion, a ob®ektom let-podstanovki mozhet byt' ne vsyakaya
stroka simvolov -- eto dolzhno byt' zakonnoe vyrazhenie yazyka. Obshchij sintaksis
makroopredeleniya imeet takoj vid:
let NAME1 ‘=' EXPR1 (‘,' NAME2 ‘=' EXPR2) ...
|to opredelenie delaet vse identifikatory NAME# sinonimami dlya
sootvetstvuyushchih vyrazhenij EXPR#. Kak i prochie vidy opredelenij,
makroopredeleniya lokal'ny dlya soderzhashchego ih bloka ili oblasti dejstviya.
Vazhno takzhe to, chto vyrazhenie EXPR dolzhno byt' korrektno ne tol'ko
sintaksicheski, no i semanticheski: v chastnosti, vse identifikatory,
upomyanutye v EXPR, dolzhny imet' smysl. V celom mehanizm makroopredelenij
obespechivaet ne tol'ko tekstual'nuyu, no i semanticheskuyu podstanovku: vse
imena budut imet' v tochke obrashcheniya k makro tot zhe smysl, kotoryj oni imeli
v tochke ego opredeleniya. Naprimer:
int value; !! celaya peremennaya
let v1 = value; !! v1 -- sinonim value
{ float value; !! pereopredelenie value v podbloke
value; !! (float value)
v1 !! (a eto -- int value)
}
Nakonec, esli EXPR yavlyaetsya L-vyrazheniem, to NAME -- takzhe L-vyrazhenie.
Mehanizm makroopredelenij yavlyaetsya dovol'no moshchnym sredstvom, ispol'zuemym
dlya samyh raznyh celej: ot opredeleniya simvolicheskih literalov (v otlichie ot
konstant-peremennyh, dlya nih ne trebuetsya dopolnitel'naya pamyat') do prostogo
sokrashcheniya slishkom dlinnyh identifikatorov peremennyh, funkcij, tipov i
klassov:
%err_no (%string FileName) #SystemOpenFile;
let SysOpen = SystemOpenFile !! sokrashchenie
V zavershenie rassmotrim opisanie conceal -- mehanizm "skrytiya" imen.
Esli identifikator, opredelennyj v nekoj vneshnej oblasti dejstviya (naprimer,
global'nyj) neobhodimo sdelat' nedostupnym v nekoj vnutrennej (i vseh
oblastyah, vlozhennyh v nee), etogo legko dobit'sya s pomoshch'yu special'nogo
opisatelya conceal:
conceal NAME (‘,' NAME1) ...
Opisatel' conceal delaet vse perechislennye v nem imena lokal'no
nedostupnymi (ot opisatelya do konca vnutrennej oblasti dejstviya, soderzhashchej
ego). V sushchnosti, opisanie conceal NAME rabotaet primerno kak let
NAME=<nothing>. V osnovnom, mehanizm conceal prednaznachen dlya raboty s
ob®ektami i ierarhiyami klassov (naprimer, skrytiya kakih-nibud' atributov
bazovogo klassa v proizvodnyh klassah), chto, konechno, ne oznachaet, chto ego
nel'zya ispol'zovat' dlya drugih celej.
Instrukcii i potok upravleniya
Sobstvenno programma sostoit v osnovnom iz operatorov ili instrukcij
yazyka (poslednij termin kazhetsya nam predpochtitel'nym, poetomu im my i budem
pol'zovat'sya). Prostejshie vidy instrukcij my uzhe rassmotreli. Tak, vse vidy
opisanij yavlyayutsya zakonnymi instrukciyami, dopustimymi v lyubom meste
programmy. Lyuboe vyrazhenie -- eto takzhe instrukciya (vozvrashchaemoe znachenie,
esli ono est', ignoriruetsya). V yazyke predusmotren takoj mehanizm
gruppirovki instrukcij, kak blok, t.e. posledovatel'nost' instrukcij,
razdelennyh tochkami s zapyatoj (‘;') i zaklyuchennaya v figurnye skobki ("{}").
Blok rassmatrivaetsya kak edinaya instrukciya i yavlyaetsya oblast'yu lokalizacii
dlya vseh soderzhashchihsya v nem opisanij. Zamet'te, chto v etom otnoshenii yazyk
sleduet tradiciyam Paskalya: toska s zapyatoj -- eto sintaksicheskij razdelitel'
instrukcij (no ni odna instrukciya ne zavershaetsya etim simvolom). Vo mnogih
sluchayah izbytochnaya tochka s zapyatoj ne schitaetsya oshibkoj, t.k. v yazyke
opredelena pustaya instrukciya, ne soderzhashchaya ni odnogo simvola (i, ochevidno,
ne vypolnyayushchaya nikakih dejstvij). Lyubaya instrukciya mozhet byt' pomechena
metkoj vida LABEL ‘:', chto pozvolyaet instrukciyam break, continue i goto na
nee ssylat'sya. Rassmotrim drugie vidy instrukcij.
Instrukciya utverzhdeniya (assert) imeet vid:
assert CND
Semantika ee prosta: vychislyaetsya CND (vyrazhenie tipa bool). Esli ono
istinno, nichego ne proishodit, v protivnom sluchae vozbuzhdaetsya
isklyuchitel'naya situaciya AssertException. |ta instrukciya nuzhna v osnovnom dlya
"otlova" logicheskih oshibok v processe otladki programmy.
Konechno zhe, imeetsya uslovnaya instrukciya (if/unless), imeyushchaya sleduyushchij
vid:
(if P_CND | unless N_CND) BLOCK
[else E_STMT]
Esli (dlya if-formy) vyrazhenie P_CND istinno ili (dlya unless-formy)
vyrazhenie N_CND lozhno, vypolnyaetsya blok BLOCK. V protivnom sluchae, esli
prisutstvuet neobyazatel'naya chast' else, budet vypolnena instrukciya E_STMT.
Zametim, chto telo uslovnoj instrukcii -- eto vsegda blok, ogranichennyj
figurnymi skobkami (chto snimaet problemu neodnoznachnosti "visyashchego else").
Odnako, kruglye skobki vokrug usloviya (kak v C) ne trebuyutsya (hotya, konechno,
nichemu i ne pomeshayut). V chasti else dopustima proizvol'naya instrukciya
(naprimer, drugoj if/unless). Ochevidno, chto formy if i unless polnost'yu
vzaimozamenyaemy, i kakuyu iz nih ispol'zovat' -- vopros konkretnogo sluchaya.
V otlichie ot bol'shinstva yazykov, v Kserione imeetsya tol'ko odna (zato
dovol'no moshchnaya) instrukciya cikla. Vot ee samyj obshchij sintaksis:
[for I_EXPR]
(while P_CND_PRE | until N_CND_PRE | loop)
[do R_EXPR]
BLOCK
[while P_CND_POST | until N_CND_POST]
Hotya ona vyglyadit dovol'no gromozdkoj, bol'shaya chast' ee komponent
neobyazatel'na. Neobyazatel'naya chast' for zadaet inicializator cikla --
vyrazhenie I_EXPR, kotoroe vsegda vychislyaetsya odin raz pered samym nachalom
raboty cikla. Dalee vsegda sleduet zagolovok cikla, zadayushchej ego
preduslovie, proveryaemoe pered kazhdoj iteraciej cikla. Esli (v forme while)
P_CND_PRE lozhno ili (v forme until) N_CND_PRE istinno, cikl zavershit svoyu
rabotu. Esli zhe zagolovok cikla svoditsya k loop, preduslovie otsutstvuet.
Telom cikla yavlyaetsya blok BLOCK, obychno vypolnyayushchij osnovnuyu rabotu.
Neobyazatel'naya chast' do zadaet postiteraciyu cikla: vyrazhenie R_STMT budet
vychislyat'sya na kazhdoj iteracii posle tela cikla. Nakonec, cikl mozhet imet' i
postuslovie: esli (v forme while) P_CND_POST lozhno ili (v forme until)
N_CND_POST istinno, cikl takzhe zavershitsya. Kakuyu iz dvuh form ispol'zovat'
dlya pred- i postusloviya -- eto, opyat'-taki, vopros predpochteniya. Preduslovie
i postuslovie mogut prisutstvovat' odnovremenno -- v etom sluchae, cikl
preryvaetsya, kogda perestaet soblyudat'sya hotya by odno iz nih. Nakonec
zametim, chto vmesto vyrazheniya I_EXPR mozhet byt' dano lyuboe opisanie, i pri
etom cikl stanovitsya oblast'yu lokalizacii dlya nego (t.e. kak by neyavno
zaklyuchaetsya v blok). |lementy for i do logicheski izbytochny -- oni nuzhny
tol'ko dlya togo, chtoby mozhno bylo radi naglyadnosti sobrat' v zagolovke vsyu
logiku upravleniya ciklom. Tak, esli nuzhen cikl s peremennoj i, menyayushchej
znachenie ot (vklyuchaya) 0 do (isklyuchaya) N; eto obychno zapisyvaetsya tak:
for u_int i = 0 while i < N do ++ i { !( telo cikla )! }
Neredko neobhodimo prervat' vypolnenie cikla gde-nibud' poseredine. Dlya
etogo udobno ispol'zovat' instrukciyu preryvaniya break:
break [LABEL]
Ona preryvaet vypolnenie soderzhashchego ee cikla, pomechennogo metkoj LABEL
(ravno kak i vseh vlozhennyh v nego ciklov, esli oni est'). Esli element
LABEL opushchen, preryvaetsya samyj vnutrennij iz ciklov, soderzhashchih instrukciyu
break. Instrukciya prodolzheniya continue:
continue [LABEL]
vyzovet preryvanie tekushchej iteracii cikla LABEL (ili, esli metka
opushchena, samogo vlozhennogo cikla) i perehod k ego sleduyushchej iteracii
(vklyuchaya vypolnenie postiteracii i proverku postusloviya, esli oni est').
V zavershenie upomyanem ob instrukcii perehoda goto:
goto [LABEL]
peredayushchej upravlenie instrukcii, pomechennoj metkoj LABEL. O vrednosti
podobnyh instrukcij klassiki strukturnogo programmirovaniya napisali stol'ko,
chto net smysla ih povtoryat'. Instrukciya goto v yazyke est', a ispol'zovat' li
ee v programme -- delo vashej sovesti i lichnyh predpochtenij.
Dlya zaversheniya raboty funkcii primenyaetsya uzhe znakomaya nam instrukciya
return:
return [EXPR]
Ona dopustima tol'ko v opredelenii funkcii i obespechivaet vyhod iz nee
s vozvratom znacheniya EXPR (podhodyashchego tipa). Vyrazhenie EXPR opuskaetsya,
esli tip funkcii -- void.
Nakonec, v yazyke imeetsya instrukciya with, tesno svyazannaya s ob®ektami i
potomu rassmotrennaya v sleduyushchem razdele.
Ob®ekty i klassy
Kserion -- eto ob®ektno-orientirovannyj yazyk. V nem prisutstvuet
koncepciya ob®ekta -- klyuchevogo mehanizma abstrakcii dannyh, obespechivayushchego
dlya nih inkapsulyaciyu, nasledovanie i polimorfizm.
Kazhdyj ob®ekt yazyka otnositsya k odnomu iz klassov, opredelyayushchih
specifichnye dlya nego svojstva i atributy. Samyj obshchij sintaksis opisaniya
klassa takov:
class CLASS_NAME [‘:' SUPERCLASS_NAME]
{
CLASS_DECLS
}
[instate INSTATE_LIST]
[destructor DESTRUCTOR_BODY]
Rassmotrim vse elementy opisaniya po poryadku. Prezhde vsego, kazhdyj klass
obyazan imet' unikal'noe v svoej oblasti dejstviya imya (CLASS_NAME). Klass
mozhet byt' libo kornevym, libo zhe proizvodnym ot uzhe opredelennogo
superklassa (klassa SUPERCLASS_NAME). Dalee sleduet zaklyuchennoe v figurnye
skobki telo opisaniya klassa, predstavlyayushchee soboj spisok CLASS_DECLS. Ego
elementami mogut byt' prakticheski vse vidy opisanij yazyka (vklyuchaya i
nekotorye drugie, rassmotrennye nizhe). V bol'shinstve sluchaev v opisanii
klassa prisutstvuyut peremennye, konstanty i funkcii.
Lyubaya peremennaya, opisanie kotoroj soderzhitsya v deklaracii klassa, po
umolchaniyu schitaetsya ego komponentoj. |to znachit, chto dlya kazhdogo ob®ekta
klassa sushchestvuet sobstvennaya kopiya etoj peremennoj. Esli zhe peremennaya
imeet yavno specificirovannyj rezhim razmeshcheniya static ili shared, ona
yavlyaetsya peremennoj klassa, t.e., v otlichie ot ego komponent, sushchestvuet v
edinstvennom ekzemplyare, vne zavisimosti ot togo, skol'ko ob®ektov dannogo
klassa bylo sozdano. Raznica mezhdu rezhimami static i shared sostoit v tom,
chto static-peremennye sushchestvuyut global'no (vremya ih sushchestvovaniya sovpadaet
so vremenem vypolneniya programmy), a dlya shared oblast' dejstviya, ravno kak
i vremya sushchestvovaniya, opredelyayutsya deklaraciej klassa.
V deklaracii klassa mogut prisutstvovat' vlozhennye bloki lichnyh
(private) i zashchishchennyh (protected) opisanij. Kak i v C++, imena vseh
ob®ektov, deklarirovannyh v private-bloke, dostupny tol'ko vnutri deklaracii
klassa, a v protected-bloke -- takzhe i vnutri deklaracij vseh ego
podklassov. Vse prochie deklaracii yavlyayutsya publichnymi, t.e. dostupnymi izvne
bez kakih-libo ogranichenij.
Sintaksicheski opisanie klassa igraet rol' kornya opisaniya. Zametim, chto
posle togo, kak klass deklarirovan, dlya ssylok na nego (kak i na vse prochie
proizvodnye tipy) ispol'zuetsya klyuchevoe slovo type ili ‘%' (a ne class).
V semantike ob®ektov unikal'nym (i ves'ma vazhnym) yavlyaetsya ponyatie
tekushchego ekzemplyara ob®ekta. Dlya kazhdogo klassa opredelen odin i tol'ko odin
tekushchij ekzemplyar. Ego mozhno rassmatrivat' kak neyavnuyu peremennuyu klassa s
tipom CLASS_NAME^ i rezhimom razmeshcheniya shared, inicializiruemuyu, kak i vse
ukazateli, znacheniem nil. V processe vypolneniya programmy tekushchij ekzemplyar
klassa mozhet vremenno menyat'sya. Obratit'sya k tekushchemu ekzemplyaru nekotorogo
klassa (skazhem, CLASS_NAME), mozhno ochen' prosto: po imeni etogo klassa. V
kontekste opisaniya lyubogo klassa vmesto ego imeni mozhno ispol'zovat'
klyuchevoj slovo this:
CLASS_NAME; !! tekushchij ekzemplyar klassa CLASS_NAME
this !! tekushchij ekzemplyar tekushchego klassa
Rassmotrim teper' binarnuyu operaciyu dostupa k klassu ‘.' (tochka).
Pervym operandom etoj operacii vsegda yavlyaetsya ob®ekt nekotorogo klassa, a
vtoroj operand (proizvol'noe vyrazhenie) -- eto rezul'tat operacii (ot nego
vyrazhenie takzhe zaimstvuet L-kontekstnost' i konstantnost'). Kak i v C++ i
Paskale, ona mozhet ispol'zovat'sya, naprimer, dlya dostupa k otdel'nym
komponentam ob®ekta, no v Kserione ee semantika znachitel'no shire. Formal'no
ona imeet dva nezavisimyh aspekta: deklarativnyj i procedurnyj.
Deklarativnyj aspekt operacii sostoit v tom, chto ee vtoroj operand
vychislyaetsya v kontekste prostranstva imen dannogo klassa (t.e. v nem
dostupny imena komponent, peremennyh, funkcij i inye atributy klassa).
Procedurnyj aspekt -- v tom, chto ona (na vremya vychisleniya svoego vtorogo
operanda) delaet svoj pervyj operand-ob®ekt tekushchim ekzemplyarom dlya svoego
klassa. Oba perechislennyh aspekta sochetayutsya estestvennym obrazom, kak vidno
iz primerov:
!! trivial'nyj vektor iz treh komponent
class VECTOR { float x, y, z };
%VECTOR vec1, vec2; !! para ob®ektov klassa VECTOR
vec1.x; !! x-komponenta vec1
vec2.(x + y + z); !! summa komponent vec2
vec1.(x*x + y*y + z*z) !! norma vektora vec1
Esli zhe pervyj operand -- eto ssylka na tekushchij ob®ekt (inymi slovami,
imya klassa), to deklarativnaya semantika ostaetsya neizmennoj, no procedurnaya
vyrozhdaetsya v pustuyu operaciyu (t.k. tekushchij ob®ekt uzhe yavlyaetsya takovym).
Takim obrazom, operaciya dostupa k klassu stanovitsya prakticheski tochnym
analogom operacii ‘::' (kvalifikacii) iz C++:
VECTOR.x !! x-komponenta tekushchego ekzemplyara VECTOR
this.x !! to zhe samoe v kontekste klassa VECTOR
V sisteme instrukcij yazyka imeetsya svoj analog operacii dostupa k
klassu -- instrukciya prisoedineniya with:
with OBJ_EXPR BLOCK
Ee semantika prakticheski ta zhe: vypolnit' blok instrukcij BLOCK v
kontekste klassa, opredelennogo OBJ_EXPR (deklarativnaya), i s OBJ_EXPR v
kachestve tekushchego ekzemplyara etogo klassa (procedurnaya). K primeru:
with vec1 { x = y = z = 0f }; !! obnulit' komponenty vec1
with VECTOR { x = y = z = 0f } !! to zhe s tekushchim ekzemplyarom VECTOR
V yazyke ne sushchestvuet special'nogo ponyatiya metoda klassa -- v osnovnom
potomu, chto oni i ne trebuyutsya. Metody klassov v C++ i Java harakterizuyutsya
tem, chto vmeste s drugimi argumentami oni neyavno poluchayut ukazatel' na
tekushchij ob®ekt klassa, s kotorym dolzhny rabotat'. Odnako, v Kserione ponyatie
tekushchego ob®ekta yavlyaetsya global'nym i ravno primenimym ko vsem funkciyam.
Funkcii, deklarirovannye vnutri klassa, otlichayutsya ot drugih tol'ko tem, chto
imeyut neposredstvennyj dostup ko vsem atributam klassa (vklyuchaya ego lichnuyu i
zashchishchennuyu chast'). Esli zhe poslednee ne trebuetsya, funkcii, rabotayushchie s
ob®ektami opredelennogo klassa, mogut byt' deklarirovany i za ego predelami.
Privedem primer dlya opisannogo nami klassa VECTOR:
!! Umnozhenie vektora na skalyar `a`
void (float a) scale_VECTOR
{ with VECTOR { x *= a; y *= a; z *= a } }
Opisannyj nami "psevdo-metod" scale_VECTOR ispol'zovat' na praktike tak
zhe prosto, kak i funkcii, deklarirovannye vmeste s samim klassom:
vec2.Scale_VECTOR (1.5) !! Umnozhit' vec2 na 1.5
with vec2 { Scale_VECTOR (1.5) } !! to zhe, chto i vyshe
Scale_VECTOR (2f) !! Umnozhit' tekushchij ekzemplyar VECTOR na 2
Pomimo etogo, dlya kazhdogo klassa avtomaticheski opredelyayutsya operacii
prisvaivaniya, inicializacii i sravneniya (na ravenstvo i neravenstvo).
Prisvaivanie ob®ektov sostoit v posledovatel'nom prisvaivanii vseh ih
komponent. Analogichnym obrazom opredelyaetsya ekzemplyarnaya inicializaciya:
ob®ekt vsegda mozhet byt' inicializirovan prisvaivaniem emu drugogo ob®ekta
togo zhe klassa. Operaciya sravneniya takzhe opredelena kak pokomponentnaya: esli
vse sootvetstvuyushchie komponenty ravny, dva ob®ekta schitayutsya ravnymi; v
protivnom sluchae oni razlichny. |ti operacii nad ob®ektami vsegda dostupny; v
otlichie ot C++ ih nevozmozhno pereopredelit' ili zhe "razopredelit'".
Konechno zhe, pomimo ekzemplyarnoj inicializacii predusmotreny i drugie
zakonnye sposoby inicializirovat' ob®ekt klassa. Dlya klassov vsegda
opredelena spiskovaya inicializaciya, a mozhet byt' dostupen i vyzov
konstruktora. Rassmotrim eti vozmozhnosti po poryadku.
Samyj trivial'nyj sposob inicializacii sozdavaemogo ob®ekta -- eto
inicializaciya ego spiskom komponent. V principe, etot sposob analogichen
spiskovoj inicializacii klassov i struktur v C i C++, no on dopuskaet bol'she
vozmozhnostej.
Obshchij sintaksis spiskovogo inicializatora ob®ekta imeet primerno takoj
vid:
‘#' ‘(' <COMP_LIST> ‘)'
gde COMP_LIST -- eto spisok inicializatorov dlya komponent ob®ekta. Ego
sintaksis my podrobno rassmatrivat' ne budem, poskol'ku on polnost'yu
identichen spisku argumentov funkcij. Edinstvennoe razlichie: spisok zdes'
primenyaetsya ne k parametram funkcionala, a k komponentam ob®ekta. V spiske
dopustimy i pozicionnye inicializatory, i imennye. Prakticheski nichem ne
otlichaetsya i semantika. Komponenty ob®ekta, kak i parametry funkcii, mogut
imet' inicializaciyu po umolchaniyu (v tom chisle, i s ispol'zovaniem ranee
opisannyh komponent), i yavnaya inicializaciya pereopredelyaet neyavnuyu. Nakonec,
zametim, chto pri inicializacii deklariruemoj peremennoj mozhet ispol'zovat'sya
sokrashchennaya forma: vmesto VAR = #( LIST ) mozhno napisat' prosto VAR ( LIST
). Privedem primery dlya klassa VECTOR:
%VECTOR null = #(0f, 0f, 0f); !! nulevoj vektor
%VECTOR null (0f, 0f, 0f) !! (to zhe, koroche)
%VECTOR null (x: 0f, y: 0f, z: 0f) !! (to zhe, ochen' razvernuto)
!! koordinatnye vektory-orty
%VECTOR PX (1f, 0f, 0f), PY (0f, 1f, 0f), PZ (0f, 0f, 1f)
%VECTOR NX (-1f, 0f, 0f), NY (0f, -1f, 0f), NZ (0f, 0f, -1f)
Dlya naibolee trivial'nyh klassov, podobnyh klassu VECTOR, spiskovaya
inicializaciya yavlyaetsya samym prostym i udobnym sposobom sozdaniya ob®ekta.
Odnako, chasto nuzhny i kla