6.Prednaska

Z Pascal
Prejsť na: navigácia, hľadanie
6. Prednáška

úlohy | cvičenie


Typy v Pascale

V Pascale sme sa zoznámili s týmito typmi:

  • jednoduché typy:
    • ordinálny typ Integer - celé čísla
    • ordinálny typ Char - znaky
    • ordinálny typ Boolean - logické hodnoty
    • Real - desatinné čísla
  • štruktúrované typy – obsahujú iné typy
    • string – znakové reťazce ako postupnosti očíslovaných znakových premenných
    • TextFile – textové súbory ako postupnosti znakov – pristupujeme k nim sekvenčne

Pascal je prísny na typy (typový jazyk):

  • treba deklarovať premenné
  • hodnoty sa nemôžu miešať: pre operácie aj podprogramy treba dodržiavať typy operandov a parametrov
    • výnimkou je automatická konverzia: celé číslo sa niekedy prekonvertuje na desatinné


Vlastné typy


Okrem už definovaných typov môžeme zadefinovať aj vlastné typy. Zapisujeme to v deklaračnej časti programu (tam, kde definujeme premenné a podprogramy):

type
  meno typu = popis typu;

Meno typu je ľubovoľný identifikátor, ktorý nie je rezervovaným slovom a na danej úrovni deklarácií ešte nebol definovaný. K menám nových definovaných typov väčšinou budeme pridávať na začiatok písmeno T.

Za znak rovná sa = zapisujeme popis typu, ktorý je buď

  • identifikátor už existujúceho typu
  • súvislá časť už existujúceho typu (len pre ordinálne typy)
  • ďalšie špeciálne konštrukcie na tvorbu typov (napr. pole, záznam, množina a pod.)


Typ interval

Interval je taký typ, ktorý je odvodený (vytvorený) z nejakého už existujúceho ale len ordinálneho typu - tomuto už existujúcemu typu hovoríme bázový typ. Typ definujeme tak, že určíme minimálnu a maximálnu konštantu tohto nového typu. Napr. ak zapíšeme 1 .. 10 znamená to, že vytvárame podtyp celých čísel (lebo sú to celočíselné konštanty) a tento nový typ obsahuje všetky konštanty z intervalu <1, 10>. Automaticky zo svojho bázového typu (Integer) preberá všetky vlastnosti a operácie. Takto definovaný typ je tiež ordinálny typ.

Napr. takto môžeme zadefinovať nový typ a potom aj premennú tohto typu

type
  TInterval = 1..10;
var
  X: TInterval;

Premenná X môže nadobúdať len hodnoty z tohto intervalu. Ak by sme sa pokúsili do nej priradiť nejakú inú hodnotu (napr. 0), Pascal by hlásil chybu (predpokladáme, že máme zapnutú kontrolu v nastaveniach prekladača). Inak pracujeme s touto premennou rovnako ako s obyčajnou celočíselnou premennou. Nakoľko typ interval je tiež ordinálny typ, môžeme ho použiť, napr. aj vo for-cykle. Ešte je tu jeden rozdiel od typu Integer - typ interval môže v pamäti zaberať menej miesta ako jeho bázový typ, napr. v našom príklade premenná X zaberá iba jeden bajt.

type
  TMaleCislo = -100..100;
  TKladne = 1 .. 1000000;
  TRoky = 1900..2100;
  TCeleCislo = Integer;
  TBajt = 0..255;
  • tieto typy preberajú všetky operácie od Integer
  • s Integer sú kompatibilné a preto môžeme v jednom výraze miešať premenné, resp. ich navzájom priradzovať (ak je hodnota v prípustnom intervale)

Aké funkcie a príkazy fungujú s intervalovým typom:

  • funkcia SizeOf() – vráti počet bajtov
  • funkcie Low() a High() – vrátia minimálnu a maximálnu hodnotu
  • pre funkcie Pred() a Succ() sa kontroluje prípustný interval výslednej hodnoty
  • aj pre príkazy Inc() a Dec() sa kontroluje prípustný interval výslednej hodnoty
  • premenná sa môže použiť ako riadiaca (počítadlo) vo for-cykle

Pascal má niektoré celočíselné typy preddefinované:

type
  Longint = -2147483648 .. 2147483647;     // 4 bajty
  Integer = Longint;
  Byte = 0 .. 255;                         // 1 bajt
  Shortint = -128 .. 127;                  // 1 bajt
  Smallint = -32768 .. 32767;              // 2 bajty
  Word = 0 .. 65535;                       // 2 bajty
  Cardinal = 0 .. 4294967295;              // 4 bajty
 
  Int64 = -9223372036854775808 .. 9223372036854775807;   // 8 bajtov (-263 .. 263-1)

Typ interval môžeme odvodiť aj od iných ordinálnych typov, napr.

type
  TCifry = '0' .. '9';
  TPismena = 'A' .. 'Z';
  TMalePism = 'a' .. 'z';
  TZnaky = '  ' .. ‘~';       // alebo aj #32 .. #126

Aj typ Char by sme si mohli predstaviť ako keby bol definovaný intervalom:

type
  Char = #0 .. #255;                        // 1 bajt

Takýmto definíciám nového typu hovoríme priama definícia, lebo typ definujeme v odseku definície typov. Typ môžeme definovať aj nepriamo pri deklarovaní premennej, napr.

var
  MalePis: 'a'..'z';

Používanie typu interval nám niekedy môže aj dosť skomplikovať našu prácu. Napr. tento jednoduchý program funguje úplne správne:

var
  I: 1..10;
begin
  for I := 1 to 10 do
    WriteLn(I);
  ReadLn;
end.

Program vypíše 10 čísel od 1 do 10. Ak by sme ho prepísali pomocou while-cyklu, tak ako sme zvyknutí, program prestane fungovať a spadne s chybovou správou:

var
  I: 1..10;
begin
  I := 1;
  while I <= 10 do
  begin
    WriteLn(I);
    Inc(I);
  end;
  ReadLn;
end.

Program pri poslednom prechode cyklom najprv vypíše hodnotu 10 a potom sa chystá zvýšiť obsah premennej I o 1. Toto ale spôsobí prekročenie povoleného rozsahu premennej I, a program preto spadne. Uvedomte si, že testovanie I <= 10 v podmienke while-cyklu nemá v tomto prípade zmysel, keďže premenná I aj tak nemôže nadobúdať väčšiu hodnotu ako 10.


Zložený typ pole

Definujeme:

type
  meno typu = array [typ indexu] of typ_prvkov;

Pole sa skladá z veľa "jednoduchších" premenných, tzv. prvkov poľa. Všetky tieto prvky sú rovnakého typu. Pristupujeme k nim (selekcia) pomocou, tzv. indexu. Typom indexu môže byť ľubovoľný ordinálny typ. Typom prvkov poľa môže byť ľubovoľný typ. Štruktúra pole v pamäti zaberá toľko miesta, koľko zaberá jeden prvok krát počet prvkov poľa, t.j. počet rôznych hodnôt typu indexu. Napr.

type
  TMojePole = array [1..100] of Integer;

definuje pole, ktoré bude mať 100 prvkov (100 celočíselných premenných) a keďže každý prvok zaberá 4 bajty, celé toto pole zaberá 400 bajtov. FreePascal dovolí zadeklarovať maximálne 2 GB štruktúru, ale Windows má k dispozícii pre aplikáciu väčšinou len niekoľko desiatok až stovák MB. Napr.

type
  Typ1 = array [Integer] of Byte;  // zaberá presne 4 GiB - to je už veľa
  Typ2 = array [Byte] of Integer;  // 1 KiB (1024 B)
  Typ3 = array [Char] of Boolean;  // 0,25 KiB (256 B)

Štandardná funkcia SizeOf pre ľubovoľný identifikátor typu alebo premennej vráti počet bajtov, ktoré zaberá. Napr.

SizeOf(Typ2) vráti 1024
SizeOf(Integer) vráti 4

Zapamätajte si:

1 B = bajt (8 bitov)
1 KB = kilo bajt = 1000 bajtov
1 MB = mega bajt = 1000 KB = 1000*1000 B = milión bajtov
1 GB = giga bajt = 1000 MB = 1000*1000 KB = 1000*1000*1000 B = miliarda bajtov

Okrem týchto jednotiek existujú aj binárne jednotky

1 KiB = kibi bajt = 1024 bajtov
1 MiB = mebi bajt = 1024 KiB = 1024*1024 B = 1048576 bajtov (trochu viac ako milión bajtov)
1 GiB = gibi bajt = 1024 MiB = 1024*1024 KiB = 1024*1024*1024 B = 1073741824 bajtov (trochu viac ako miliarda bajtov)

Ak zadeklarujeme

var
  P: TMojePole;

označuje to jednu premennú P typu TMojePole. Už vieme, že táto premenná zaberá 400 bajtov, lebo v sebe obsahuje 100 celočíselných premenných (100 prvkov). Ku každému prvku pristupujeme pomocou indexu, t.j. pomocou hodnoty z prípustného intervalu podľa deklarácie - v našom prípade indexom musí byť hodnota z intervalu 1..100. Ak chceme pracovať so samostatným prvkom, index zapisujeme do hranatých zátvoriek, napr.

  P[5] := 37;

zmení obsah 5-teho prvku. S prvkami poľa pracujeme úplne rovnako ako s obyčajnými premennými (okrem toho, že ich nesmieme použiť ako premennú for-cyklu).

Ukážme program, ktorý bude pracovať so 7-prvkovým poľom reálnych čísel. Napr. týchto 7 čísel môže znamenať 7 nameraných teplôt v priebehu jedného týždňa:

var
  Pole: array [1..7] of Real;
  I: Integer;
begin
  Pole[1] := 22.3;
  Pole[2] := 17;
  Pole[3] := 18.5;
  Pole[4] := 19.1;
  Pole[5] := 20;
  Pole[6] := 22.1;
  Pole[7] := 24.5;
  for I := 1 to 7 do
    WriteLn(I, '. den teplota ', Pole[I]:0:1);
  ReadLn;
end.

Vidíme, ako do prvkov poľa najprv priraďujeme nejaké hodnoty a ako potom pomocou for-cyklu tieto hodnoty vypisujeme. Vo for-cykle sme využili počítadlo cyklu - premennú I na indexovanie prvkov poľa.

Ak počet prvkov nie je veľký, môžeme tieto hodnoty priradiť nielen pomocou príkazu priradenia, ale môžeme priamo inicializovať obsah poľa už počas deklarácie. Vtedy do okrúhlych zátvoriek vymenujeme toľko konštánt, koľko má prvkov zadeklarované pole. Napr.

var
  Pole: array [1..7] of Real = (22.3, 17, 18.5, 19.1, 20, 22.1, 24.5);
  I: Integer;
  Sucet, Priemer: Real;
begin
  for I := 1 to 7 do
    WriteLn(I, '. den teplota ', Pole[I]:0:1);
  Sucet := 0;
  for I := 1 to 7 do
    Sucet := Sucet + Pole[I];
  Priemer := Sucet / 7;
  WriteLn('Priemer = ', Priemer:0:2);
  ReadLn;
end.

Zároveň sme do programu pridali aj výpočet priemernej teploty za celý týždeň.

Ak by sme potrebovali pracovať s iným počtom nameraných hodnôt, musíme meniť nielen deklarácie programu, ale aj všetky výskyty konštanty 7, ktorá vyjadruje počet prvkov poľa. Jednou z možností je zadefinovanie konštanty, napr. N, ktorá bude vyjadrovať počet prvkov poľa. Zároveň program doplníme o generovanie náhodných hodnôt do poľa Pole a tiež o výpis ku každému prvku poľa o tom, či je táto hodnota priemerná, podpriemerná alebo nadpriemerná:

const
  N = 10;
var
  Pole: array [1..N] of Real;
  I: Integer;
  Sucet, Priemer: Real;
begin
  for I := 1 to N do
    Pole[I] := Random(100) / 10 + 15;
  Sucet := 0;
  for I := 1 to N do
    Sucet := Sucet + Pole[I];
  Priemer := Sucet / N;
  WriteLn('Priemer = ', Priemer:0:2);
  for I := 1 to N do
    if Pole[I] = Priemer then
      WriteLn(I:3, '. den teplota ', Pole[I]:0:1, ' priemer')
    else if Pole[I] < Priemer then
      WriteLn(I:3, '. den teplota ', Pole[I]:0:1, ' podpriemer')
    else if Pole[I] > Priemer then
      WriteLn(I:3, '. den teplota ', Pole[I]:0:1, ' nadpriemer');
  ReadLn;
end.

Takýto zápis programu je už univerzálnejší: stačí zmeniť hodnotu konštanty N a program bude stále fungovať správne.

V tomto programe sme použili porovnanie Pole[I] = Priemer. Už vieme, že keď porovnávame dve reálne čísla na rovnosť, nepresnosť reálnej aritmetiky môže niekedy dávať nesprávne výsledky. Preto v ďalšom upravíme aj toto porovnanie.

Pre typ pole existuje ešte jedna možnosť, ako môžeme zabezpečiť správne fungovanie programu aj po zmene hraníc poľa. Štandardné funkcie Low, High a Length (poznáme ich s prácou s ordinálnymi typmi a reťazcami) fungujú aj pre polia. Ak ako parameter uvedieme identifikátor typu pole (napr. TMojePole), alebo premennú typu pole, tak tieto funkcie vrátia:

  • Low(Pole) - dolnú hranicu indexu poľa (v našom prípade 1)
  • High(Pole) - hornú hranicu indexu poľa (v našom prípade 10)
  • Length(Pole) - počet prvkov poľa (v našom prípade 10)
    • vo všeobecnosti platí: Length(Pole) = High(Pole) - Low(Pole) + 1

Upravíme program s teplotami tak, aby bolo vidieť použitie týchto funkcií. Pridali sme prečítanie nameraných hodnôt so súboru.

type
  TIndex = 10..30;
  TPole = array [TIndex] of Real;
var
  Pole: TPole;
  I: TIndex;
  Sucet, Priemer: Real;
  Subor: TextFile;
begin
  AssignFile(Subor, 'teploty.txt');
  Reset(Subor);
  for I := Low(Pole) to High(Pole) do
    Read(Subor, Pole[I]);
  CloseFile(Subor);
  Sucet := 0;
  for I := Low(Pole) to High(Pole) do
    Sucet := Sucet + Pole[I];
  Priemer := Sucet / Length(Pole);
  WriteLn('Priemer = ', Priemer:0:2);
  for I := Low(Pole) to High(Pole) do
    if Abs(Pole[I] - Priemer) < 0.1 then
      WriteLn(I:3, '. den teplota ', Pole[I]:0:1, ' priemer')
    else if Pole[I] < Priemer then
      WriteLn(I:3, '. den teplota ', Pole[I]:0:1, ' podpriemer')
    else if Pole[I] > Priemer then
      WriteLn(I:3, '. den teplota ', Pole[I]:0:1, ' nadpriemer');
  ReadLn;
end.

Všimnite si, že sme úmyselne zadefinovali aj spodnú hranicu indexu poľa rôznu od 1. Preto sme všetky cykly upravili tak, aby išli od dolného indexu Low(Pole) až po horný High(Pole). Dolná hranica indexu poľa sa v programoch využíva veľmi zriedkavo, nakoľko ju programátori veľmi zriedkavo menia (na rozdiel od hornej hranice). Uvedomte si, že v tomto programe sme mohli všetky for-cykly zapísať aj inak:

  for I := Low(TPole) to High(TPole) do
    ...
  for I := Low(TIndex) to High(TIndex) do
    ...
  for I := Low(I) to High(I) do
    ...

Posledný zápis for-cyklu funguje len vďaka tomu, že sme premennú I zadeklarovali ako typ interval a ten je rovnaký ako typ indexov poľa.


Polia ako parametre

Pascal umožňuje pre prácu s poliami veľmi efektívne využívať aj podprogramy. Ukážeme to na takomto príklade: v 1000-prvkovom poli celých čísel uchovávame informácie o počte obyvateľov v 1000 domoch nejakej obce. Pre jednoduchosť vygenerujme hodnoty prvkov náhodne (ako číslo od 0 do 9) a vypíšme tieto hodnoty:

var
  Dom: array [1..1000] of Integer;
  I: Integer;
begin
  for I := 1 to 1000 do
    Dom[I] := Random(9);
  for I := 1 to 1000 do
    Write(Dom[I], ' ');
  WriteLn;
  ReadLn;
end.

Po spustení program vypíše 1000 náhodných čísel.

Prepíšme teraz tento program tak, že využijeme podprogramy. Najprv zapíšme procedúru, ktorá vypíše obsah poľa. Bude mať jeden parameter a to samotné pole, ktoré treba vypísať:

procedure Vypis(Pole: array [1..1000] of Integer);
var
  I: Integer;
begin
  for I := 1 to 1000 do
    Write(Pole[I], ' ');
  WriteLn;
end;

V Pascale parametre takto ale fungovať nebudú: pri definovaní parametrov (alebo typu výsledku funkcie) nesmieme použiť nepriamu definíciu typu. Typ treba najprv zadefinovať priamo a až potom v deklarácii procedúry použijeme jeho meno. Napr. toto je chybné použitie typov parametrov:

procedure A(B: array [1..10] of Char);
procedure X(Y: 1..10);
function F(Z: Char): array [1..10] of Char;

Správne sme mali najprv zadefinovať nové typy a ich identifikátory použiť v deklarácii podprogramov, napr.

type
  TPoleZnakov = array [1..10] of Char;
  TInterval = 1..10;
 
procedure A(B: TPoleZnakov);
procedure X(Y: TInterval);
function F(Z: Char): TPoleZnakov;

Opravíme definíciu procedúry Vypis a zároveň pridáme aj funkciu na generovanie náhodných hodnôt do poľa:

type
  TPole = array [1..1000] of Integer;
 
procedure Vypis(Pole: TPole);
var
  I: Integer;
begin
  for I := 1 to 1000 do
    Write(Pole[I], ' ');
  WriteLn;
end;
 
function Nahodne(N: Integer): TPole;
var
  I: Integer;
begin
  for I := 1 to 1000 do
    Result[I] := Random(N);
end;
 
var
  Dom: TPole;
begin
  Dom := Nahodne(9);
  Vypis(Dom);
  ReadLn;
end.

Vieme, že parametre podprogramov môžu byť viacerých typov. Platí to aj pre typ pole a práve pre typ pole to musíme veľmi dôkladne zvážiť:

  • var-parameter - nevyhradzuje sa žiadna nová pamäť, ale podprogram priamo manipuluje s premennou vstupného parametra, teda s celým poľom
  • const-parameter - podobne ako var-parameter, len je zakázané v podprograme toto pole meniť, t.j. do neho niečo priraďovať
  • hodnotový parameter - pri volaní podprogramu sa vytvorí duplikát celého poľa (rovnakej veľkosti) a vďaka tomu môžeme modifikovať tento duplikát (lokálnu premennú) bez toho, aby sa menila premenná vstupného (skutočného) parametra. Na použitie tohto typu parametra pre pole treba dávať veľký pozor: okrem toho, že spomaľuje výpočet, veľmi míňa pamäť pre lokálne premenné (tzv. systémový zásobník) a často program na tomto aj hlási chyby.

V našom príklade sme procedúry Vypis definovali s hodnotovým parametrom typu pole. To znamená, že pri volaní tejto procedúry sa najprv vytvorí kópia celého 1000-prvkového poľa, procedúra ďalej pracuje len s touto kópiou (teda ju vypíše) a po skončení práce, túto dočasnú kópiu zruší. Zrejme to bude takto fungovať správne, ale je to veľmi neefektívne. Ak by sme použili typ parametra const, procedúra by pracovala rovnako správne, ale nevytvárala by sa žiadna zbytočná kópia celého poľa.

Na príklade vidíme, že aj výsledkom funkcie môže byť typ pole, ale tiež musí byť zadaný identifikátorom typu. Pripomeňme si, ako funguje volanie funkcie a konkrétne, ako to bude fungovať pre výsledok typu pole:

  • vyhradia sa všetky lokálne premenné (aj hodnotové parametre)
  • vyhradí sa jedna špeciálna lokálna premenná Result, ktorá je rovnakého typu ako typ funkcie, teda pole - táto premenná má zatiaľ nedefinovanú hodnotu
  • s touto premennou pracujeme vo funkcii ako s iným bežným poľom
  • po skončení výpočtu funkcie sa "zabudnú", t.j. uvoľnia všetky lokálne premenné, len hodnota Result sa stane výsledkom celej funkcie.

Doprogramujme ešte niekoľko podprogramov, aby sme videli rôzne možnosti práce s poľom:

type
  TPole = array [1..1000] of Integer;
 
procedure Vypis(const Pole: TPole);
var
  I: Integer;
begin
  for I := 1 to 1000 do
    Write(Pole[I], ' ');
  WriteLn;
end;
 
procedure Vypis(var Subor: TextFile; const Pole: TPole);
var
  I: Integer;
begin
  for I := 1 to 1000 do
  begin
    Write(Subor, Pole[I], ' ');
    if I mod 40 = 0 then
      WriteLn(Subor);
  end;
  WriteLn(Subor);
end;
 
function Nahodne(N: Integer): TPole;
var
  I: Integer;
begin
  for I := 1 to 1000 do
    Result[I] := Random(N);
end;
 
function Citaj(var Subor: TextFile): TPole;
var
  I: Integer;
begin
  for I := 1 to 1000 do
    Read(Subor, Result[I]);
  ReadLn(Subor);
end;
 
procedure Inc(var Pole: TPole);
var
  I: Integer;
begin
  for I := 1 to 1000 do
    Pole[I] := Pole[I] + 1;                    // System.Inc(Pole[I]);
end;
 
var
  Dom: TPole;
  Text: TextFile;
begin
  AssignFile(Text, 'dedina.txt');
  Reset(Text);
  Dom := Citaj(Text);
  CloseFile(Text);
  Inc(Dom);
  Vypis(Dom);
  AssignFile(Text, 'dedina1.txt');
  Rewrite(Text);
  Vypis(Text, Dom);
  CloseFile(Text);
  ReadLn;
end.

Všimnite si, že sme vytvorili funkciu Inc na zvýšenie hodnôt všetkých prvkov v poli. Tým sme ale prekryli pôvodný štandardný identifikátor funkcie Inc a preto ho už v pôvodnom význame používať nemôžeme - v komentári môžete vidieť, ako sa tento problém aj tak dá vyriešiť. Tiež si všimnite, že procedúru Vypis sme definovali v dvoch rôznych variantoch: ak má iba jeden parameter, vypisuje ako text konzolovej aplikácie, ak má dva parametre (prvý je textový súbor a druhý je pole), tak vypisuje do otvoreného textového súboru.

Ďalej vyriešime niekoľko menších úloh s takýmto poľom domov. Nasledujúci program zistí nielen počet obyvateľov v celej obci, ale aj počet prázdnych domov (s nulovou hodnotou). Tu už neuvádzame skôr definované podprogramy (procedúru Inc treba zrušiť, inak nebude fungovať Inc(Pocet)):

var
  Dom: TPole;
  I, Pocet, Pocet0: Integer;
begin
  Dom := Nahodne(9);
  Vypis(Dom);
  WriteLn('============');
  Pocet := 0;
  Pocet0 := 0;
  for I := 1 to 1000 do
  begin
    Inc(Pocet, Dom[I]);
    if Dom[I] = 0 then
      Inc(Pocet0);
  end;
  WriteLn('Pocet obyvatelov = ', Pocet);
  WriteLn('Pocet prazdnych domov = ', Pocet0);
  ReadLn;
end.

Ak by sme potrebovali zistiť, koľko je prázdnych domov, v koľkých domoch býva len 1 obyvateľ, v koľkých dvaja, atď. použili by sme tzv. frekvenčnú tabuľku: pre každý počet obyvateľov v dome použijeme jeden prvok pomocného poľa Pocty - Pocty[0] označuje počet prázdnych domov, Pocty[1] je počet domov s 1 obyvateľmi, Pocty[2] je počet domov, kde bývajú dvaja, atď. Program môžeme zapísať aj pomocou pomocnej funkcie:

type
  TPole = array [1..1000] of Integer;
  TTabulka = array [0..8] of Integer;
 
function PocetObyvatelov(const Pole: TPole): Integer;
 var
  I: Integer;
begin
  Result := 0;
  for I := 1 to High(Pole) do
    Inc(Result, Pole[I]);
end;
 
function ZistiPoctyDomov(const Pole: TPole): TTabulka;
 var
  I: Integer;
begin
  for I := 0 to 8 do
    Result[I] := 0;
  for I := 1 to High(Pole) do
    Inc(Result[Pole[I]]);
end;
 
procedure Vypis(const Pole: TPole);
var
  I: Integer;
begin
  for I := 1 to High(Pole) do
    Write(Pole[I], ' ');
  WriteLn;
end;
 
function Nahodne(N: Integer): TPole;
var
  I: Integer;
begin
  for I := 1 to High(Pole) do
    Result[I] := Random(N);
end;
 
var
  Dom: TPole;
  I: Integer;
  FrekvTabulka: TTabulka;
begin
  Dom := Nahodne(9);
  Vypis(Dom);
  WriteLn('============');
  WriteLn('Pocet obyvatelov = ', PocetObyvatelov(Dom));
  FrekvTabulka := ZistiPoctyDomov(Dom);
  for I := 0 to 8 do
    WriteLn('Pocet domov s ', I, ' obyvatelmi je ', FrekvTabulka[I]);
  ReadLn;
end.

Dobre si uvedomte, čo urobí príkaz Inc(Result[Pole[I]]); vo funkcii ZistiPoctyDomov.

Vďaka tomu, že sme pri práci s premennou Pole zapísali for-cykly ako for I := 1 to High(Pole) do, program bude správne fungovať, aj keď zmeníme hornú hranicu indexu poľa v deklaráciách TPole.


Polia rôznych typov

Typ prvkov poľa môže byť ľubovoľný existujúci typ. Vo väčšine doterajších príkladov sme sa zameriavali na polia celých a reálnych čísel. Stručne na príkladoch ukážeme využitie niektorých iných typov prvkov polí. Programy v tejto časti bežia ako konzolová aplikácia.



Pole znakov


Pole znakov má niekoľko výnimiek oproti iným poliam:

  • polia znakov je možné vypísať do súboru (resp. do textovej plochy) pomocou jedného volania Write, resp. WriteLn
  • do poľa môžeme jedným priradením priradiť aj konštantu znakový reťazec: ak je táto konštanta kratšia ako dĺžka poľa, tak sa pole doplní nulovými znakmi (#0), ak je táto konštanta dlhšia, prebytočné znaky sa zanedbajú;
  • znakové pole môžeme aj prečítať zo súboru (resp. vstupného riadku konzolovej aplikácie) jedným príkazom Read, resp. ReadLn, ak je v momentálnom riadku súboru menej znakov ako je dĺžka poľa, toto sa doplní nulovými znakmi (#0)
var
  S: array [3..10] of Char;
  I: Integer;
begin
  S := 'abcdefghij';
  WriteLn('"', S, '"');
  S := 'abc';
  WriteLn('"', S, '"');
  Write('Zadaj:');
  ReadLn(S);
  Write('"', S, '"');
  for I := Low(S) to High(S) do
    Write(' ', ord(S[I]));
  ReadLn;
end.

Môžete vidieť, ako funguje priradenie dlhšieho reťazca, kratšieho reťazca a prečítanie (ReadLn) zo vstupu. Ak sme na vstupe zadali pascal, vidíme takýto výstup:

"abcdefgh"
"abc     "
Zadaj:pascal
"pascal  " 112 97 115 99 97 108 0 0



Pole číslic


Celé číslo môžeme rozložiť po cifrách do prvkov poľa. Takto môžeme reprezentovať aj oveľa väčšie čísla ako povoľuje Integer (hoci aj 1000-ciferné). Keď zvládneme naprogramovať niektoré aritmetické operácie nad takýmito veľkými číslami, môžeme veľmi presne uskutočniť niektoré výpočty, s ktorými sú inak problémy, napr. faktoriál, Fibonacciho čísla a pod. Nasledujúci program prečíta celé číslo, prevedie ho do poľa cifier, vynásobí ho dvoma a vypíše bez úvodných núl:

type
  TVelkeCislo = array [1..1000] of 0..9;
var
  Cislo: TVelkeCislo;
  I, Prenos, N: Integer;
begin
  Write('Zadaj cislo: ');
  ReadLn(N);
  for I := 1 to High(Cislo) do
  begin
    Cislo[I] := N mod 10;
    N := N div 10;
  end;
  // násobenie dvomi
  Prenos := 0;
  for I := 1 to High(Cislo) do
  begin
    N := 2 * Cislo[I] + Prenos;
    Cislo[I] := N mod 10;
    Prenos := N div 10;
  end;
  //výpis
  I := High(Cislo);
  while (I > 1) and (Cislo[I] = 0) do
    Dec(I);
  for I := I downto 1 do
    Write(Cislo[I]);
  WriteLn;
  ReadLn;
end.



Frekvenčná tabuľka


Ak si uvedomíme, že znakový typ (Char) je vlastne tiež interval 256 znakových hodnôt #0 .. #255, môžeme zadefinovať pole, ktorého indexy sú všetky znaky z tohto intervalu. Nasledujúci program postupne číta všetky znaky nejakého textového súboru a pre každý si v poli V eviduje, koľkokrát sa v súbore vyskytol. Na záver postupne vypíše počty výskytov malých písmen od 'a' do 'z' a tiež počet výskytov znaku #13, čo je vlastne počet riadkov súboru:

var
  V: array [Char] of Integer;
  Z: Char;
  Subor: TextFile;
begin
  for Z := Low(Char) to High(Char) do  // t.j. for Z := #0 to #255 do
    V[Z] := 0;
  AssignFile(Subor, 'Project1.pas');
  Reset(Subor);
  while not Eof(Subor) do
  begin
    Read(Subor, Z);
    Inc(V[Z]);    // V[Z] := V[Z] + 1;
  end;
  CloseFile(Subor);
  for Z := 'a' to 'z' do
    WriteLn(Z, ' ... ', V[Z]);
  WriteLn('pocet riadkov ', V[#13]);
end;



Pole reťazcov


Do poľa reťazcov môžeme prečítať obsah celého súboru:

type
  TPole = array [1..1000] of string;
var
  Retazce: TPole;
  Subor: TextFile;
  N: Integer;
begin
  AssignFile(Subor, 'subor.txt');
  Reset(Subor);
  N := 1;
  while (N <= High(TPole)) and not Eof(Subor) do
  begin
    Readln(Subor, Retazce[N]);
    Inc(N);
  end;
  CloseFile(Subor);
  ...
end;

Ak máme v poli reťazcov uložené mená nejakých študentov, môžeme v ňom vyhľadať meno študenta, ktorý je prvý v abecede:

var
  Studenti: array [1..100] of string;
  Prvy: string;
  I: Integer;
begin
  ...
  Prvy := Studenti[1];
  for I := 2 to High(Studenti) do
    if Prvy > Studenti[I] then
      Prvy := Studenti[i];
  WriteLn('prvy v abecede je ', Prvy);
  ...
end;



Pole logických hodnôt


Pole logických hodnôt (Boolean) použijeme pri hľadaní prvočísel pomocou Eratostenovho sita (pozri aj stránky v angličtine Sieve of Eratosthenes).

Eratostenovo sito je algoritmus, ktorý hľadá všetky prvočísla do nejakej hodnoty metódou, ktorú vymyslel grécky matematik ešte približne 2 storočia pred našim letopočtom. Princíp algoritmu je nasledovný: vypíšeme si všetky čísla od 2 do nejakej maximálnej hodnoty (napr. 10000). Zoberieme prvé z nich (teda číslo 2) a všetky jeho násobky označíme, že už určite prvočíslami nebudú. Zoberieme ďalšie najmenšie ešte neoznačené číslo (teda číslo 3) a opäť označíme všetky jeho násobky. Takto pokračujeme so všetkými neoznačenými číslami. To čo ostane neoznačené, sú hľadané prvočísla.

Pre tento algoritmus použijeme 10000-prvkové logické pole. Hodnota True bude označovať, že je to kandidát na prvočíslo. Hodnota False znamená, že sme už príslušné číslo označili ako nejaký násobok a teda už to prvočíslo určite nebude. Program najprv inicializuje celé pole – okrem 1 všetkým prvkom nastavíme True. Potom postupne prechádzame všetky prvky a ak majú hodnotu True, tak vo while-cykle označíme všetky jeho násobky ako False.

var
  ErSito: array [1..10000] of Boolean;
  I, J: Integer;
begin
  ErSito[1] := False;
  for I := 2 to High(ErSito) do
    ErSito[I] := True;
  I := 2;
  while I * I <= High(ErSito) do     // I <= Sqrt(High(ErSito))
  begin
    if ErSito[I] then
    begin
      J := 2 * I;
      while J <= High(ErSito) do
      begin
        ErSito[J] := False;
        Inc(J, I);
      end;
    end;
    Inc(I);
  end;
  for I := 1 to High(ErSito) do
    if ErSito[I] then
      Write(I, ' ');
  WriteLn;
  ReadLn;
end.

Všimnite si podmienku vo vonkajšom while-cykle, na základe ktorej prechádzame len tie hodnoty, ktoré nie sú väčšie ako odmocnina z maximálnej hranice poľa (t.j. len do 100). Vďaka tomu, že v programe pracujeme s High(ErSito), môžeme v deklarácii poľa zmeniť hornú hranicu, napr. aj na 1000000000 a program bude pracovať správne (hoci mu môže dosť dlho trvať).



Pole súborov


Program zistí, v ktorých z piatich súborov ('subor1.txt', 'subor2.txt', 'subor3.txt', 'subor4.txt', 'subor5.txt') sa nachádza reťazec 'program':

var
  Subor: array [1..5] of TextFile;
  Riadok: string;
  I: Integer;
begin
  for I := 1 to 5 do
  begin
    AssignFile(Subor[I], 'subor' + IntToStr(I) + '.txt');
    Reset(Subor[I]);
  end;
 
  for I := 1 to 5 do
    while not Eof(Subor[I]) do
    begin
      Readln(Subor[I], Riadok);
      if Pos('program', Riadok) <> 0 then
      begin
        WriteLn('subor', I, '.txt obsahuje slovo program');
        Break;
      end;
    end;
 
  for I := 1 to 5 do
    CloseFile(Subor[I]);
end.


späť | ďalej