6. Polia


Posledná zmena: 13.10.2002

Banner Text 8.10.2002

    čo sme sa doteraz naučili

    • poznáme niektoré jednoduché typy - integer, char, boolean a real
    • poznáme aj niektoré zložené typy - string a TextFile (pravdepodobne aj objekt korytnačka je zložený typ)

    čo sa budeme dnes učiť

    • zoznámime sa s novým jednoduchým typom - interval a s novým zloženým typom - pole
    • naučíme sa, ako sa pracuje s poľom, ktoré je parametrom podprogramu
    • uvidíme príklady na pole korytnačiek

Typ interval

  • interval je odvodený z bázového typu  (tento môže byť len ordinálny typ),
    • napr. integer 1..10 (1 a 10 - konštanty bázového typu),
    • definovanie: min..max kde min<=max
  • preberá vlastnosti a operácie bázového typu; interval je ordinálny typ
  • premenná, ktorá je tohto typu, môže nadobúdať len hodnoty z intervalu
  • nepriama definícia
      var x:1..10;
  • priama definícia (v odseku definície typov):
    • type
        interval= 1..10;     //  type identifikátor_typu = popis_typu;
        cele    = integer;
        male    = byte;
        mpis    = 'a'..'z';
        cifry   = '0'..'9';

  • niektoré jednoduché typy, predefinované v Pascale:
    • type
        Integer  = -2147483648 .. 2147483647;
        Longint  = -2147483648 .. 2147483647;  // kvôli kompatibilite s Turbo pascalom
        Smallint = -32768 .. 32767;
        Shortint = -128 .. 127;
        Byte     = 0 .. 255;
        Word     = 0 .. 65535;
        Cardinal = 0 .. 4294967295;
        Int64    = -263 .. 263-1;

        Char     = #0 .. #255;

 

Štruktúrovaný (zložený) typ POLE

type pole = array[typ_indexu] of typ_prvkov;

  • všetky prvky poľa sú rovnakého typu = pole sa skladá z veľa "jednoduchších" premenných
  • tieto premenné sú prístupné cez index (selektovanie)
  • typ_indexu - ľubovoľný ordinálny typ
  • typ_prvkov - ľubovoľný typ
  • už neplatí, že celá štruktúra musí zaberať menej ako 64 KB - ako to bolo v Turbo pascale
  • obmedzenie na veľkosť je len také, aby to zvládal Windows a ten bez problémov zvládne dosť veľa MB
  • Delphi dovolí zadeklarovať maximálne 2 GB štruktúru, ale Windows má k dispozícii pre aplikáciu väčšinou len niekoľko MB

array[integer] of byte; - zaberá presne 4 GB - to je už veľa
array[byte] of integer; - 1 KB (1024 B)
array[char] of boolean; - 0,25 KB (256 B)

zapamätajte si:

  • 1 B = bajt (8 bitov)
  • 1 KB = kilo bajt = 1024 bajtov (trochu viac ako tisíc bajtov)
  • 1 MB = mega bajt = 1024 KB = 1024*1024 B = 1048576 bajtov (trochu viac ako milión bajtov)
  • 1 GB = giga bajt = 1024 MB = 1024*1024 KB = 1024*1024*1024 B = 1073741824 bajtov (trochu viac ako miliarda bajtov)
  • počet prvkov poľa musí byť známy pri kompilácii
  • všetky prvky poľa sa nedajú naraz načítať ani vypísať jediným príkazom read, write - treba to rozpísať, napr. pomocou for-cyklu
  • Príklad: Program, ktorý o každom prvku načítaného poľa vypíše, či je jeho hodnota nad priemerom alebo pod priemerom všetkých vstupných prvkov, resp. či je to priemer:

jednoduchá práca s poľom celých čísel:

const
  max=7;
type
  index=1..max;
  pole=array[index] of integer;
var
  p:pole;
  i:index;
  s:integer;
  t:TextFile;
begin
  AssignFile(t,'text.txt'); Reset(t);
  for i:=1 to max do read(t,p[i]);
  CloseFile(t);
  AssignFile(t,'vypis.txt'); Rewrite(t);
  s:=0;
  for i:=1 to max do inc(s,p[i]);
  writeln(t,'Priemer: ',s/max:0:2);
  for i:=1 to max do
    if p[i]<s/max then writeln(t,p[i],' pod')
    else if p[i]>s/max then writeln(t,p[i],' nad')
    else writeln(t,p[i],' priemer');
  CloseFile(t);
end;

 niekoľko vylepšení:

  • napriek tomu, že si nebudeme v nejakej špeciálnej konštante (max) pamätať počet prvkov poľa, môžeme tento program prepísať, tak aby fungoval správne aj pre zmenený interval indexov poľa
  • použijeme pomocné funkcie low, high a length:
    • tieto fungujú pre typ alebo premennú typu pole, ale aj pre ľubovoľné ordinálne typy a premenné
    • funkcia high - vráti pre pole index maximálneho prvku, pre ordinálny typ vráti maximálnu hodnotu
    • funkcia low - vráti pre pole index minimálneho prvku, pre ordinálny typ vráti minimálnu hodnotu
    • funkcia length funguje len pre pole a string - pre pole vráti počet prvkov, pre string momentálnu dĺžku
  • všimnite si, že p[i]<s/max sme nahradili p[i]*length(p)<s - tento počíta to isté, ale pracuje len v celých číslach

bez použitia reálnej aritmetiky:

type
  pole=array[-1..5] of integer;    // počet prvkov je 7
var
  p:pole;
  i,s:integer;
  t:TextFile;
begin
  AssignFile(t,'text.txt'); Reset(t);
  for i:=low(p) to high(p) do read(t,p[i]);
  CloseFile(t);
  AssignFile(t,'vypis.txt'); Rewrite(t);
  s:=0;
  for i:=low(p) to high(p) do inc(s,p[i]);
  writeln(t,'Priemer: ',s/length(p):0:2);
  for i:=low(p) to high(p) do
    if p[i]*length(p)<s then writeln(t,p[i],' pod')
    else if p[i]*length(p)>s then writeln(t,p[i],' nad')
    else writeln(t,p[i],' priemer');
  CloseFile(t);
end;

Polia ako parametre podprogramov

  • formálne parametre typu pole môžu byť troch typov:
    • var-parameter - nevyhradzuje sa žiadna nová pamäť, ale podprogram priamo manipuluje so skutočným parametrom
    • const-parameter - podobne ako var-parameter, len sa nám nedovolí v podprograme to takéhoto poľa 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 menil skutočný parameter
      • treba dávať pozor na použitie tohto typu formálneho parametra, ak je to pole, lebo spomaľuje výpočet, veľmi míňa pamäť pre lokálne premenné a často program na tomto aj hlási chyby
  • pri definovaní formálnych parametrov nesmieme nepriamo definovať nový typ, t.j. môžeme uvádzať len identifikátory typov
  • typ pole môže byť aj výsledkom funkcie (typ funkcie musí byť vždy zadaný identifikátorom typu!)
    • túto situáciu si môžeme predstaviť tak, že volanie funkcie spôsobí:
      • vyhradia sa všetky lokálne premenné (aj hodnotové formálne 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
      • po skončení výpočtu funkcie sa "zabudnú" - uvolnia všetky lokálne premenné, len hodnota Result sa stane výsledkom celej funkcie

Príklad: procedúra presun, ktorá presúva prvky poľa b do poľa a:

type
  index=1..max;
  pole=array[index] of integer;

var
  x,y:pole;

procedure presun(var a:pole; b:pole);
begin
  a:=b;
end;

...

begin
  presun(x,y);
end;
  • neskôr uvidíme aj parametre typu "otvorené pole" (open-array)

Napíšeme program, ktorý

  • náhodne generuje do poľa X prvky z intervalu 0..n-1
  • vypíše pole X
  • presunie pole X do poľa Y, zvýši hodnoty v poli Y o 1 a vypíše pole Y:

procedúry a funkcie s poliami:

type
  pole=array[1..100] of integer;

  function nahodne(n:integer):pole;
  var
    i:integer;
  begin
    for i:=low(pole) to high(pole) do
      Result[i]:=random(n);
  end;

  procedure vypis(var t:TextFile; const p:pole);
  var
    i:integer;
  begin
    for i:=low(p) to high(p) do write(t,p[i],' ');
    writeln(t);
  end;

  procedure presun(var a:pole; const b:pole);
  begin
    a:=b;
  end;

  procedure inc(var a:pole);
  var
    i:integer;
  begin
    for i:=low(a) to high(a) do a[i]:=a[i]+1;    // system.inc(a[i]);
  end;

var
  x,y:pole;
  t:TextFile;
begin
  AssignFile(t,'vypis.txt'); Rewrite(t);
  randomize;
  x:=nahodne(20);
  vypis(t,x);
  presun(y,x);
  inc(y);
  vypis(t,y);
  CloseFile(t);
  // Memo1.Lines.LoadFromFile('cisla.txt');
end;
  • všimnite si, že sme vytvorili funkciu inc na zvýšenie hodnôt prvkov v poli, tým sme 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á odstrániť)

Chybné použitie typov:

procedure a(b:array[1..10] of char);

   ­ typ formálneho parametra b nie je identifikátor

procedure x(y:1..10);

   ­ typ formálneho parametra y nie je identifikátor

function f(z:char):array[1..10] of char;

   ­ typ funkcie musí byť zadaný identifikátorom typu

Pole znakov

type pole=array[1..10] of char;

// polia znakov do 255 znakov je možné vypísať pomocou writeln

var s:pole;
begin
  s:='abcdefghij';
     // povolené priradenie presnej dĺžky ako je deklarovaná,
     // inak Type mismatch, napr. s:='abc'
  writeln(t,s);    // povolený výpis pomocou write(t,identifikátor)
end;

  • pole znakov sa nedá naraz celé načítať pomocou read
  • výnimka: pole array[0..n] of char Pascal chápe ako "Null-terminated string", a potom read vie čítať aj stringy, ktoré sú kratšie a ukončí ich #0 - neskôr uvidíme použitie aj takýchto znakových reťazcov

Polia korytnačiek

  • n korytnačiek vygenerujeme vedľa seba na vodorovnej priamke
  • každej nastavíme iný uhol
  • všetky "naraz" nakreslia kružnicu

pomalá verzia:

const
  n=50;
var
  i,j:integer;
  k:array[1..n] of TKor;
begin
  for i:=1 to n do
    k[i]:=TKor.Create(10*i+10,250,i*15);
  for j:=1 to 180 do
    for i:=1 to n do begin
      k[i].Dopredu(4); k[i].Vpravo(2);
      cakaj(1);
    end;
end;

zrýchlenie programu:

const
  n=50;
var
  i,j:integer;
  k:array[1..n] of TKor;
begin
  for i:=1 to n do begin
    k[i]:=TKor.Create(10*i+10,250,i*15);
    k[i].HP:=5;
  end;
  for j:=1 to 180 do begin
    for i:=1 to n do
      with k[i] do begin
        Dopredu(4); Vpravo(2);
      end;
    cakaj(1);
  end;
end;

Poznámka:

  • nový pascalovský príkaz with - vo vnútri neho sa "prednostne" pracuje s danou korytnačkou
  • čo sa stane, ak sa navzájom prehodia dva for cykly (s i a j)?
  • zvládne to aj napr. 100 korytnačiek, ale treba ich zahustiť (TKor.Create(4*i+10,250,...));

Jednoduchá animácia pomocou korytnačiek:

  • predchádzajúci príklad takto jednoducho pozmeníme:
    • cyklus for ... nahradíme nekonečným while true do ...
    • skôr ako sa všetky korytnačky pohnú o malý krok a otočia, zmažeme obrazovku, t.j. na obrazovke ostáva len posledná "stopa"

"krúženie" korytnačiek:

const
  n=50;
var
  i:integer;
  k:array[1..n] of TKor;
begin
  for i:=1 to n do begin
    k[i]:=TKor.Create(10*i+10,250,i*15);
    k[i].HP:=10;
  end;
  while true do begin
    zmaz;
    for i:=1 to n do
      with k[i] do begin
        Dopredu(4); Vpravo(2);
      end;
    cakaj(1);
  end;
end;
  • môžeme s touto ideou rôzne experimentovať:
    • korytnačky okrem toho, že prejdú nejaký malý krok, môžu popritom niečo kresliť
    • každá korytnačka sa môže otáčať o iný uhol
    • môžeme grafickej ploche zmeniť "farbu pozadia"

zrýchlená verzia:

const
  n=80;
var
  i:integer;
  k:array[1..n] of TKor;
begin
  for i:=1 to n do begin
    k[i]:=TKor.Create(10*i+10,250,i*15);
    k[i].HP:=10;
    k[i].FP:=clYellow;
  end;
  while true do begin
    zmaz(clNavy);
    for i:=1 to n do
      with k[i] do begin
        Dopredu(54); Dopredu(-50); Vpravo(1+i/n);
      end;
    cakaj(1);
  end;
end;

Ďalší príklad

  • v tomto príklade korytnačky nevygenerujeme na jednej priamke, ale rovnomerne na kružnici
  • najprv ich všetky vytvoríme v strede plochy, každú natočíme iným smerom a potom so zdvihnutým perom prejdú nejakú rovnakú vzdialenosť
  • použili sme vlastnosť korytnačky Smer, pomocou ktorej môžeme nastavovať absolútne natočenie korytnačky

korytnačky na kružnici:

const
  n=60;
var
  i:integer;
  k:array[1..n] of TKor;
begin
  for i:=1 to n do begin
    k[i]:=TKor.Create;
    with k[i] do begin
      HP:=15; FP:=clBlue;
      PH; Smer:=360/n*i; Dopredu(100); PD;
      Smer:=Smer*2;
    end;
  end;
  while true do begin
    zmaz;
    for i:=1 to n do
      with k[i] do begin
        Dopredu(4); Vpravo(2);
      end;
    cakaj(1);
  end;
end;

NDÚ:

  • v predchádzajúcom príklade korytnačky nebudú kresliť malý krok, ale nakreslia úsečky, tak aby boli cyklicky pospájané
  • experimentujte s rôznymi nastaveniami počiatočných smerov korytnačiek (namiesto Smer:=Smer*2; skúste napr. Vpravo(90); alebo Smer:=Smer*3;)

korytnačky sa naháňajú

  • n korytnačiek vygenerujeme na náhodných pozíciách
  • v každom kroku sa každá korytnačka posunie o 1/100 vzdialenosti k svojej nasledovníčke (posledná sa posunie k prvej)

naháňačka:

const
  n=8;
var
  i:integer;
  k:array[1..n] of TKor;
  xx,yy:real;
begin
  randomize;
  zmaz;                     // nastavia sa premenné sirka a vyska
  for i:=1 to n do begin
    k[i]:=TKor.Create(random(sirka),random(vyska));
    k[i].HP:=5;
    k[i].FP:=random(16777216);
  end;
  while true do begin
    for i:=1 to n do
      with k[i] do begin
        xx:=k[i mod n+1].X; yy:=k[i mod n+1].Y;
        Smer:=Smerom(xx,yy);
        Dopredu(Vzd(xx,yy)/100);
      end;
    cakaj(1);
  end;
end;
  • všimni te si použite i mod n+1
  • použili sme novú korytnačiu funkciu Smerom, ktorá vypočíta absolútny uhol od korytnačky k nejakému bodu v rovine
  • v tomto príklade by bol asi lepší konečný cyklus namiesto while true do, keďže po nejakom čase sa všetky korytnačky stretnú v jednom bode
  • program trochu upravíme: korytnačky okrem toho, že prejdú 1/10 vzdialenosti ku svojej nasledovníčke, nakreslia k nej aj spojnicu

korytnačky kreslia zaujímavé obrazce:

const
  n=8;
var
  i,j:integer;
  k:array[1..n] of TKor;
  xx,yy,d:real;
begin
  zmaz(clBlack);
  randomize;
  for i:=1 to n do begin
    k[i]:=TKor.Create(random(sirka),random(vyska));
    k[i].HP:=2;
    k[i].FP:=random(16777216);
  end;
  for j:=1 to 100 do begin
    for i:=1 to n do
      with k[i] do begin
        xx:=k[i mod n+1].X; yy:=k[i mod n+1].Y; d:=Vzd(xx,yy);
        Smer:=Smerom(xx,yy);
        dopredu(d); Dopredu(d/10-d);
      end;
    cakaj(1);
  end;
end;
  • program by fungoval, aj keby sme vyhodili riadok caka(1); - dokonca by chodil rýchlejšie


© 2002 AB, KVI
blaho@fmph.uniba.sk