Ocenite etot tekst:




     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 (tinyshortintlong), tak zhe, kak i vse plavayushchie (floatdoublequad)  i vse  simvol'nye  (charw_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  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  klassy-"chernye  yashchiki",   imeyushchie  netrivial'nuyu
vnutrennyuyu strukturu,  celostnost' kotoroj dolzhna  vsegda  byt'  obespechena.
Spiskovaya  inicializaciya  dlya nih neudobna i nenadezhna, i luchshe ispol'zovat'
special'nye funkcii -- konstruktory.

     Vse  konstruktory   deklariruyutsya   vnutri   sootvetstvuyushchego   klassa.
Sintaksis   opisaniya  takoj  zhe,   kak  i  u   funkcij,  tol'ko  v  kachestve
vozvrashchaemogo tipa ispol'zuetsya fiktivnyj tip  constructor  (na samom  dele,
konstruktory  ne vozvrashchayut znacheniya  voobshche). V otlichie ot C++ i  Java, vse
konstruktory  v  Kserione --  imenovannye:  klass mozhet  imet'  proizvol'noe
kolichestvo  konstruktorov, no ih imena dolzhny razlichat'sya (i  ni odno iz nih
ne sovpadaet s imenem klassa).  Tak, k  opisaniyu  klassa VECTOR  my mogli by
dobavit' konstruktor:

     !! inicializaciya vektora polyarnymi koordinatami
!! (len -- modul', phi -- dolgota, theta -- shirota)
sonstructor (float len, phi, theta) polar
{ x = len * sin(phi) * cos(theta), y  = len * cos(phi) * cos(theta), z = len
* sin(theta) }

     Tot zhe konstruktor mozhet byt' bolee kompaktno zapisan tak:

     sonstructor (float len, phi, theta) polar :
(len *  sin(phi) * cos(theta), len * cos(phi) * cos(theta), len * sin(theta)
) {}

     Konstrukciya  v kruglyh skobkah posle dvoetochiya -- eto tot  zhe spiskovyj
inicializator dlya  ob®ekta,  elementy kotorogo mogut obrashchat'sya k parametram
konstruktora.   V   dannom   sluchae  mozhno   vybrat',  kakuyu  imenno   formu
ispol'zovat',  no  esli  kakie-to  komponenty klassa  trebuyut  netrivial'noj
inicializacii   (naprimer,    sami   yavlyayutsya    ob®ektami),    ispol'zovat'
spisok-inicializator v konstruktore  -- eto  edinstvennyj korrektnyj  sposob
zadat'  im  nachal'noe  znachenie. Nezavisimo ot togo,  kak konstruktor  polar
opredelen, ispol'zovat' ego mozhno tak:

     %VECTOR anyvec = :polar (200f, PI/4f, PI/6f)

     Obratite  vnimanie na  dvoetochie pered vyzovom konstruktora:  ono  yavno
ukazyvaet  na to, chto pri inicializacii  budet  ispol'zovan konstruktor  dlya
etogo klassa.

     Kak i v C++, v Kserione sushchestvuyut vremennye ob®ekty.  Vremennyj ob®ekt
sozdaetsya libo  ukazaniem spiska komponent,  libo  obrashcheniem k konstruktoru
(obychno kvalificirovannomu s pomoshch'yu operacii ‘.'). Naprimer:

     VECTOR (0.5, 0.3, -0.7) !! vremennyj vektor
VECTOR.polar (10.0, 2f*PI, PI/2f) !! drugoj variant

     Sushchestvovanie   vremennyh  ob®ektov   obychno   dlitsya  ne  dol'she,  chem
vypolnyaetsya instrukciya, v kotoroj oni byli sozdany.

     Ne tol'ko inicializaciya, no i deinicializaciya ob®ekta mozhet potrebovat'
netrivial'nyh  dejstvij, poetomu dlya klassa mozhet byt' zadan destruktor. |to
--   prosto  blok  koda,   opredelyayushchij  dejstviya,  neyavno  vypolnyaemye  pri
zavershenii  sushchestvovaniya  lyubogo ob®ekta klassa. U  klassa  ne byvaet bolee
odnogo destruktora. Dazhe esli  destruktor  ne  zadan yavno, kompilyator  chasto
sozdaet neyavnyj destruktor v teh sluchayah,  kogda  eto neobhodimo.  Dejstviya,
opisannye v  yavnom  destruktore,  vsegda  vypolnyayutsya  do  vyzova  neyavnogo.
Sobstvenno, yavnye destruktory nuzhny redko: v osnovnom oni  trebuyutsya  lish' v
teh  sluchayah,  kogda  ob®ekt  zadejstvuet  kakie-to vneshnie po  otnosheniyu  k
programme  resursy  (skazhem,   otkryvaet  fajly  ili  ustanavlivaet  setevye
soedineniya), a takzhe dlya otladochnyh celej i statistiki.

     Ochen'  kratko rassmotrim aspekty  yazyka, svyazannye s nasledovaniem. Kak
uzhe govorilos', klass mozhet imet' superklass, i v  etom  sluchae on nasleduet
vse  atributy  superklassa,  v  dopolnenie  k tem,  kotorye opredelyaet  sam.
Oblast' vidimosti klassa vlozhena  v oblast'  vidimosti  superklassa, poetomu
lyubye  atributy superklassa mogut  byt' pereopredeleny v proizvodnom klasse.
Podklassu  dostupny  vse  publichnye  i vse  zashchishchennye  (no  ne  privatnye!)
deklaracii superklassa.  Mehanizmy  let  i  conceal  dayut gibkie vozmozhnosti
upravleniya vidimost'yu atributov superklassa, pozvolyaya skryvat' ih ili davat'
im al'ternativnye imena.

     Lyubaya   funkciya,  deklarirovannaya  v  nekotorom  klasse,  mozhet   imet'
specifikator  virtual.  On oznachaet, chto dannaya funkciya yavlyaetsya virtual'noj
funkciej dannogo klassa, t.e. mozhet imet' al'ternativnuyu realizaciyu  v lyubom
iz ego podklassov. Mehanizm virtualizacii  vyzovov funkcij obespechivaet t.n.
dinamicheskoe  svyazyvanie:  v otlichie ot obychnogo svyazyvaniya, osnovannogo  na
informacii  o  tipah  vremeni  kompilyacii, dlya  virtual'noj  funkcii  vsegda
vyzyvaetsya imenno  ta versiya,  kotoraya  neobhodima,  ishodya  iz dinamicheskoj
informacii o real'nom tipe ob®ekta dannogo  klassa, dostupnoj pri vypolnenii
programmy. Pereopredelit' virtual'nuyu funkciyu ochen' prosto. Dlya etogo ee imya
dolzhno byt'  vklyucheno v  spisok  pereopredeleniya instate, obychno zavershayushchij
deklaraciyu podklassa. Parametry  i tip funkcii  povtorno zadavat' ne  nuzhno:
oni  zhestko opredelyayutsya virtual-deklaraciej  superklassa. Neredko v  spiske
instate daetsya  i realizaciya novoj  versii  virtual'noj funkcii; v protivnom
sluchae realizaciya dolzhna byt' dana pozdnee.

     Esli virtual'naya funkciya ne pereopredelena v podklasse,  nasleduetsya ee
versiya iz superklassa. Fakticheski, imya virtual'noj funkcii -- eto interfejs,
za kotorym skryvaetsya  mnozhestvo  razlichnyh funkcij. Nakonec,  kak  i v C++,
podklass  mozhet yavno vyzvat'  versiyu iz kakogo-nibud'  superklassa s pomoshch'yu
polnost'yu kvalificirovannogo imeni.

     Nakonec,  govorya  o  nasledovanii   klassov,  nel'zya  ne  upomyanut'  ob
abstraktnyh klassah (ili prosto abstraktah). Abstraktnyj klass -- eto klass,
dlya  kotorogo  ne  sushchestvuet  ni  odnogo  ob®ekta  (i,  sootvetstvenno,  ne
opredelen  tekushchij  ekzemplyar)  i  kotoryj  mozhet  ispol'zovat'sya  tol'ko  v
kachestve proizvoditelya  klassov-potomkov. Pri  opisanii  abstraktnogo klassa
ispol'zuetsya klyuchevoe  slovo abstract vmesto class.  Abstraktnye superklassy
prednaznacheny dlya realizacii bazovyh koncepcij, kotorye lezhat v osnove nekoj
gruppy  rodstvennyh  ob®ektov, no  sami ne mogut imet'  nikakogo  "real'nogo
voploshcheniya".

     Kak obychno, my prodemonstriruem  nasledovanie, polimorfizm  i abstrakty
na  bolee-menee realistichnom primere  (rabota  s prostejshimi geometricheskimi
ob®ektami).

     !! Geometricheskaya figura (abstraktnyj klass)
abstract Figure {
     !! figura obychno imeet...

     !! -- nekij perimetr:
     float () virtual perimeter;
     !! -- nekuyu ploshchad':
     float () virtual area;
     };

     !! Tochka
class Point : Figure {
} instate #perimeter { return 0f }, #area { return 0f };

     !Otrezok (dliny L)
class Line : Figure {
float L !! dlina
} instate #perimeter { return L }, #area { return 0f };

     !! Kvadrat (so storonoj S)
class Square : Figure {
float S !! storona
} instate #perimeter { return 4 * S }, #area { return S * S };

     !! Pryamougol'nik (so storonami A, B)
class Rectangle : Figure {
float A, B
} instate #perimeter { return 2 * (A + B) }, #area { return A * B };

     !! Krug (s radiusom R)
class Circle : Figure {
float R
} instate #perimeter { return 2 * PI * R }, #area { return PI * R * R };

     Pri  vsej primitivnosti opredelennoj  nami ierarhii ob®ektov, s nej uzhe
mozhno   delat'   chto-to   soderzhatel'noe.  K   primeru   sleduyushchij  fragment
podschityvaet summarnuyu ploshchad' figur v massive ssylok na figury fig_vec:

     %Figure @ []@ fig_vec; !! ssylka na vektor ssylok na figury
float total_area = 0f; !! summarnaya ploshchad'
for u_int i = 0 while i <> fig_vec# do ++ i
     { total_area += fig_vec [i].area () }

     Nakonec  my  otmetim, chto virtual'nye  funkcii -- eto  ne  edinstvennyj
polimorfnyj  mehanizm  v   yazyke.   Pri  neobhodimosti  mozhno   ispol'zovat'
special'nuyu operaciyu yavnogo privedeniya ukazatelya  na superklass  k ukazatelyu
na podklass. Binarnaya operaciya kvalifikacii:

     CLASS qual OBJ_PTR_EXPR

     predprinimaet  popytku preobrazovat'  OBJ_PTR_EXPR (ukazatel' na  nekij
ob®ekt)  k  ukazatelyu  na  klass  CLASS   (kotoryj  dolzhen  byt'  podklassom
OBJ_PTR_EXPR^). Operaciya vozvrashchaet vyrazhenie tipa CLASS^: esli  ob®ekt,  na
kotoryj ukazyvaet vtoroj operand,  dejstvitel'no yavlyaetsya ekzemplyarom klassa
CLASS,  vozvrashchaetsya ukazatel'  na  nego,  v  protivnom  sluchae vozvrashchaetsya
znachenie  nil.  Vot  pochemu vozvrashchaemoe  znachenie vsegda dolzhno proveryat'sya
prezhde, chem s nim predprinimayutsya dal'nejshie vychisleniya.

     %Figure ^fig_ptr; !! ukazyvaet na figuru
%Rectangle some_rect (10f, 20f); !! pryamougol'nik 10 * 20
%Circle some_circ (50f); !! okruzhnost' radiusa 50

fig_ptr = some_rect@; !! fig_ptr ukazyvaet na pryamougol'nik
Rectangle qual fig_ptr; !! vernet ukazatel' na some_rect
Circle qual fig_ptr; !! vernet nil

fig_ptr = some_circ@; !! fig_ptr ukazyvaet na okruzhnost'
Rectangle qual fig_ptr; !! vernet nil
Circle qual fig_ptr; !! vernet ukazatel' na some_circ

     Kvalifikaciya s  pomoshch'yu qual  ochen'  pohozha  na dinamicheskoe privedenie
tipov dynamic_cast v poslednih versiyah yazyka C++.

     Opredelenie operacij

     Kak  i  v C++,  v Kserione  predusmotreny  sredstva dlya pereopredeleniya
operacij. Srazu  zhe zametim,  chto na  samom  dele  korrektnee govorit' ob ih
doopredelenii:  ne  sushchestvuet sposoba pereopredelit' operaciyu, uzhe  imeyushchuyu
smysl (naprimer, opredelit'  operaciyu  ‘-‘ tak, chtoby  ona  skladyvala celye
chisla). Odnako, esli operaciya  ne opredelena dlya  nekotoroj kombinacii tipov
operandov, to  v  etom sluchae ej mozhet  byt' pripisana nekotoraya  semantika.
Operacii   --  prakticheski  edinstvennyj  mehanizm   yazyka,   gde  dopustima
peregruzka v zavisimosti ot tipov operandov, i yazyk pozvolyaet rasprostranit'
etot   princip   i   na  proizvodnye   tipy.   (Sintaksis,   prioritet   ili
associativnost' operacii pereopredelyat', konechno, nel'zya.)

     Novaya  semantika operacii  zadaetsya  s pomoshch'yu  special'nogo  opisatelya
opdef:

     opdef OP_DEF1 ‘=' EXPR1 (‘,' OP_DEF2 ‘=' EXPR2) ...

     Kak  i  vse  prochie  opisaniya,  opredeleniya  operacij  imeyut  lokal'nyj
harakter. Kazhdyj  element OPDEF -- eto  konstrukciya,  imitiruyushchaya  sintaksis
sootvetstvuyushchej operacii, no vmesto operandov-vyrazhenij  v nej zadayutsya tipy
dannyh. (Garantirovanno mogut ispol'zovat'sya lyubye primitivnye tipy i  imena
klassov,  no vozmozhno,  v budushchem mozhno budet ispol'zovat' lyubye proizvodnye
tipy).

     Sootvetstvuyushchee  vyrazhenie EXPR  budet podstavlyat'sya vmesto  kombinacii
OPDEF. Pri etom  v  EXPR dopustimo  ispol'zovanie  special'nyh  termov  vida
(<1>),  (<2>)..., sootvetstvuyushchih  pervomu  operandu,  vtoromu i
t.p. Primer:

     opdef VECTOR + VECTOR = VECTOR.add (<1>, <2>)

     Zdes' opredelyaetsya  novaya  semantika  operacii  ‘+' dlya  dvuh  ob®ektov
klassa VECTOR.  Vmesto  etoj  operacii  budet podstavlen  vyzov  funkcii add
(predpolozhitel'no  opredelennoj  v  klasse  VECTOR)  s  oboimi  operandami v
kachestve argumentov.

     Fakticheski opredelenie  operacii -- eto raznovidnost' makroopredeleniya,
i    v   semantike   makropodstanovki   imeetsya   ochen'   mnogo   obshchego   s
let-opredeleniyami.   Tak,   podstanovka   yavlyaetsya   semanticheskoj,   a   ne
tekstual'noj. No opredelennaya operaciya -- eto ne vyzov funkcii: i dlya samogo
opredeleniya i  dlya vseh ego operandov dejstvuet semantika  podstanovki, a ne
vyzova. Gromozdkoe opredelenie vyzovet generaciyu bol'shogo kolichestva lishnego
koda,  a esli  v  tele  opdef-opredeleniya  ssylka  na  parametr  vstrechaetsya
mnogokratno, sootvetstvuyushchij ej operand takzhe budet podstavlen neskol'ko raz
(chto, voobshche-to, ves'ma nezhelatel'no).

     Nakonec,  otmetim,  chto  dlya  togo,  chtoby  opredelenie  operacii  bylo
zadejstvovano,  trebuetsya tochnoe sootvetstvie real'nyh tipov operandov tipam
v opdef-deklaracii. Privedeniya tipov ne dopuskayutsya.  (V dal'nejshem, pravda,
eto ogranichenie mozhet byt' oslableno.)

     Privedem  soderzhatel'nyj  primer  opredeleniya  operacij.  Pust'  u  nas
imeetsya klass  String, realizuyushchij simvol'nye stroki, grubaya model' kotorogo
dana nizhe:

     class String {

!! (opredeleniya...)

!! dlina tekushchej stroki
u_int () #length;
!! konkatenaciya (sceplenie) strok head & tail
%String (%String head, tail) #concat;
!! replikaciya (povtorenie n raz) stroki str
%String (%String str; u_int n) #repl;
!! podstroka stroki str (ot from do to)
%String (%String str; u_int from, to) #substr;

!! ...
}

     Teper'  opredelim nabor  operacij,  pozvolyayushchih  rabotat'  so  strokami
proshche.

     !! dlya kompaktnosti ...
let Str = String;
!! ‘#' kak dlina stroki:
opdef Str# = (<1>).len ();
      !! ‘+' kak konkatenaciya:
opdef Str + Str = Str.concat ((<1>), (<2>));
!! ‘*' kak replikaciya:
opdef Str * u_int = Str.repl ((<1>), (<2>));
opdef u_int * Str = Str.repl ((<2>), (<1>));
!! otrezok kak podstroka
opdef Str [u_int..u_int] = Str.substr (<1>, <2>, <3>);

     Opredelennye tak operacii dovol'no udobno ispol'zovat':

     Str("ABBA")#; !! 4
Str("Hello, ") + Str("world!"); !! Str("Hello, world!")
Str("A") * 5; !! Str("AAAAA")
3 * Str("Ha ") + Str("!"); !! Str("Ha Ha Ha !")
Str("Main program entry") [5..12]; !! Str("program")

     Kak  uzhe   govorilos',  imeyushchiesya  v  yazyke  operacii  vvoda  i  vyvoda
prednaznacheny isklyuchitel'no dlya pereopredeleniya. Dlya bol'shinstva primitivnyh
tipov (i dlya mnogih  ob®ektnyh) eti operacii  pereopredeleny  v  standartnyh
bibliotekah  vvoda-vyvoda,  chto  delaet ih ispol'zovanie ochen'  prostym.  Ih
razumnoe opredelenie dlya pol'zovatel'skih klassov -- rekomenduemaya praktika.
Tak,  dlya  upomyanutogo klassa  VECTOR  my  mozhem  opredelit' operaciyu vyvoda
(OFile -- klass vyhodnyh potokov):

     opdef OFile <: VECTOR =
(<1>)  <:  ‘(‘  <:  (<2>).x <: ‘,'  <: (<2>).y
<: ‘,' <: (<2>).z <: ‘)'

     Zametim, chto poskol'ku operaciya  vyvoda levo- associativna i vozvrashchaet
v  kachestve  znacheniya svoj  levyj operand (potok  vyvoda), opredelennaya nami
operaciya takzhe budet obladat'  etim svojstvom,  chto ochen' horosho. No u etogo
opredeleniya  est' i nedostatok:  pravyj operand  vychislyaetsya  tri  raza, chto
neeffektivno  i  chrevato  pobochnymi  effektami. V  dannom sluchae  eto  legko
popravit':

     opdef OFile <: VECTOR =
(<2>).((<1>) <: ‘(‘ <: x <: ‘,' <: y <: ‘,' <:
z <: ‘)')

     No,  voobshche-to  govorya,  esli  opredelennaya tak operaciya  vyvoda  budet
ispol'zovat'sya   intensivno,   eto   privedet   k   zametnomu    pereizbytku
sgenerirovannogo koda.  Luchshim resheniem  budet opredelit' funkciyu dlya vyvoda
ob®ektov VECTOR, a potom, uzhe cherez nee, operaciyu.

     Import i eksport.
Pragmaty.

     V  zavershenie  nashego  obzora   rassmotrim  mehanizmy,   obespechivayushchie
vzaimodejstvie  mezhdu Kserion-programmoj i  vneshnej sredoj. Ponyatno,  chto ni
odna  real'naya programma  ne mozhet  obojtis' bez  nih: naprimer, standartnye
sredstva  vvoda-vyvoda   i  vzaimodejstviya  s  OS,  matematicheskie  funkcii,
sredstva obrabotki isklyuchenij -- vse eto nahoditsya v standartnyh bibliotekah
yazyka.

     Programma sostoit iz logicheski nezavisimyh, no vzaimodejstvuyushchih  mezhdu
soboj   strukturnyh  edinic,  nazyvaemyh   modulyami.   Obychno   odin  modul'
sootvetstvuet odnomu fajlu ishodnogo koda programmy. Kazhdyj iz modulej mozhet
vzaimodejstvovat' s drugimi s  pomoshch'yu mehanizmov eksporta (pozvolyayushchego emu
predostavlyat'  svoi  resursy  drugim  modulyam) i importa  (pozvolyayushchego  emu
ispol'zovat' resursy, predostavlennye drugimi modulyami).

     Lyubye vneshnie ob®ekty modulya (naprimer, global'nye peremennye, funkcii,
tipy dannyh  i  klassy) mogut  byt'  eksportirovany  vo vneshnyuyu  sredu.  |to
delaetsya za schet pomeshcheniya ih v blok deklaracii eksporta, imeyushchej vid:

     export { DECLARATION_LIST }

     V  module  mozhet byt'  mnogo  deklaracij eksporta,  no  tol'ko na samom
verhnem  (global'nom)  urovne  ierarhii  opisanij.  Vse   vneshnie   ob®ekty,
opredelennye  v spiske opisanij  DECLARATION_LIST, stanut  dostupnymi drugim
modulyam.  CHtoby  poluchit'  k  nim  dostup,   modul'  dolzhen  vospol'zovat'sya
deklaraciej importa, imeyushchej vid:

     import MODULE { STMT_LIST }

     V  otlichie  ot  deklaracii  eksporta,  deklaraciya  importa  mozhet  byt'
lokal'noj: ona mozhet vstretit'sya v lyubom bloke  ili, k primeru, v deklaracii
klassa. Zdes' MODULE -- eto tekstovaya  stroka, zadayushchaya imya modulya.  V bolee
obshchem  sluchae, eto imya importiruemogo resursa, kotoryj mozhet byt' global'nym
(obshchesistemnym)  ili  dazhe setevym (sintaksis MODULE zavisit ot realizacii i
zdes' ne rassmotren). STMT_LIST -- proizvol'nyj spisok instrukcij, v kotorom
budet dostupno vse, eksportirovannoe resursom MODULE. V  chastnosti, on mozhet
soderzhat' drugie  deklaracii import, chto pozvolyaet importirovat' opisaniya iz
neskol'kih modulej.

     Tochnaya semantika mehanizma importa/eksporta --  slishkom  slozhnaya  tema,
chtoby rassmatrivat' ee zdes' v detalyah. Esli kratko, to  peredache cherez etot
mehanizm  mogut  podvergat'sya deklaracii peremennyh i funkcij,  klassov, vse
opredelennye pol'zovatelem tipy, makroopredeleniya i  operacii.  Zametim, chto
kazhdyj modul' fakticheski sostoit  iz  vneshnej  (deklarativnoj)  i vnutrennej
(realizacionnoj)  chastej.  Dlya pravil'noj kompilyacii  vseh  importerov etogo
modulya trebuetsya  lish' znanie  pervoj iz nih; realizacionnaya chast' modulya (v
vide sgenerirovannogo koda) ostaetsya privatnoj.

     Nakonec,  sushchestvuet  special'noe  sluzhebnoe  sredstvo  dlya  upravleniya
processom kompilyacii -- pragmaty:

     pragma PRAGMA_STR

     Literal'naya stroka PRAGMA_STR  soderzhit  direktivy  kompilyatoru,  nabor
kotoryh takzhe  mozhet sil'no zaviset' ot  realizacii i  poka opredelen  ochen'
priblizitel'no.   Predpolagaetsya,   chto   pragmaty   budut   zadavat'  opcii
kompilyatora,  takie,  kak  rezhimy  kodogeneracii, obrabotki preduprezhdenij i
oshibok, vyvoda listinga i t.p.

     Perspektivy razvitiya i nerealizovannye vozmozhnosti yazyka

     Kserion   --  yazyk  poka  eshche   ochen'  molodoj   i  ves'ma  dalekij  ot
sovershenstva. V processe razrabotki  yazyka  u ego sozdatelej voznikali samye
raznye  idei otnositel'no  vozmozhnostej ego  dal'nejshego  razvitiya -- kak  v
kratkosrochnoj, tak  i v  "strategicheskoj" perspektive. Na nekotoryh  iz etih
idej stoit ostanovit'sya podrobnee.

     Tak,   prakticheski   neizbezhnym   predstavlyaetsya   vklyuchenie   v   yazyk
let-makroopredelenij  s  parametrami.  Funkcional'no  oni  budut  pohozhi  na
parametrizovannye #define C-preprocessora -- no, v otlichie ot poslednih, oni
budut, podobno opdef'am, imet'  strogo  tipizovannye parametry i analogichnuyu
semantiku podstanovki. Ne isklyucheno, chto  parametrizovannye makroopredeleniya
budut dazhe  dopuskat' peregruzku i vybor odnogo iz variantov na osnove tipov
argumentov.

     V  bolee  otdalennoj perspektive,  vozmozhno,  poyavitsya i  stol'  moshchnyj
makro-mehanizm,  kak  shablony (template) dlya deklaracij  klassov  i funkcij,
podobnye analogichnym  sredstvam v C++. Odnako, poka trudno uverenno skazat',
kakoj vid primet etot mehanizm v okonchatel'noj forme.

     Sejchas  v   yazyke  otsutstvuyut  kakie-libo  formy   instrukcii  vybora,
analogichnoj  switch/case  v C i C++,  no  ih  otsutstvie  ochen' chuvstvuetsya.
Skoree  vsego, kogda  analogichnyj mehanizm  budet vklyuchen  v  yazyk, on budet
sushchestvenno bolee moshchnym. V chastnosti, on budet dopuskat' nelinejnuyu  logiku
perebora i bolee slozhnye kriterii proverki "sluchaev".

     Bezuslovno,  bylo by ochen'  poleznym takzhe  vvedenie  v  yazyk mehanizma
perechislimyh tipov (enum), podobnogo imeyushchimsya i v Paskale, i v C.

     Na  povestke dnya  stoyat i bolee slozhnye voprosy. Dolzhno li  v  Kserione
byt' realizovano mnozhestvennoe nasledovanie, kak v C++? |tot vopros yavlyaetsya
odnim   iz   samyh   spornyh.   Vozmozhny  raznye  varianty:  polnyj   zapret
mnozhestvennogo   nasledovaniya  (chto   vryad  li   priemlimo),   mnozhestvennoe
nasledovanie tol'ko  ot  special'nyh abstraktnyh klassov-interfejsov  (takoj
podhod   prinyat    v    Java),   nasledovanie    tol'ko   ot   nerodstvennyh
klassov-roditelej, i, nakonec, nasledovanie bez kakih-libo ogranichenij.

     Est'  dostatochno mnogo  neyasnyh voprosov, svyazannyh s  aspektami zashchity
soderzhimogo  klassov.  V  nastoyashchej  redakcii  yazyka  prinyat  namnogo  bolee
liberal'nyj  podhod  k  etomu  voprosu,  chem  v  C++ i Java.  YAzyk dopuskaet
raznoobraznye   mehanizmy  inicializacii  ekzemplyara   klassa  (ekzemplyarom,
spiskom  komponent, konstruktorom i, nakonec, vsegda dostupna avtomaticheskaya
neyavnaya  inicializaciya). Kak pravilo,  ob®ekty vsegda inicializiruyutsya nekim
"razumnym" obrazom,  odnako mozhet  vozniknut'  potrebnost'  i v  klassah  --
"chernyh  yashchikah",  inicializaciya  kotoryh  proishodit   isklyuchitel'no  cherez
posredstvo  konstruktorov.  S  samoj  semantikoj  konstruktorov  takzhe  est'
nekotorye neyasnosti.

     Nakonec,  diskussionnym  yavlyaetsya vopros o  tom, kakie  sredstva dolzhny
byt'  vstroeny  v  yazyk, a kakie --  realizovany v standartnyh  bibliotekah.
Naprimer, obrabotka isklyuchenij (a  v budushchem, vozmozhno,  i  mnogopotochnost')
planirovalos'  realizovat' kak vneshnie  bibliotechnye  sredstva -- no  protiv
takogo podhoda takzhe est' ser'eznye vozrazheniya.

     Vprochem, chto by ne planirovali razrabotchiki -- okonchatel'nyj vybor, kak
my nadeemsya, budet prinadlezhat' samim pol'zovatelyam yazyka.

     Zaklyuchenie

     V  zaklyuchenie  privedem   nebol'shoj,   no  vpolne  realistichnyj  primer
zavershennogo   Kserion-modulya,   realizuyushchego   prostejshie   operacii    nad
kompleksnymi chislami.

     !!
!! Ishodnyj fajl: "complex.xrn"
!! Realizaciya klassa `complex`:
!! kompleksnye chisla (immutabel'nye)
!!

!! vneshnie funkcii (v real'noj programme importiruemye):
double (double x, y) #atan2; !! dvuhargumentnyj arktangens
double (double x, y) #hypot; !! gipotenuza
double (double x) #sqrt; !! kvadratnyj koren'

     class complex {
     !! komponenty klassa
     double Re, Im; !! (real, imag)

     !! [Unarnye operacii nad %complex]
     %complex (%complex op1) %opUnary;

     %opUnary #conj; !! Sopryazhenie
     %opUnary #neg; !! Otricanie
     %opUnary #sqrt; !! Kvadratnyj koren'

     !! [Binarnye operacii nad %complex]
     %complex (%complex op1, op2) %opBinary;

     %opBinary #add; !! Slozhenie
     %opBinary #sub; !! Vychitanie
     %opBinary #mul; !! Umnozhenie
     %opBinary #div; !! Delenie

     !! Proverka na nul'
     bool () is_zero { return Re -- 0f && Im -- 0f };

     !! [Sravneniya dlya %complex]
     bool (%complex op1, op2) %opCompare;

     !! (na ravenstvo):
     %opCompare eq { return op1.Re -- op2.Re && op1.Im -- op2.Im };
     !! (na neravenstvo):
     %opCompare ne { return op1.Re <> op2.Re || op1.Im <> op2.Im
};

     !! Modul'
     double (%complex op) mod { return hypot (op.Re, op.Im) };
     !! Argument
     double (%complex op) arg { return atan2 (op.Re, op.Im) };
     };

     !! Realizaciya predeklarirovannyh funkcij

!! Sopryazhennoe dlya op1
#complex.conj { return #(op1.Re, - op1.Im) };

!! Otricanie op1
#complex.neg { return #(- op1.Re, - op1.Im) };

!! Slozhenie op1 i op2
#complex.add { return #(op1.Re + op2.Re, op1.Im + op2.Im) };

!! Vychitanie op1 i op2
#complex.sub { return #(op1.Re - op2.Re, op1.Im - op2.Im) };

!! Proizvedenie op1 i op2
#complex.mul {
     return #(op1.Re * op2.Re - op1.Im * op2.Im,
     op1.Im * op2.Re + op1.Re * op2.Im)
     };

!! CHastnoe op1 i op2
#complex.div {
     !! (delitel' dolzhen byt' nenulevoj)
     assert ~op2.is_zero ();

     double denom = op2.Re * op2.Re + op2.Im * op2.Im;
     return # ((op1.Re * op2.Re + op1.Im * op2.Im) / denom,
     - (op1.Re * op2.Im + op2.Re * op1.Im) / denom)
     };

let g_sqrt = sqrt; !! (global'naya funkciya `sqrt`)

!! Kvadratnyj koren' iz op1 (odno iz znachenij)
#complex.sqrt {
     double norm = complex.mod (op1);
     return #(g_sqrt ((norm + op1.Re) / 2f), g_sqrt ((norm - op1.Re) / 2f))
     };

     !!
!! Operacii dlya raboty s complex
!!

!! unarnyj '-' kak otricanie
opdef -complex = complex.neg ((<1>));
!! unarnyj '~' kak sopryazhenie
opdef ~complex = complex.conj ((<1>));

!! binarnyj '+' kak slozhenie
opdef complex + complex = complex.add ((<1>), (<2>));
!! binarnyj '-' kak vychitanie
opdef complex - complex = complex.sub ((<1>), (<2>));
!! binarnyj '*' kak umnozhenie
opdef complex * complex = complex.mul ((<1>), (<2>));
!! binarnyj '/' kak delenie
opdef complex / complex = complex.div ((<1>), (<2>));






Last-modified: Sun, 08 Sep 2002 05:41:28 GMT
Ocenite etot tekst: