ssy-"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>));