ELEKTRONINĖS KNYGOS

tus0.gif (69 bytes)

Gintautas GRIGAS
PROGRAMAVIMAS PASKALIU

5. PROGRAMAVIMO TECHNOLOGIJOS ELEMENTAI

Jau įpusėjome knygą. Jau sudarėme keliolikos paprastų uždavinių programas ir matėme, kad kiekviena jų yra savita. Dažnai ne iš karto pavyksta sudaryti gerą programą, tokią, kad ją galima būtų laikyti tobulybės viršūne. Taigi, atėjo laikas pamąstyti, kaip rašome programas, paieškoti būdų, kaip programuoti greitai ir gerai.

Tam pačiam uždaviniui galima sudaryti daug skirtingų programų. Programavimas yra kūrybinis procesas, ir sunku rasti bendrus receptus, kaip sudaryti kiekvieno uždavinio programą. Tačiau galima suformuoti bendras taisykles, kurios padėtų šį darbą paspartinti, jį geriau atlikti. Tai ypač svarbu sudarant didesnių uždavinių programas. Bet kokį didesnį darbą lengviau įveikti, suskirsčius jį į dalis – mažesnius darbus. Tas pats tinka ir programavimui.

Uždavinio programavimą galima suskirstyti į šitokias dalis – etapus:

1) uždavinio formulavimas;
2) sprendimo metodo parinkimas ar sudarymas;
3) programos rašymas;
4) programos tikrinimas;
5) programos tobulinimas;
6) programos derinimas (išbandymas kompiuteriu).

Programa yra produktas, daiktas, kuriuo naudosis daugelis žmonių. Mus supa daugybė žmogaus sukurtų daiktų. Juos vertiname dėl to, kad jie atlieka jiems skirtas funkcijas. Jais gėrimės, jeigu jie gražūs ir juose matome juos sukūrusių žmonių mintį ir pagarbą mums, t.y., tiems, kas tais daiktais naudojasi. Deja gražių daiktų ne tiek daug, o netikusių mėtosi visur.

„Programavimas yra ir mokslas ir menas. Šie du požiūriai puikiai papildo vienas kitą. Aš jaučiu, kad programavimas panašus į poeziją arba muziką. Yra elegantiškų programų, yra grakščių programų, yra spindinčių programų. Aš teigiu, kad galima parašyti puikias programas, kilnias programas ir išties didingas programas“ (Donaldas Knutas).

5.1. Uždavinio formulavimas

Norint išspręsti bet kokį uždavinį, reikia turėti jo sąlygą. Programavimo uždavinio sąlyga vadinama uždavinio formuluote. Programavimo uždavinio sprendinys (rezultatas) yra programa. Taigi, norint sudaryti programą, reikia turėti užduotį programavimui – uždavinio formuluotę.

Formuluojant uždavinį, reikia nuodugniai išsiaiškinti, kokie reikalavimai keliami būsimai programai. Priešingu atveju galima ko nors nenumatyti ir neatlikti veiksmų, kurie galbūt buvo turimi galvoje, bet nebuvo aiškiai suformuluoti. Uždavinio formuluotėje reikia aiškiai nurodyti, kokie turi būti pradiniai duomenys, ką atlikti pagal programą ir kokių rezultatų norima. (Kaip gauti rezultatus, t. y. kokius veiksmus atlikti, nustatoma vėliau, kai sudaroma programa).

1 pavyzdys. Tarkime, kad gavome užduotį:

Reikia sudaryti programą duotųjų skaičių sumai rasti.

Turime patikslinti, kiek yra duota skaičių, arba (jeigu nežinoma, kiek jų yra) apibrėžti, kaip bus nustatoma tų skaičių sekos pabaiga. Pateikiame kelias šio uždavinio formuluotes.

1. Pradiniai duomenys – du sveikieji skaičiai. Reikia juos perskaityti ir išspausdinti, apskaičiuote ir išspausdinti jų sumą.

2. Pradiniai duomenys – dešimt sveikųjų skaičių. Reikia juos perskaityti, apskaičiuoti ir išspausdinti jų sumą.

3. Pradiniai duomenys – sveikųjų skaičių, nelygių nuliui, seka. Sekos pabaigoje – nulis. Reikia perskaityti pradinius duomenis, rasti ir išspausdinti jų sumą.

Kuris šių variantų geriausias, turi nuspręsti būsimasis programos naudotojas, nes jis geriausiai žino savo uždavinį. Kai programos naudotojas yra kartu ir programuotojas (o taip būna mokantis programavimo), jis pats turi nuspręsti , kuris variantas tinkamiausias.

2 pavyzdys. Pradinis duomuo yra skaičius, reiškiantis metus. Reikia sudaryti programą, pagal kurią galima būtų nustatyti, ar metai yra keliamieji.

Ar tai, kas pasakyta, galima laikyti tikslia uždavinio formuluote. Be abejo, ne.

Pirma, nenurodyta, apie kokius metus kalbama: šio šimtmečio, šio tūkstantmečio, ar bet kuriuos metus po Kristaus gimimo. O gal tai ir metai prieš Kristų?

Antra, nepasakyta, kokia forma kompiuteris turi pateikti rezultatą.

Patiksliname uždavinio formuluotę:

Pradinis duomuo yra natūralusis skaičius, reiškiantis mūsų eros metus. Reikia sudaryti programą, pagal kurią kompiuteris išspausdintų duotuosius metus ir žodį METAI, o po to – žodį KELIAMIEJI, jeigu metai keliamieji, arba žodį PAPRASTIEJI, jeigu metai paprastieji, arba žodį NETEISINGI, jeigu pradinis duomuo neteisingas.

Tokia uždavinio formuluotė jau pakankamai aiški ir tiksli.

3 pavyzdys. Reikia parašyti programą, kuri patikrintų, ar iš trijų atkarpų, kurių ilgiai duoti, galima sudaryti trikampį. Reikia spausdinti duotuosius atkarpų ilgius ir žodį TAIP, jei galima, arba NE, jei negalima sudaryti trikampio.Išanalizuokime šią formuluotę.

Apie nulinius ir neigiamus atkarpų ilgius neverta ir kalbėti, nes iš tokių atkarpų trikampio sudaryti negalima. Todėl savaime aiškus atsakymas – NE.

Iš pradžių galima pasigesti atkarpų ilgio matavimo vienetų. Bet, gerai pagalvojus, pasidaro aišku, kad jų nurodyti ir nereikia. Svarbu, kad visos atkarpos būtų išmatuotos tais pačiais ilgio vienetais.

Taigi prie šios uždavinio formuluotės nieko pridurti nereikia.

Uždaviniai

5.1.1. Duotos dvi uždavinio formuluotės:

1) Pradinis duomuo – sveikasis skaičius n. Reikia sudaryti programą visų intervalo (0; n) lyginių skaičių sumai rasti.

2) Pradinis duomuo – sveikasis skaičius n. Reikia sudaryti programą visų lyginių skaičių nuo 0 iki n (jei n nelyginis, tai iki n – 1) sumai rasti. Jeigu n < 0, tai sumuoti neigiamus skaičius. Jų suma neigiama.

Ar šios formuluotės ekvivalenčios, t. y. ar galima sudaryti programą, atitinkančią abi formuluotes?

5.1.2. Uždavinio šitokia formuluotė.

Pradiniai duomenys – šimto sveikųjų skaičių seka. Sudarykite programą, kuri patikrintų, ar duotoje skaičių sekoje yra bent vienas nulis.

Ko šioje formuluotėje trūksta?

5.2. Kaip kuriamas arba pasirenkamas uždavinio sprendimo metodas

Programuotojas turi mokėti išspręsti uždavinį. Tik po to, kai bus sudaryta programa, kompiuteris ją galės atlikti ir išvaduoti žmogų nuo daugelio ilgų duomenų apdorojimo veiksmų.Tačiau juos žmogus turi gerai mokėti atlikti ir sugebėti užrašyti programoje. Taigi programuotojui visų pirma reikia išsiaiškinti, kaip sprendžiamas uždavinys.

Daugelio paprastų uždavinių sprendimo būdas akivaizdus. Pavyzdžiui, jeigu reikia atlikti aritmetinius veiksmus su keletu skaičių, pakanka parašyti aritmetinį reiškinį; o atliekant vienodus veiksmus su daugeliu skaičių, rašomas ciklas, ir pan.

Programuojant sudėtingesnius uždavinius, reikia remtis žinomais matematikos dėsniais, taisyklėmis, teoremomis.

1 pavyzdys. Reikia rasti visų nelyginių natūraliųjų skaičių, ne didesnių už n, sumą. Paprasčiausias šio uždavinio sprendimo būdas – parašyti ciklą, pagal kurį būtų sumuojami visi iš eilės einantys nelyginiai skaičiai: 1 + 3 + 5 ... . Tačiau neskubėkime. Ši seka – aritmetinė progresija. O progresijos narių sumą sn apskaičiuojame pagal formulę:

sn = (a1 + an )/2

čia a1 – pirmas narys,
an
– paskutinis narys,
n – narių skaičius.

Parašę šią formulę vietoj ciklo, daug kartų sumažinsime kompiuterio darbą.

2 pavyzdys. Grįžkime prie ankstesnio skyrelio 3 pavyzdžio uždavinio. Čia reikia patikrinti, ar iš trijų atkarpų, kurių ilgiai duoti, galima sudaryti trikampį. Norint išspręsti šį uždavinį, pakanka žinoti trikampio savybę, teigiančią, kad kiekviena trikampio kraštinė yra mažesnė už kitų dviejų kraštinių sumą.Pažymėję atkarpų ilgius raidėmis a, b ir c, galime šią savybę išreikšti loginiu reiškiniu

(a+b > c) and (a+c > b) and (b+c > a),

kurio reikšmė yra true, jeigu iš atkarpų galima sudaryti trikampį, ir false – priešingu atveju.

3 pavyzdys. Reikia sudaryti programą, kurį nustatytų į kokį pirminių daugiklių skaičių išskaidomas duoto skaičiaus faktorialas. Pavyzdžiui, jeigu pradinis duomuo yra skaičius 5, tai rezultatas turi būti 15, nes

10! = 3628800 = 2×2×2×2×2×2×2×2×3×3×3×3×5×5×7.

Paprasčiausias sprendimo būdas – rasti skaičiaus faktorialą, o po to jį skaidyti į pirminius daugiklius ir suskaičiuoti tų daugiklių skaičių. Tačiau ir nedidelio skaičiaus faktorialas yra labai didelis skaičius. Todėl greitai gausime perpildymą. Betgi kai ir pradinis duomuo, ir rezultatas yra nedideli skaičiai, nelogiška, kad būtų perpildymas. Todėl reikia paieškoti kitokių sprendimo būdų.

Skaičiaus n faktorialas yra skaičių nuo 1 iki n sandauga. Taigi galima skaidyti į pirminius daugiklius atskirus skaičius jų nesudauginus.

Sprendžiant uždavinį, visada reikia nuodugniai išanalizuoti jo formuluotę, neužmiršti visų galimų atvejų, visų galimų pradinių duomenų reikšmių. Kartais sprendžiant uždavinį aptinkama, kad formuluotėje ne viskas pasakyta arba joje yra prieštaravimų. Tada ji tikslinama.

Daugelį uždavinių galima išreikšti lygtimis. Programuotojas, be abejo, turi pats mokėti tas lygtis spręsti ir jų sprendimo algoritmą pateikti programoje. Atskira matematikos šaka (skaičiavimo matematika ir skaičiavimo metodai) nagrinėja matematikos uždavinių, daugiausia lygčių, sprendimo metodus, tinkančius programavimui.

Iš kelių uždavinio sprendimo metodų pasirenkamas vienas, atsižvelgiant į jo universalumą, programavimo paprastumą, kompiuterio laiko ir atminties ekonomiją bei kitas charakteristikas, turinčias įtakos programavimui ir programos atlikimui.

Tenka programuoti įvairių žmogaus veiklos sričių (fizikos, biologijos, muzikos, ekonomikos ir pan.) uždavinius ir atlikti veiksmus su jiems būdingais objektais (gyvūnais, muzikos garsais, ekonominiais rodikliais ir pan.). Uždavinių sprendimo metodus nagrinėja matematika, o veiksmai atliekami su matematiniais objektais: skaičiais, loginėmis reikšmėmis ir pan.Todėl nematematiniai uždaviniai pakeičiami jiems ekvivalenčiais matematikos uždaviniais, kitaip sakant, sudaromas realaus pasaulio uždavinio matematinis modelis. Toks keitimas vadinamas modeliavimu.

Su realaus pasaulio matematiniu modeliu susidūrėme, nagrinėdami 3.1 skyrelio 1 pavyzdį. Tenai tiltus pakeitėme loginiais kintamaisiais ir atlikome su jų reikšmėmis logines operacijas, atspindinčias realų pasaulį.

5.3. Programos rašymas

Kai uždavinys nesudėtingas ir iš karto aišku, kaip jį spręsti, tai nesunku ir jo programą parašyti. Sudėtingesnį uždavinį aprėpti ir programuoti visą iš karto sunku, o labai sudėtingą – išvis neįmanoma. Todėl stambesnis uždavinys skaidomas į smulkesnes dalis ir mėginama rašyti kiekvienos dalies programą. Jeigu kuri nors dalis yra dar per stambi, ji skaidoma į smulkesnes dalis tol, kol tos jų programos pasidaro pakankamai trumpos ir vaizdžios. Šitaip dalimis galima parašyti ir labai stambių uždavinių programas.

Pavyzdys. Parašykime programą 5.2 skyrelio 3 pavyzdžio uždaviniui: į kokį pirminių daugiklių skaičių išskaidomas duoto skaičiaus faktorialas.

Pirmiausia sudarome programos eskizą,

program p;
  var n,                 { n! }
        k,                 { faktorialo daugiklis }
        d,                 {vieno k pirminių daugiklių skaičius }
        daugsk: integer;
begin
 
read(n);
  daugsk := 0;
  for k := 2 to n do
    begin
      
       daugsk := daugsk + d
    end;
  writeln(daugsk)
end.

Tuos veiksmus, kuriuos mokame programuoti, užrašėme Paskalio kalbos žymenimis, o tuos , kurių dar neprogramavome, pavaizdavome stačiakampiu su jame įrašytu žodiniu veiksmų formulavimu. Taigi, suprogramavome tik dalį uždavinio. Tai, kas įrašyta į stačiakampį, dar reikės programuoti. Taigi, programos rašymą suskaidėme į dalis.

Dabar reikia suprogramuoti, t.y., užrašyti Paskalio kalbos žymenimis stačiakampio veiksmus. Juos galima paimti iš 4.5.2 uždavinio sprendimo ir pritaikyti šio uždavinio programai.

program p;
  var n, nn,                   { n! }
        k, kk,                   { faktorialo daugiklis }
        d,                         { vieno k pirminių daugiklių skaičius }
        daugsk: integer;
begin
 
read(n);
  daugsk := 0;
  nn := n;
  for k := 2 to n do
    begin
       
kk := k;
        d := 0;
        while kk <= nn do
             begin
                while
nn mod kk = 0 do
                    begin
                      
d := d + 1;
                       nn := nn div kk
                    end;
               
kk := kk + 1
             end;
        
daugsk := daugsk + d
     end;
  writeln(daugsk)
end.

Štai ir visa programa. Perrašydami programos fragmentą iš 4.5.2 uždavinio sprendimo, juose turėjome kai ką pakeisti, ypač kintamųjų vardus. Teko programą papildyti naujų kintamųjų aprašais. Tai nepatogu ir keičiant galima padaryti klaidų. Šių nepatogumų galima išvengti naudojantis autonominėmis programų dalimis – funkcijomis ir procedūromis. Apie jas kalbėsime 6 skyriuje.

5.4. Programos tikrinimas ir derinimas

Jau spėjome patirti, kad, sudarant programą, suklysti labai lengva. Todėl ją reikia kruopščiai ir nuodugniai patikrinti dar prieš pateikiant kompiuteriui.

Tikrinant kiekvieną programą, rekomenduojama įsitikinti, ar:

1) nėra sintaksės klaidų;
2) aprašyti visi (kintamųjų) vardai;
3) apibrėžiamos visų kintamųjų reikšmės prieš jas vartojant;
4) programos veiksmai baigtiniai;
5) teisingi rezultatai.

Sintaksės (arba gramatikos) klaidos atsiranda, kai, rašydami programą, nusižengiame programavimo kalbos (mūsų atveju – Paskalio) gramatikai – pavartojame ne tuos arba ne ten, kur reikia, skyrybos ženklus, netaisyklingai parašome kokią nors kalbos konstrukciją ir pan. Sintaksės klaidos didesnių problemų nekelia nes jas atranda kompiliatorius ir čia pat informuoja apie jas.

Beveik visi kompiliatoriai neaptinka neapibrėžtų reikšmių vartojimo. Primename, kad visuose reiškiniuose (prieskyros sakinio dešinėje pusėje, po žodžių if, while, to, rašymo sakiniuose ir kitur) galima vartoti tik apibrėžtas (prieš tai priskirtas) kintamųjų reikšmes. Kai programą sudaro vien paprastieji (prieskyros, duomenų skaitymo arba rašymo) sakiniai, reikšmių apibrėžtumą patikrinti labai paprasta: prieš kiekvieną sakinį, kuriame vartojama kintamojo reikšmė, bet kurioje programos vietoje turi būti sakinys, suteikiantis reikšmę tam kintamajam.

Kai kintamajam priskiriantis reikšmę sakinys yra sąlyginiame sakinyje arba cikle, tai juo pasitikėti ne visada galima. Mat toks sakinys gali būti kartais neatliekamas, o tada kintamojo reikšmė gali likti neapibrėžta.

1 pavyzdys. Turime programą skaičiaus absoliutiniam didumui (moduliui) rasti:

program abs;
  var a, b : integer;
begin
 
read(a);
  if a > 0 then b := a
             else if a < 0 then b := -a;
  writeln(b)
end.

Kai a = 0, neatliekama nė viena sąlyginio sakinio šaka ir kintamojo b reikšmė lieka neapibrėžta. Vadinasi, ši programa neteisinga vien dėl to, kad jos rezultatas yra neapibrėžtas tik su viena pradinio duomens reikšme.

Jeigu kintamojo reikšmė apibrėžiama tik sąlyginiame sakinyje, tai reikia įsitikinti, ar, esant bet kokiems pradiniams duomenims, bus atliekama bent viena sakinio šaka, kurioje yra apibrėžiantis reikšmę sakinys.

Jeigu kintamasis gauna reikšmę tik cikle, tai reikia įsitikinti, ar tas ciklas visada bus bent kartą atliekamas.

Kompiuteris gali atlikti tik tokią programą, kurioje reikalaujama baigtinio veiksmų skaičiaus. Kai programoje yra ciklų, kurių antraštė prasideda žodžiu while, tai atsiranda pavojus ciklui niekada nesibaigti.

Tam, kad ciklas būtų baigtinis, būtina (bet nepakankama) sąlyga, jog ciklo viduje būtų keičiama bent viena reikšmė, esanti ciklo antraštės sąlygoje. Pavyzdžiui, net ir nesigilindami į atliekamų veiksmų prasmę, galime pasakyti, kad nebaigtinis yra ciklas

while a > 0 do
  
write(a)

Jeigu ciklo antraštės sąlyga a > 0 yra tenkinama prieš atliekant ciklą, tai ji bus ir toliau tenkinama, nes cikle nekeičiama kintamojo a reikšmė.

Kad minėta sąlyga yra tik būtina, bet nepakankama, matyti iš šitokio pavyzdžio:

while a > 0 do
  
a := a + 1

Čia ciklo antraštės sąlygos kintamojo a reikšmė keičiama didėjimo kryptimi. Jeigu sąlyga a > 0 buvo tenkinama prieš atliekant ciklą pirmą kartą, ji bus tenkinama ir atlikus ciklą kiek norima kartų.

Išnagrinėkime kitą ciklą:

while a >= 0 do
   a := a - 1

Šis ciklas yra baigtinis, kad ir kokia didelė būtų kintamojo a reikšmė prieš atliekant ciklą: atiminėjant iš šios reikšmės vienetą, ji vis tiek kada nors pasidarys neigiama, sąlyga ciklo antraštėje nebus tenkinama ir ciklas baigsis.

Kaip patikrinti, ar programos rezultatai teisingi?

Pats paprasčiausias būdas – išbandyti kompiuteriu. Labai svarbu parinkti tinkamus kontrolinius pradinius duomenis. Jeigu programoje yra ciklų, pravartu kontrolinius duomenis parinkti tokius, kad ciklas nebūtų nė karto atliekamas, kad būtų atliekamas vieną kartą ir kad būtų atliekamas kelis kartus. Kai programoje yra sąlyginių sakinių, reikia parinkti tokius kontrolinius duomenis, kad bent vieną kartą būtų atlikta kiekvieno sąlyginio sakinio dalis (šaka).

2 pavyzdys. Tarkime, turime programą skaičiui 2 pakelti laipsniu n:

program laipsnis;
  var n,                    { laipsnio rodiklis }
        p,                    { rezultatas }
        k: integer;
begin
 
read(n);
  p := 1;
  for k := 1 to n do
    
p := p*2;
  writeln(p)
end.

Jai patikrinti parenkame keletą pradinių duomenų ir pateikę juos kompiuteriui gauname tokius rezultatus:

-5     1
0      1
1      2
2      4
5    32

Rezultatai teisingi, kai n >=0. Kai n < 0, tai visada gauname vienetą. Todėl galima tvirtinti, kad ši programa tinka dvejetui kelti teigiamu laipsniu.

Ar galima teigti, kad programos rezultatai yra teisingi, esant bet kurioms pradinių duomenų reikšmėms, jei jie teisingi su keliomis pasirinktomis pradinių duomenų reikšmėmis? Deja, ne. Išnagrinėkime dar vieną pavyzdį.

3 pavyzdys. Tarkime, yra sudaryta kita programa skaičiui 2 kelti laipsniu n.

program laip;
  var k, n, p: integer;
begin
 
read(n);
  p := 1;
  for k := 1 to n do
    
p := k*2;
  writeln(p)
end.

Apskaičiavę šios programos rezultatus, esant pradiniams duomenims 0, 1, 2, gauname 1, 2, 4. Jie teisingi. Tačiau daryti išvadą, kad programa laip teisinga, dar per anksti. Paėmę bet kurį didesnį pradinį duomenį, įsitikinsime, kad programos rezultatas neteisingas. Pavyzdžiui, kai n = 3, rezultatas yra 6, o turi būti 8, nes 23 = 8.

Taigi, apsiribojus keliais pradiniais duomenimis arba keliais jų variantais, galima praleisti kaip tik tuos duomenis, su kuriais gaunami klaidingi rezultatai. Kitaip tariant, su atskirais pradiniais duomenimis galima įrodyti tik tai, kad programa klaidinga (jei ji iš tikrųjų klaidinga), o ne tai, kad teisinga (nors iš tikrųjų ji ir yra teisinga). Todėl labai svarbu parinkti tinkamus kontrolinius pradinius duomenis, kuriems esant rezultatai galėtų būti neteisingi.

Apie kontrolinių duomenų parinkimą galima paskaityti straipsnyje [6].

Klaidų ieškojimas ir taisymas kompiuteriu vadinamas programos derinimu.

Ne visas klaidas pavyksta greitai ir lengvai rasti. Sudėtingose programose pasitaiko giliai „pasislėpusių“ klaidų. Kaip jų ieškoti ir jas rasti, bendrų receptų nėra. Ieškant klaidų programose, praverčia logika, įžvalgumas ir, žinoma, geras dalyko – programavimo – išmanymas.

Atlikdami programą kompiuteriu, ne tik aptinkame klaidas, bet pabūname jos naudotoju: paruošiame pradinius duomenis, skaitome kompiuterio išspausdintus rezultatus, t.y. įsitikiname, ar patogu naudotis sudarytąja programa. Praktiškai išbandydami programą, galime patobulinti ir uždavinio formuluotę. Tada reikia vėl grįžti prie ankstesnių programavimo darbų ir pakoreguoti jau sudarytą programą.

Kad programa teisinga su visomis galimomis pradinių duomenų reikšmėmis, reikia įrodyti matematiškai. Tai padaryti kur kas sunkiau, negu patikrinti programos rezultatą su atskiromis pradinių duomenų reikšmėmis.

4 pavyzdys. Pabandykime įsitikinti, kad programos laipsnis (žr. 2 pavyzdį) rezultatas teisingas, ne skaičiuodami, o išskleisdami ciklą formule, esant įvairioms pradinio duomens reikšmėms.

p := 1;
for k := 1 to n do
   p := p*2

Kai ciklas n < 1, ciklas neatliekamas nė karto. Vadinasi, ciklas rezultato reikšmės nekeičia ir išlieka ankstesnė jo reikšmė p = 1.

Kai n > 1, ciklas kartojamas n kartų. Vadinasi,

p = 1 × 2,              kai n = 1,
p = 1 × 2 × 2,        kai n = 2,
p = 1 × 2 × 2 × 2,   kai n = 3.

Nesunku padaryti išvadą, kad

p = 1 × 2 × 2 × ... ×2
            n kartų

su kiekvienu n. Tai reiškia, kad p = 2n.

Uždaviniai

5.4.1. Duoti du sąlyginiai sakiniai

1) if a > 0 then b := 5
               else b := 10;

2) if a < 0 then b := 10
               else b := 5;

Kokiai kintamojo a reikšmei esant jie neekvivalentūs (t.y. jų rezultatai skirtingi)?

5.4.2. Kintamojo a reikšmė iš anksto nežinoma. Nurodykite sąlygas, kurias tenkins
         kintamojo reikšmė atlikus šiuos sakinius:

a) if a > 10 then a := 10
                 else a := a + 5

b) if a > 10 then a := 1
                 else a := 0

5.4.3. Kintamojo a reikšmė iš anksto nežinoma. Nurodykite sąlygas, kurias tenkins
         kintamojo reikšmė atlikus šiuos sakinius:

a) while a > 10 do
      
a := a-1

b) while a > 10 do
 
      a := a-10

5.4.4. Kokias sąlygas turi tenkinti kintamojo k reikšmė, kad būtų baigtiniai šie ciklai:

a) while c < 0 do
       c := c+k

a) while k <> 0 do
       k := k+1

a) while k <> 0 do
       k := k-2

5.5. Programos tobulinimas

Ne iš karto pavyksta sudaryti grakščią ir ekonomišką programą. Kai programa sudaryta, netgi patikrinta, dažnai gimsta naujų idėjų programai patobulinti – padaryti ją trumpesnę, lakoniškesnę, aiškesnę arba ekonomiškesnę (pavyzdžiui, greičiau atliekamą).

1 pavyzdys. Tarkime, skaičiaus moduliui rasti buvo parašytas šitoks sakinys:

if a >= 0 then b := a
             else if a < 0 then b := -a

Nesunku pastebėti, kad antroji sąlyginio sakinio dalis apima visus tuos atvejus, kurie netenkina pirmosios sąlygos (a >= 0). Todėl sakinį galima užrašyti trumpiau (ir aiškiau):

if a >= 0 then b := a
             else b := -a

2 pavyzdys. Turime ciklą:

for k := 1 to 1000 do
   s := s + p + k*k

Kiekvieną kartą atliekant jį, prie kintamojo s reikšmės pridedama ta pati kintamojo p reikšmė. Todėl sudėtį galima iškelti iš ciklo, pakeitus ją daugyba:

s := s + 1000*p;
for k := 1 to 1000 do
   s := s + k*k

Dabar 1000 sudėčių bus pakeista viena daugyba, todėl kompiuteris programą atliks greičiau.

Programos rašymas, tikrinimas ir tobulinimas yra tarpusavyje glaudžiai susiję. Patobulinę patikrintą programą, vėl turime ją tikrinti, nes tobulindami gaėjome padaryti naujų klaidų. Jas pataisius gali vėl atsirasti tobulintinų vietų. Kartais tobulinant ir taisant klaidas, programoje atsiranda tiek daug pakeitimų ir „užlopytų“ vietų, kad ją geriau parašyti iš naujo.

Programuotojas, rašydamas programą, pripranta prie jos ir nepastebi trūkumų. Dažnai programą pavyksta patobulinti, kai ją skaitome praėjus ilgesniam laikui nuo jos rašymo. Tada būname ją primiršę ir geriau sekasi viską pergalvoti iš naujo. Trūkumus lengviau pastebėti ir kito programuotojo sudarytoje programoje.

5.6. Programavimo stilius

Naudotojas naudoja programą savo uždaviniams spręsti. Jam svarbu, kokį uždavinį sprendžia ta programa, kaip pateikti pradinius duomenis kompiuteriui ir kaip kompiuteris išspausdins rezultatus. Kadangi duomenų skaitymo ir rašymo veiksmai nurodomi programoje, tai naudotojui geresnė ta programa, kuriai paprasčiau paruošti pradinius duomenis ir kuri patogiau (vaizdžiau) išspausdins rezultatus.

Programą sudaro programuotojas, ją atlieka kompiuteris, o ja naudojasi žmogus – naudotojas (25 pav.). Vadinasi, programą galima vertinti programuotojo, kompiuterio ir naudotojo požiūriu.

25 pav. Programuotojo, naudotojo ir kompiuterio sąryšis

 

 

 

Naudotojas naudoja programą savo uždaviniams spręsti. Jam svarbu, kokį uždavinį sprendžia ta programa, kaip pateikti pradinius duomenis kompiuteriui ir kaip kompiuteris pateiks rezultatus. Kadangi duomenų skaitymo ir rašymo veiksmai nurodomi programoje, tai naudotojui geresnė ta programa, kuriai paprasčiau paruošti pradinius duomenis ir kuri patogiau (vaizdžiau) parodys (išspausdins) rezultatus.

1 pavyzdys. Pateikiame dvi programas tam pačiam uždaviniui spręsti – dviem laiko intervalams, išreikštiems valandomis ir minutėmis, sudėti.

program laikas1;
  var ah, amin,               { pirmas intervalas, val., min. }
        bh, bmin,               { antras intervalas, val., min. }
        h, min: integer;       { intervalų suma, val., min. }
begin
 
read(ah, amin, bh, bmin);
  min := ah*60 + amin + bh*60 + bmin;
  h := min div 60;
  min := min mod 60;
  writeln(h, min: 3)
end.

program laikas2;
  var ah, amin,             { pirmas intervalas, val., min. }
        bh, bmin,             { antras intervalas, val., min. }
        h, min: integer;     { intervalų suma, val., min. }
begin
 
read(ah, bh, amin, bmin);
  min := ah*60 + amin + bh*60 + bmin;
  h := min div 60;
  min := min mod 60;
  writeln(h, min: 3)
end.

Programos skiriasi tik pradinių duomenų pateikimo tvarka. Be abejo, racionaliau išdėstyti pradiniai duomenys programoje laikas1 – čia pirmiau eina pirmojo intervalo valandos ir minutės, po to antrojo intervalo valandos ir minutės. Todėl naudotojas geriau pasirinks programą laikas1.

Programos tekstas naudotojo nedomina – jis gali net nemokėti jo perskaityti. Jam reikalinga tik trumpa informacija, kaip pateikti pradinius duomenis ir kokius rezultatus jis gaus. Tokia informacija vadinama naudojimosi programa instrukcija. Programos laikas1 ji būtų maždaug tokia:

Programa laikas1 skirta dviem laiko intervalams sumuoti. Pradiniai duomenys – 4 sveikieji skaičiai, pateikiami šitokia tvarka: pirmojo intervalo valandos ir minutės, antrojo intervalo valandos ir minutės. Rezultatai – laiko intervalų suma: pirmiau valandos, po to – minutės.

Kompiuteris atlieka veiksmus, surašytus programoje, formaliai, paraidžiui. Todėl jo požiūriu programos vaizdumas ar duomenų paruošimo patogumas neturi prasmės. Tačiau kompiuteriui svarbus programos ekonomiškumas. Iš tikrųjų programos ekonomiškumas svarbus naudotojui (ne kompiuteriui). Tik šis parametras siejasi su kompiuterio parametrais (skaičiavimų greičiu ir atmintinės kiekiu). Todėl kompiuterio požiūriu geresnė programa yra ta, kuriai mažiau reikia atmintinės (mažiau kintamųjų) ir mažiau veiksmų (ypač – mažesnis ciklų kartojimų skaičius).

2 pavyzdys. Pateikiame dvi programas aritmetinės progresijos pirmųjų 100 narių sumai rasti. Abiejų programų pradiniai duomenys tie patys – pirmasis narys ir skirtumas.

program prog;
  const n = 100;             { sumuojamų narių skaičius }
  var a1,                        { pirmasis narys }
        aj,                         { narys }
        d,                          { skirtumas }
        suma,                    { narių suma }
        j: integer;
begin
 
read(a1, d);
  suma := a1; aj := a1;
  for j := 2 to n do
    begin
       
aj := aj + a1;
        suma := suma + aj
    end;
 
writeln(suma)
end.

program progresija;
  const n = 100;                { sumuojamų narių skaičius }
  var a1,                           { pirmasis narys }
        an,                           { paskutinis narys }
        d,                             { skirtumas }
        suma: integer;           { narių suma }
begin
 
read(a1, d);
  an := a1 + (n-1)*d;
  suma := n * (a1 + an) div 2;
  writeln(suma)
end.

Aišku, ekonomiškesnė yra programa progresija, kurioje progresijos narių suma skaičiuojama pagal formules, be ciklo.

Programuotojas rašo programą kompiuteriui. Atrodytų, kad mes esame programų kūrėjai, o kompiuteris – jų skaitytojas ir atlikėjas. Kompiuteris tikrai yra programų atlikėjas. Tačiau programas skaito ir žmogus, norėdamas sužinoti, kokie veiksmai užrašyti joje, pasisemti patirties iš jau sudarytų programų, kai mokosi programuoti, kai sudaro naujas programas naudodamasis jau sudarytų programų tekstais. Programas tenka skaityti, kai jos tobulinamos, kai taisomos klaidos, kai pritaikomos naujiems uždaviniams. Taigi ir pačiam programos autoriui dažnai tenka pabūti jos skaitytoju – nuolat grįžti prie anksčiau sudarytų ir jau pamirštų programos dalių.

Taigi rašant programą, reikia nuolat galvoti, kad ją nagrinės žmogus ir įsivaizduoti esant jos skaitytoju. Todėl reikia stengtis, kad programa turi būti aiški, lengvai suprantama. Kai programa aiški, joje mažiau būna ir klaidų, mažiau „tamsių vietų“ klaidoms pasislėpti.

Norint parašyti gerai suprantamą ir lengvai skaitomą programą, reikia daugiau pastangų. Tačiau sugaištas laikas beveik visada atsiperka: programa rašoma vieną kartą, o skaitoma daug kartų, programą rašo vienas žmogus, o skaito daugelis.

Taigi programuotojo požiūriu vertingesnė yra aiškesnė ir vaizdesnė programa. Šis vertinimo kriterijus gali prieštarauti kompiuteriniam: tam, kad programa būtų aiškesnė, kartais reikia daugiau kintamųjų arba suprantamesnių, bet lėčiau atliekamų veiksmų. Bet kompiuterių greitis ir atmintinė nuolat didėja, todėl kuriant programas vis dažniau atsižvelgiama į žmogaus poreikius. Pagaliau kompiuteris tarnauja žmogui, o ne atvirkščiai.

Taigi programos kokybę reikia vertinti trimis požiūriais:

1) programuotojo (programos teksto skaitytojo),
2) programos naudotojo ir
3) kompiuterio.

Bet kokiu atveju programa turi būti teisinga – negalima kalbėti apie programos kokybę, jeigu ji neteisinga, arba švelniau pasakius – sprendžia ne tą uždavinį, kurio buvo tikėtasi.

5.7. Rezultatų apipavidalinimas

Savaime aišku, kad turi būti patogu naudotis programa. Iš programos teikiamų pranešimų jam turi būti aišku, ką kiekvienu momentu jis turi daryti, kokius pradinius duomenis pateikti. Rezultatai turi būti išduodami aiškiu ir suprantamu pavidalu.

Jeigu programą numatoma dažniau ir ilgiau naudoti, tai reikia stengtis jos rezultatus pateikti kuo vaizdžiau, su paaiškinimais. Rašydami programą sugaišime daugiau laiko, tačiau ja bus lengviau naudotis. Kompiuteris gali ne tik apskaičiuoti rezultatus, bet ir spausdinti visiškai parengtus dokumentus – algalapius, žiniaraščius, tvarkaraščius ir pan. Kaip tai padaryti pailiustruosime pavyzdžiu.

Pavyzdys. Programa duoto intervalo sveikųjų skaičių kvadratų ir kubų lentelei spausdinti.

program KvadrataiKubai;
  var nuo, iki, { skaičių intervalas }
        n: integer;
begin
 
read(nuo, iki);
  for n := nuo to iki do
   
writeln(n: 5, n*n: 5, n*n*n: 5);
end.

Jeigu šiai programai pateiktume pradinius duomenis

20   30

tai kompiuterio ekrane pamatytume šitokius rezultatus:

20   400     8000
21   441     9261
22   484   10648
23   529   12167
24   576   13824
25   625   15625
26   676   17576
27   729   19683
28   784   21952
29   841   24389
30   900   27000

Kai domimės tik algoritmu ir eksperimentuojame su kompiuteriu, tai toks rezultatų pavaizdavimas mus tenkina. Tuo tarpu jeigu programą planuotume platinti naudotojams, reikėtų, kad naudotojas matytų, ką ši programa skaičiuoja, kokių pradinių duomenų jai reikia, o rezultatai turėtų būti pateikti vaizdžiau, pavyzdžiui lentele. Papildysime programą visais šiais naudotojui reikalingais komponentais.

program KvadrataiKubai;
var nuo, iki, { skaičių intervalas }
n: integer;
begin
writeln('Programa duoto intervalo skaičių');
writeln('kvadratams ir kubams skaičiuoti');
writeln;
write('Intervalo pradžia: ');
read(nuo);
write('Intervalo pabaiga: ');
read(iki);
writeln;
writeln(' Kvadratai ir kubai ');
writeln;
writeln('+-------+--------+--------+');
writeln(' :        :         :         :');
writeln(' :     n :   n*n : n*n*n :');
writeln(' :        :         :         :');
writeln('+-------+--------+--------+');
for n := nuo to iki do
writeln(' : ',  n: 5, ' : ', n*n: 5, ' : ', n*n*n: 5,' :');
writeln('+-------+--------+--------+')
end.

Dabar kompiuteris informuos apie tai ką ši programa skaičiuoja, paprašys pradinių duomenų: intervalo pradžios ir pabaigos. Pasibaigus programai ekrane matysime štai tokį vaizdą:

Programa duoto intervalo skaičių
kvadratams ir kubams skaičiuoti

Intervalo pradžia: 20
Intervalo pabaiga: 30

Kvadratai ir kubai
+-------+-------+----------+
 :        :        :           :
 :     n :  n*n :   n*n*n :
 :        :        :           :
+-------+-------+----------+
 :   20 :  400 :   8000 :
 :   21 :  441 :   9261 :
 :   22 :  484 : 10648 :
 :   23 :  529 : 12167 :
 :   24 :  576 : 13824 :
 :   25 :  625 : 15625 :
 :   26 :  676 : 17576 :
 :   27 :  729 : 19683 :
 :   28 :  784 : 21952 :
 :   29 :  841 : 24389 :
 :   30 :  900 : 27000 :
 +------+-------+----------+

Lentelės rėmeliai dar nelabai gražūs – jie sudaryti iš simbolių, kuriuos galima surinkti kompiuterio klaviatūra. Gražesnę lentelę galima padaryti iš pseudografikos ženklų. Tą pačią programą pakeisime taip, kad kompiuteris lentelės rėmelius sudarytų iš pseudografikos ženklų.

program KvadrataiKubai;
  var nuo, iki, n: integer;
begin
 
writeln('Programa duoto intervalo skaičių');
  writeln('kvadratams ir kubams skaičiuoti');
  writeln;
  write('Intervalo pradžia: ');
  read(nuo);
  write('Intervalo pabaiga: ');
  read(iki);
  writeln(' Kvadratai ir kubai ');
  writeln;
  writeln('╔═══════╤═══════╤═══════╗');
  writeln('║                │                │                ║ ');
  writeln('║             n │          n*n │        n*n*n ║');
  writeln('║                │                │                ║');
  writeln('╠═══════╪═══════╪═══════╣');
  for n := nuo to iki do
    
writeln(' ║ ',  n:5,  '  │  ',  n*n:5,  '  │  ',  n*n*n:5,  '   ║');
  writeln('╚═══════╧═══════╧═══════╝')
end.

Dabar kompiuteris rezultatus pateiks surašytus į štai tokią lentelę:

Kvadratai ir kubai
╔═══════╤═══════╤═══════╗
║                │                │                ║
║             n │          n*n │        n*n*n ║
║                │                │                ║
╠═══════╪═══════╪═══════╣
║           20 │          400 │        8000 ║
║           21 │          441 │        9261 ║
║           22 │          484 │      10648 ║
║           23 │          529 │      12167 ║
║           24 │          576 │      13824 ║
║           25 │          625 │      15625 ║
║           26 │          676 │      17576 ║
║           27 │          729 │      19683 ║
║           28 │          784 │      21952 ║
║           29 │          841 │      24389 ║
║           30 │          900 │      27000 ║
╚═══════╧═══════╧═══════╝

Atkreipiame dėmesį į tai, kad programos tekste lentelę formuojantys sakiniai ženklai išdėstyti taip, kad būtų galima matyti, koks „paveikslas“ bus piešiamas kompiuterio ekrane.

Apipavidalinant rezultatus svarbus kiekvienas rašomas simbolis ir kiekviena tuščia vieta (tarpas). Todėl būsimą ekrano vaizdą geriau pirma suprojektuoti ant languoto popierio ir po to rašyti programą. Programoje padarytos klaidos lengvai pastebimos ją atlikus kompiuteriu.

Dar didesnes rezultatų vaizdavimo galimybes duoda tikroji grafika (ne pseudo grafika!). Tačiau mes pagrindinį dėmesį skirsime algoritmams ir grafikos nenagrinėsime.

Praktikos darbas

5.7.1. Daugybos lentelė. Suprojektuokite daugybos lentelę ir sudarykite programą, kuri apskaičiuotų lentelės elementus ir juos įrašytų į ekrane rodomą lentelę.

5.8. Programos teksto apipavidalinimas

Kompiuteriui nesvarbu, kaip išdėstytas programos tekstas. Tačiau žmogui nagrinėti programą kur kas lengviau, kai aiškios sakinių ribos, lengvai suvokiama programos struktūra.

Programą lengviau skaityti, kai programos teksto išdėstymas atspindi programos struktūrą, kai iš teksto vaizdžiai matosi kiekvienos valdymo struktūros pradžia ir pabaiga bei struktūrų hierarchija – kaip struktūros įdėtos viena į kitą. Tai išreiškiama programos teksto eilučių lygiavimu ir atitraukimu nuo kairiojo puslapio krašto. Lygiavimo taisyklės labai paprastos: lygiaverčiai sakiniai, t.y. priklausantys tai pačiai sakinių sekai vienodai atitraukiami nuo kairiojo eilutės krašto – lygiuojami. Į sakinį įdėti kiti sakiniai (jie sudaro jau kitą, žemesnį, hierarchijos lygį) daugiau patraukiami į dešinę. Per kiek ženklų patraukti į dešinę, kaip išdėstyti sakinių pradžios ir pabaigos bazinius žodžius (pvz. begin – end, case – end, repeat – until), yra stiliaus dalykas. Tačiau gero stiliaus taisyklės reikalauja, kad visos programos tekstas būtų išdėstytas pagal vienodas taisykles. Šioje knygoje kiekvieną naują sakinių hierarchijos lygį pradedame su nemažesniu kaip dviejų pozicijų poslinkiu į dešinę, kiekvieną žodį end rašome po jį atitinkančiu žodžiu begin.

Pateiksime lygiavimo schemos pavyzdį

read ...
a := ...
for ...
   begin
     
a :=
      while ...
           case ...
              5: repeat
                      if
...
                          then
                          else
                      b := ...
                  until ...
             
6: for ...
           end;
   end;

   while
...
         if ...

   write ...

Šitoks programos teksto išdėstymas primena knygos turinį. Smulkesnių skyrelių pavadinimai daugiau patraukti į dešinę, negu juos gaubiančių stambių skyrių.

Įvairiau lygiuojami sąlyginiai sakiniai. Kai veiksmai šakojasi į dvi trumpai užrašomas kryptis, žodį else rašomas po jį atitinkančiu žodžiu then, pavyzdžiui:

if a = 0 then write(a)
           else write(b)

Šakojimąsi į dvi sudėtingesnės šakas rekomenduojama užrašyti šitaip:

if a = 0
     then ...
     else ...

Kai veiksmai šakojasi į daugelį krypčių, kiekviena nauja žodžių else ir if pora šiek tiek patraukiama į dešinę, pavyzdžiui:

if a = b then write(a+1)
   else if a = c then write(a+2)
      else if a = d then write(a+3)
         else write(a)

Šitaip rašant tekstas mažiau nuslenka į dešinę.

Programą lengviau skaityti, kai vienoje eilutėje yra vienas sakinys. Programos vaizdumas nenukenčia ir kai į vieną eilutę sudedami keli labai trumpi ir tarpusavyje susiję sakiniai, pavyzdžiui:

a := 0; b := 0;

Į vieną eilutę galima parašyti ir neigą sudėtinį sakinį, pavyzdžiui:

begin read(a); write(a) end

Vienos eilutės tekstas taip pat turi struktūrą: vieni žodžiai ar simboliai yra stipriau susiję, negu kiti. Sąsajų stiprumas išreiškiamas tarpais. Tarp logiškai išsiskiriančių simbolių, žodžių ar jų grupių, esančių vienoje eilutėje, reikia palikti didesnį tarpą, negu tų grupių viduje. Pavyzdžiui, po vieną tarpą rekomenduojama palikti abipus prieskyros simbolio :=. Jeigu reiškinyje (be skliaustų) yra santykio ir aritmetinių operacijų, tai, palikę po tarpą abipus santykio operacijų, pabrėšime, kad jos atliekamos paskiausiai. Ta pačia taisykle reikia vadovautis ir tada, kai tenka dalį sakinio kelti į naują eilutę: geriau sakinį skaldyti į mažiau susijusias dalis. Pavyzdžiui:

if a - 12 = b*2
    then alfa := alfa*ilgis*(plotis + sigma) +
                      (a*b + c*c)
    else write('Galutiniai rezultatai ',
                    alfa*alfa - 128: 8,
                    ilgis + plotis)

Daugybos ir dalybos operacijų prioritetas aukštesnis (jos pirmiau atliekamos), negu sudėties ir atimties (jos paskiau atliekamos). Todėl abipus sudėties ir atimties operacijų simbolių reikėtų palikti po tarpą, o abipus daugybos ir dalybos – nepalikti. Tačiau padėti komplikuoja sveikųjų skaičių dalybos operacijos div ir mod, žymimos žodžiais. Nepalikus tarpų jie susilietų su gretimais kintamųjų (operandų) vardais. Todėl kai reiškinyje yra operacijos div arba mod tenka palikti tarpus abipus visų dviviečių operacijų simbolių.

Kitais atvejais tekstą išdėstant eilutėje reikėtų laikytis lietuvių kalbos rašybos taisyklių. Priimta tarpą palikti po dvitaškio, kabliataškio, kablelio, bet ne prieš šiuos simbolius. Programose ypač svarbu palikti tarpą po kabliataškio, nes jis skiria vieną sakinį nuo kito.

Programose kiek savitą paskirtį turi skliaustai. Paprastaisiais skliaustais suskliaudžiami funkcijos parametrai. Tarp funkcijos vardo atidarančio parametrų skliaustų tarpo palikti nereikia. Mat sąsaja tarp funkcijos vardo ir jos parametrų yra stipriausia (turi aukščiausią prioritetą. Paliktas tarpas, pavyzdžiui,

abs (a+b)*c

sudarytų klaidingą įspūdį, kad funkcijos parametras yra visas reiškinys ir reikia pirmiau padauginti ir po to skaičiuoti funkcijos reikšmę. Todėl šį pavyzdį reikia perrašyti šitaip:

abs(a+b) * c

Tarp suskliausto teksto ir skliaustų vidinių pusių lietuvių kalboje tarpai nepaliekami. Tačiau tarpai programose tarp komentarų ir juos gaubiančių figūrinių skliaustų padeda atskirti komentarus nuo skliaustų. Skliaustai atlieka lyg ir komentarų rėmelių vaidmenį, panašiai kaip ir pseudografikos simboliai.

Kartais rėmelių vaidmenį atlieka ir dvitaškiai aprašuose, pavyzdžiui,

var realusis  : real;
      seikasis : integer;
      s           : char;
      log         : boolean;

Plačiau apie programos teksto išdėstymą rašoma straipsnyje [7].

Uždaviniai

5.8.1. Pašalinkite nereikalingus simbolius iš programos

program simboliai;
  var a, b, i, s: integer;
begin
 
read(a);
  i := 0; s := 0;
  for i := 1 to a do
    begin
      
s := s+(i*i)*(i+1)
    end;
  writeln(s);
end.

5.8.2. Su žemiau pateikta programa atlikite šiuos veiksmus: 1) nustatykite, ką matysite
         ekrane atlikę šią programą, 2)sulygiuokite programos eilutes ir įterpkite reikalingus
         tarpus, 3) vėl nustatykite, ką matysite ekrane atlikę šią programą.

Kurį programos tekstą: čia pateiktą ar sutvarkytą, buvo lengviau suvokti?

program tvarka;
var i,s:integer;
begin s:=1;i:=10;
while i mod 7=0 do
i:=i - 1; s:= s+i * i;
if s > 25 then if s = 99 then s:=s +1 else
s:=s div i-2 else s:=s- 1;writeln(s)end.

5.9. Programos ekonomiškumas

Programa yra ekonomiška jeigu ji taupiai naudoja kompiuterio išteklius: laiką ir atmintinę.

Laikas. Sakinys į programą rašomas vieną kartą. Kai programa vykdoma, jis gali būti atliekamas vieną kartą, neatliekamas nė karto (jeigu, pavyzdžiui jis yra sąlyginiame sakinyje ir pakliuvo į nevykdomą šaką) arba atliekamas daug kartų (jeigu jis yra cikle). Aišku, kad apčiuopiamą naudą gali duoti daug kartų atliekamų sakinių vykdymo laiko ekonomija. Tarkime, kad sakinys S yra cikle, o tas ciklas dar kitame cikle:

for i := 1 to 1000 do
    for
j := 1 to 1000 do
       
S;

Sakinys S bus atliekamas milijoną kartų. Taigi, kiekviena sutaupyta mikrosekundė virs visa sekunde. Todėl reikia gerai apžiūrėti visus ciklus: ar tikrai jie reikalingi, ar cikluose nėra tokių veiksmų, kuriuos būtų galima iškelti iš ciklų.

1 pavyzdys. Programa nelyginių skaičių nuo 1 iki n sumai rasti.

program NelygSuma;
  var n,                     { intervalo pabaiga }
        suma,               { nelyginių skaičių suma }
        i: integer;
begin
 
suma := 0;
  read(n);
  for i := 1 to n do
    if
i mod 2 <> 0 then suma := suma + i;
  writeln(suma)
end.

Betgi iš eilės einantys nelyginiai skaičiai sudaro aritmetinę progresiją. O narių sumą galima apskaičiuoti pagal formulę

          n(a1+an)
sn = ------------------
               2

čia a1 – pirmas narys,
     an – paskutinis narys,
     sn – narių skaičius.

Pakeitę ciklą formule, gerokai sumažinsime programos darbo laiką.

2 pavyzdys. Tarkime kad turime šitokį programos fragmentą:

read(a);
s := 0;
for i := 1 to n do
   for
j := 1 to m do
     
s := s + a + sqrt(1/i + sqrt(1/j))

Kintamojo a reikšmė cikle nekeičiama. Todėl netikslinga jo reikšmę n× m kartų (tiek kartų atliekamas prieskyros sakinys) pridėti prie sumos s. Geriau pridėti vieną kartą, bet padaugintą iš n × m.

Reiškinio 1/i reikšmė priklauso nuo išorinio ciklo kintamojo i. Vidiniame cikle ji išlieka ta pati. Todėl jos skaičiavimą galima iškelti į išorinį ciklą.

Perrašome programos fragmentą.

read(a);
s := a*n*m;
for i := 1 to n do
   begin
      
t := 1/i;
       for j := 1 to m do
         
s := s + sqrt(t + sqrt(1/j))
   end

Programos tekstas pailgėjo, bet programos vykdymo laikas gerokai sutrumpėjo.

Tobulinant programą ekonomiškumo link nereikia persistengti. Tuos sakinius, nuo kurie atliekami nedaug kartų, geriau parašyti taip, kad jie būtų aiškesni skaitytojui, nors ir keliskart lėčiau atliekami. Kompiuteris to nepajus, o skaitytojas bus laimingas greičiau supratęs programą.

Atmintinė. Kiekvieno kintamojo reikšmei saugoti skiriama vieta kompiuterio atmintinėje. Tačiau viena mums jau žinomo paprastojo tipo (pvz., sveikųjų skaičių, realiųjų skaičių) kintamajam vietos reikia tiek mažai (kelių baitų), kad jų skaičiau mažinimas pastebimos ekonomijos neduos. Kas kita, kai programoje naudojamos didelės duomenų struktūros (žr. 7 skyr.) arba rekursija (žr. 8 skyr.). Todėl apie atmintinės ekonomiją kol kas neverta kalbėti.

Uždavinys

5.9.1. Parašykite programą nelyginių natūraliųjų skaičių sumai nuo 1 iki n rasti
         naudodamiesi aritmetinės progresijos formule.

Praktikos darbas

5.9.1. Eksperimentas. 2 pavyzdyje pateiktus abu programos fragmentus įdėkite į programas, išbandykite su kompiuteriu, kai n = 1000 ir m = 1000, nustatykite jų atlikimo laikus.

E Ankstesnis puslapis

3 Turinys

Kitas puslapis F

tus0.gif (69 bytes)