Strukturované typy

Pole

(anglicky array) je již od počátku počítačového věku nejdůležitější datovou strukturou.

Píšeme

const Dim = 3;
type  tVektor3 = array[1..Dim] of real;
var a : tVektor3;
    x : real;
    i : integer;
    b : tVektor3; 

a myslíme tím, že proměnná a je skupina tří reálných čísel. Při deklaraci strukturovaného typu pole musíme uvést typ prvku pole a typ indexu. Typ indexu musí být ordinální typ s dostatečně malým rozsahem hodnot. Většinou píšeme místo typu indexu rovnou interval, ale nikdo nám nebrání psát

type  t3Dindex = 1..Dim;
      tVektor3 = array[t3Dindex] of real;


Operace s Poli: Přístup k prvku pole

S jednotlivými prvky pole můžeme pracovat zvlášť tak, že za identifikátor proměnné typu pole přidáme v hranatých závorkách index:

a[1] := 0; 
a[2] := x+1; 
a[3] := x-1;

Identifikátor pole následovaný výrazem v závorkách je první příklad toho, kdy designator není pouhý identifikátor. Vzpomeneme-li si na synatktické diagramy z druhé přednášky, uvidíme, že přístup k prvku pole můžeme použít na levé straně přiřazovacího příkazu stejně jako ve výraze, pokud tam můžeme použít proměnnou typu z něhož je pole utvořeno.

x := a[1]+a[2]+a[3]; 

je tedy správně zapsaný přiřazovací příkaz. Kdybychom jako indexy používali pouze konstanty, vystačili bychom se třemi proměnnými a1, a2, a3. Důležité je, že jako index můžeme použít libovolný výraz kompatibilní s typem indexu udaným při deklaraci. Výše uvedený součet tedy můžeme zapsat cyklem.

x := 0;
for i := 1 to Dim do x := x + a[i]; 

Podobně jako v přiřazovacím příkazu může me použít prvek pole jako parametr.

for i := 1 to Dim do Writeln(i, ' ' , a[i]); 

Vzhledem k deklaraci spadá ve všech krocích i do intervalu 1..Dim a nedojde tedy běhové chybě. Již víme, že při deklaraci pole musíme uvést typ prvku pole a typ indexu. Při přístupu k prvku pole, ať již na některé ze stran přiřazovacího příkazu, nebo jako parametru volání procedury či funkce, teď kromě samozřejmé podmínky na typ prvku pole (ani nyní nemůžeme psát CelePole[i]:=RealnePole[i] ), musíme navíc dbát o to aby index byl v povolených mezích.


Operace s Poli: Přiřazení


Pokud vytvoříme nový typ, nemumí Pascal s proměnnými tohoto typu příliš zacházet. Kromě setupu na nižší úrověň, což pro strukturovaný typ pole je použití kosntrukce Ident[index], umí jazyk Pascal přiřazovat mezi dvěma proměnnými stejného typu. Dva typy s odlišnými identifikátory jsou stejné pokud jsou navzájem svázány řadou rovnítek v deklaraci typů, kde na obou stranách rovnítka je jen a pouze identifikátor.

type 	Typ1 = array [1..6] of integer; 
	Typ2 = array [1..6] of integer; 
	Typ3 = Typ1;
	Typ4 = Typ1; // Typ3 je stjený s Typ2

var     Z1,Y1: Typ1;
	Z2,Y2: Typ2;
	Z3   : Typ3;
	Z4   : Typ4;
...
   Z1 := Y1;  // OK
   Z3 := Z1;  // OK
   Z2 := Y2;  // OK
   Z3 := Z4;  // OK
...
   Z1 := Z2;  // NE!
   Z2 := Z3;  // NE!

Chceme-li rozšířit schopnosti jazyka pracovat s nově definovaným typem, musíme použít procedury a funkce, které mají některý z parametrů tohoto typu.

Operace s Poli: Procedury a funkce

Z předchozího vyplývá, že naše vektory nemůžeme sčítat, jak bychom chtěli:

type tVektor3 = array[1..3] of real;
var a,b,c : tVektor3;
    x    : real;
...
   a := b+c; // NE!!!!

Bohužel, Pascal nám ( až na výjmimy jako je překladač FreePascal ) neumožňuje dodat definici operace '+' pro pole. Proto musíme psát:

procedure SectiV3( a,b : tVektor3; var c : tVektor3);
begin
   c[1] := a[1]+b[1];   
   c[2] := a[2]+b[2];   
   c[3] := a[3]+b[3];
end;
...
   SectiV3(b,c, a);

nebo

function SectiV3( a,b : tVektor3) : tVektor3;
begin
   SectiV3[1] := a[1]+b[1];   
   SectiV3[2] := a[2]+b[2];   
   SectiV3[3] := a[3]+b[3];
end;
...
   a := SectiV3(b,c);

Tato druhá varianta vypadá přehledněji, ale jde spíše o výjimečné použití funkce vracející hodnotu strukturovaného typu. Jde o konstrukci, kterou připouštějí až současné překladače Pascalu. Měli bychom mít na paměti, že v tomto případě probíhá stěhování výsledku nadvakrát, nicméně, možnost zapsat formulky aspoň trochu čitelně je příjemná:

 a := SectiV3(VektorovySoucin(b,c),VektorovySoucin(d,a));

Kdy předávat pole odkazem?

Až na výjimky pokaždé! Proto náš příklad

   a := SectiV3(b,c);

s vektorovou algebrou byl špatně hned dvakrát. nejen, že zbytečně kopíroval jeden výsledek funkce do cílové proměnné při přiřazení, ale především dvakrát kopíroval hodnotu obou prametrů předávaných hodnotou. Proto pro seriózní práci s poli budeme muset všechna předání pole uskutečnit odkazem. Tím přijdeme o možnost psát Součetpoli(VektorovysSoucin(a,b),c). Protože pole mohou být opravdu velká a mohou zabírat velké množství paměti, může být dalším důvodem i neúnosná spotřeba paměti. Obecně musíme dbát o to aby režie při volání procedury a navracení výsledku nebyla příliš velká (kolik je únosné? 5 nebo 50% ?).

Konstantní parametry

Přesto existuje možnost, jak nekopírovat hodnoty hodnotou předávaných parametrů, kromě předání paramtetrů odkazem a hodnotou máme totiž (v posledních letech) k dispozici tzv. konstantní parametry.

function SectiV3(const a,b : tVektor3) : tVektor3;
begin
 SectiV3[1] := a[1]+b[1]; 
 SectiV3[2] := a[2]+b[2]; 
 SectiV3[3] := a[3]+b[3];
end;

Tím překladači sdělíme, že hodnotu parametru nehodláme měnit, a on s ním bude zacházet šikovněji, především si nebude vytvářet kopii. Jak víme, hlavička procedury nebo funkce obsahuje nejn informaci pro kompilátor, ale vyjadřuje i záměry autora. Identifkátor funkce by měl říkat co vrací. Identifkátor procedury, co dělá, a idnetifikátory parametrů by měly být také výmluvné. Pokud je naším záměrem, aby konkrétní parametr sloužil pro vstup hodnoty, je vhodné jej označit jako konstantní. Tím se vyvarujeme možné vchyby, kdy se prostřednictvím parametru předávaného hodnotou pokusíme něco vrátit. Kompilátor si bude stěžovat. Vyzkoušejte!

Pole s více indexy

Z typu tVektor3 bychom mohli deklarací

type tMatice3 = array [1..Dim] of tVektor3;

vyrobit typ tMatice3. Poté bychom mohli psát

var M: tMatice3;
    b: tVektor3;
....
M[1][1]:=1; M[1,2]:=0; M[1,3]:=0; ....
b := M[1];

Naproti tomu deklarace

type tMatice3 = array [1..3] of array [1..3] of real;

nebo její zkrácená podoba

type tMatice3 = array [1..3,1..3] of real;

by nám nedovolila přiřazovat vektory do M[1] atd.

 

Příklady použití polí

Pole mají pro nás nepřeberné množství užití. Pro představu pár příkladů:

Seznam (index je jen pořadím v seznamu a nemá sám o sobě význam)

var TazenaCisla : array [1..6] of 1..49;

Časové řady (index je stále pořadím, ale má význam, lze z něj spočíst kdy došlo k odečtní hodnoty)

var Teplota : array [1..PocetVzorku] of real;

2D data

var Teplota : array [1..PocetVzorkuX,1..PocetVzorkuY] of real;

2D obrázek (zatím stupně šedi)

type tPixMap = array [1..PocetPixluX,1..PocetPixluY] of byte;
var PixMap : tPixMap;

3D data

var Teplota : array [1..PocetVzorkuX,1..PocetVzorkuY,1..PocetVzorkuZ] of real;

Tabulka funkčních hodnot

var Faktorial : array [0..170] of real; // 171! se do proměnné typu real nevejde
    Binomial  : array [0..1000,0..1000] of real; //zkuste urcit presnejsi meze !!!

Tabulka na překódovani

var TajnyKod : array[char] of char;    
 ObrazkyKaret: array[tKarta] of tPixMap; 
          s1 : array[integer] of char; //[Error] pokus.dpr(25): Data type too large: exceeds 2 GB
          s2 : array[real] of char;    //[Error] pokus.dpr(26): Ordinal type required

Poslední dva řádky nejsou správně a je u nich uvedena chyba, kterou nám překladač ohlásí.

Paměťové nároky polí

Pole mohou velmi snadno vyčerpat dostupnou paměť. U polí s jedním indexem to ještě není příliš aktuální. Pokud budeme ale psát program por odšumování audionahrávek a celou nahrávku se pokusíme nacpat do paměti najednou, můžeme narazit i tady. Pro představu si připomeňme známý fakt, že hodina stereofonní nahrávky v CD kvalitě nám zabere přes půl gigabytu (vzorek tvoří 2x16 bitů, rychlost vzorkování je 44 100 za sekundu). Daleko snazší je vyčerpat paměť při práci s poli se dvěma indexy. Takový RGB obrázek v rozlišení 10 000x10 000 zabere 300MB. Reálná matice se stejnými rozměry zabere skoro gigabyte. Jestliže se nám může číslo 10 000 velké a můžeme doufat, že tak velké matice potřebovat nebudeme, ve třech dimenzích se situace ještě zhorší. Budeme-li chtít nějakou fyzikání veličinu definovanou v prostoru studovat na počítači, často nám nezbyde, než si prostor redukovat na prostorovou mříž a pracovat s hodotami v uzlech této mříže.

program KrychloHydro;

const N = 200;

type tGridFunction = array [1..N,1..N,1..N]

var p,vx,vy,vz : tGridFunction;
....

Výše uvedený začátek programu pro naivní počítačovou hydrodynamiku na krychli předpokládá, že na krychli o hraně dvěstě bodů budeme definovat tři komponety rychlosti a tlak. Těmito několika řádky jsme si vyžádali 256 MB paměti. Podle povahy problému se ukáže, jestli nám 200 bodů stačí. Pokud budme potřebovat více, nesmíme zapomenout, že spotřeba paměti roste se třetí mocninou N.

Než budete psát diplomovou práci, pravděpodobně vzroste typická kapacita pamětí osobních počítačů 10x. To ovšem znamená, že dovolené N vzroste pouze dvakrát.

Eratosthenovo síto

je další klasický algoritmus, a navíc ilustruje, že koncept pole je opravdu starý.

program Sito;

const N = 50000000;

var MaDelitel : array[0..N] of boolean;
i,p : integer;

begin
  p:=2; //prvni prvocislo

  repeat
    {krok 1: oznacim vsechny nasobky prvocisla p}
    i:=p+p;
    while i<=N do begin
      MaDelitel[i]:=true;
      i:=i+p;
    end;

    {krok 2: najdu dalsi prvocislo}
    p:=p+1;
    while MaDelitel[p] {tedy neni to prvocislo} do p:=p+1;

  until p*p>N; // a to cele opakuji....

//ted spoctu pocet prvocilel od 2 do N
  p:=0;
  for i :=2 to N do if not MaDelitel[i] then p:=p+1;
  Writeln('Existuje ',p,' prvocilsel <= ',N);
  Readln;
end.

Typ záznam

Struktorovaný typ se zkládá z menších kousků, podobně jako strukturovaný příkaz se zkládá z "menších" příkazů, jednoduchých i strukturvaných (for for if). V případě pole byly jednotlivé kousky stejného typu a přístup k nim jsme měli pomocí indexace. V Pascalu máme ale ještě jednu možnost.

type tVectorXYZ = record
                    x,y,z : real;
		  end;
var  a,x  : tVectorXYZ;
begin
  a.x := 1;
  a.y := -1;
  a.z := 0;

  x   := a;
  x.x := 2;
  ...
end.

Podobně jako u polí můžeme

Je libo pole záznamů nebo záznam s poli?

Samozřejmě můžeme kombinovat podle uvážení obě konstrukce a designátory nabyvaji na kráse:

type tComplex = record
                 Re,Im : real
		end;
     tCV3     = array [1..3] of tComplex;
     tRGB     = packed record 
		   R,G,B : byte;
	        end;
     tKomlexniPuntik 
             = record  
                  x     : tCV3;
		  barva : tRGB;
             	end;
var  A : tKomlexniPuntik ;
     b : tRGB;
...
A.x[2].Im := 0;
A.x[1].Re := -1;
...
           

Inicializované proměnné a konstanty.

Dokud jsme pracovali jen s jednoduchými proměnnými, nebyl problém na začátku programu napsat pár přiřazení a inicializovat obsah proměnných. Pole nebo záznmay ale mohou být delší a jejich inicializace sérií přiřazovacích příkazů by byla nepohodlná. Mohou nastat dva případy

A) hodnoty je třeba inicializovat před výpočtem a ty se již nemění.
B) hodnoty je třeba inicializovat před výpočtem, a ty se poté budou dále měnit.

K tomu můžeme použít následující konstrukce se syntaxí:

DeklaraceIniKonstProm: (var | const) Ident : Typ = IniKonstVyraz ';'
IniKonstVyraz: KonstVyraz | '(' SeznamIniKonstVyrazu ')'
SeznamIniKonstVyrazu: IniKonstVyraz (',' IniKonstVyraz)*

a zde jsou příklady s inicializovanými poli :

const iFaktorial : array [0..12] of integer = (1,1,2,6,24,120,720,5040,40320,
					       362880,3628800,39916800,479001600);
var   ObjemNadob[1..PocetNadob] = (0,0,10);
      CisloKroku : integer = 0;

Konstrukce výrazu typu pole je dovolen jen v deklaraci inicializované proměnné nebo typované konstanty, do přiřazovacího příkazu nemůžeme použít

  ObjemNadobi:=(1-x,x,y);
  ObjemNadobi:=(0,0,11);

Pozor, některé dřívější verse jazyka Pascal neumějí variantu s var a i proměnné se deklarují jako const.

type tSeznamAz10Cisel
         = record
             Pocet : 0..10;
             Cisla : array [1..10] of integer;
           end;

var Dlouhy : tSeznamAz10Cisel = (Pocet : 8; Cisla : (1,2,3,4,5,6,7,8,0,0) );
    Kratky : tSeznamAz10Cisel = (Pocet : 2; Cisla : (1,2,0,0,0,0,0,0,0,0) );

Samozřejmě můžeme inicializovat i jednoduché proměnné.

var   Oddelovac : char = ',';
      CisloKroku : integer = 0;

Proměnné z vícenásobná deklarace jako třeba

var   a,b : char ;

nemohou být inicilizovány. Neinicializované globální proměnné jsou při startu programu inicializovány na 0 (a to i tehdy, když 0 nepatří do jejich intervalu atp.), zatímco lokální proměnné obsahují před prvním použitím smetí. Typované konstanty smí být lokální (tedy deklarovány v bloku nějaké procedury či funkce), inicializované proměnné nikoli a jsou bohužel dovoleny jen na globální úrovni.

Variantní záznam

Někdy chceme ukládat jednotlivé položky přes sebe. To proto, že význam má jen jedna z nich a rezervovat místo na ty ostatní je zbytečné. Předpokládejme, že si chceme udělat pořádek v plechových geometrických součástkách na našem skladu. Taková plechová součástka je popsaná materiálem, tloušťkou plechu, tvarem a rozměry. Rozměry trojúhelníku se ale určují pomocí tří čísel zatímco u kruhu stačí jen poloměr.

program CaseTest;

type
   tTvar = (tvObdelnik, tvTrojuhelnik, tvKruh);
   tMaterial = (mtHlinik,mtOcel);
   tPlechovyUtvar = record
                     Material:tMaterial;
                     Tloustka:Real;
                     case Druh:tTvar of
                       tvObdelnik: (Vyska, Sirka: Real);
                       tvTrojuhelnik: (StranaA, StranaB, StranaC: Real);
                       tvKruh: (Polomer: Real);
                     end;

function Obvod(W:tPlechovyUtvar):real;
begin
  if W.Druh=tvObdelnik    then Obvod:=2*(W.Vyska+W.Sirka);
  if W.Druh=tvTrojuhelnik then Obvod:=W.StranaA+W.StranaB+W.StranaC;
  if W.Druh=tvKruh        then Obvod:=W.Polomer*2*Pi;
end;

Cvičení: dodělejte funkce pro plochu, objem a hmotnost součástek.

 

Endiáni: (J. Swift)

Cvičení: Pomocí variantního záznamu prozkoumejte proměnnou typu integer jako pole 4 bytů. Je zřejmé, že pokud do celého čísla typu integer uložíte hodnotu 1, pak ve třech bytech bude 0 a v jednom 1, o tom říkáme, že je nejméně výnamný. Zjistěte jestli váš počítač ukládá nejméně významý byte na nejnižší nebo naopak nejvyšší adrese. (Předpokládmáme ovšem, že pole se ukládá vždy tak, že s rostoucím index roste i adresa uložení.)

Alignment a packed record

V současných počítačích je vyzvednutí hodnoty proměnné z paměti, které musí předcházet libovlné operaci s proměnnou, velmi náročnou opearcí, např. ve srovnání s časem potřebným pro sečtení dvou celých čísel. Aby se uryclila práce počítače, vyzvedává více bytů najednou, řekněmě že 8. Pro optimální běh programu je pak vhodné, aby procesor dokázal načíst např. celé číslo (4 byty) na jedinou operaci přístupu do paměti. Nejjednodušším řešením tohoto problému je, že každá položka záznamu o velikosti 4 byty začíná na adrese dělitelné 4 a podobně pro 8 bytů. To znamená, že se v paměťovém prostoru pro uložení záznamu nacházejí nevyužitá místa. Někdy může být plýtvání opravdu velké:

type rec_brb = record
       a:byte; //tady nasleduji 7 práznych bytů
       j:real;
       b:byte; //tady nasleduji 7 práznych bytů
     end;
     rec_bb = record
       a:byte; 
       b:byte; 
     end;

...

Writeln( sizeof(rec_brb) );  //   --> 24
Writeln( sizeof(rec_bb) );   //   --> 2

Tedy pro uložení 10 bytů informace, použil překladač 24 bytů paměťového prostoru. To jsme zjistili použitím universální funkce sizeof, která jako parametr akceptuje identifikátor typu nebo proměnnou libovolného typu (jde jakoby o předávání odkazem, takže ne libovolný výraz) . Vrátí pak počet bytů který proměnná či typ zabírá.

Zabránit takovémuto zarovnávání (alignment) můžeme použitím klíčového slova packed v deklarci typu.

type rec_brb = packed record
       a:byte; 
       j:real;
       b:byte;
     end;
Tentokrát bychom dostali velikost záznamu 10 byte.

Příkaz With

Účel příkazu with vysvětlí nejlépe příklad.

type tComplex = record
                 Re,Im : real
		end;
var  z : tComplex;
     Re: Real;
...
Re := 1;
with z do 
begin
  Re := 0;
  Im := 1;
end;
Writeln (Re,' ',x.Re);

Vypíše ' 1.00000000000000E+0000 0.00000000000000E+0000', protože unvitř příkazu with přestala být vnější proměnná Re vidět a byla zakryta identifikátorem složky záznamu z.

Příkaz Case

je příkaz, který jsem doposud tajil. Občas se hodí nahradit řadu podmíněných příkazů např.

if n=0 then f:=1
else if n=1 then f:=x
else if n=2 then f:=x*x
else if n=3 then f:=x*x*x
else if n=4 then begin f:=x*x; f:= f*f; end;
else begin
  f:=exp(ln(x)*n);
end;

následujícím strukturovaným přikazem

case n of
 0:  f:=1;
 1:  f:=x;
 2:  f:=x*x;
 3:  f:=x*x*x;
 4:  begin f:=x*x; f:= f*f; end;
 else 
  f:=exp(ln(x)*n);
end;

Část začínající else lze vynechat a také můžeme místo jednoho čísla psát seznam čísel a intervalů, které se nesmějí překrývat

case c of
 '-':           n:=-n;
 'a'..'z','_':  n:=n+1;
 'A'..'Z', 
 '0'..'9','$':  n:=n-1;
end; // kód nemá žádný význam

Pole s volným koncem

V mnoha případech neznáme v okamžiku kdy píšeme program, kolik hodnot bude v poli potřeba uskladnit. Běžným řešením je odhadnout shora maximální rozumný počet a pole dimenzovat na tuto maximální zátěž. Jde o velmi běžný postup a i některé solidní programy (TeX) vám někdy nahlásí, že jste vyčerpal kapacitu a musíte si program překompilovat s větší konstantou určující kapacitu polí pro uskladnění.

Typické použití je v následujícím kódu: Když už umíme ukládat řadu hodnot do pole, můžeme si ukázat jak tato data načteme z klávesnice.

{$RANGECHECKS ON} 
const Max = 1000;
var PocetHodnot : integer = 0; //inicializovaná proměnná
    Hodnoty     : array[1..Max] of real;
    x : real;
begin
 Writeln('Zadejte až ',Max,'kladných reálných čísel');
 Writeln('Zadávání ukončete záporným číslem nebo nulou.');
 
 ReadLn(x);  
 while x>0 do begin
   PocetHodnot :=  PocetHodnot + 1;
   Hodnoty[PocetHodnot] := x;
   ReadLn(x);  
 end;
 
 Writeln('Načetl jsem ', PocetHodnot, ' čísel. Příště s nimi i něco udělám');

end.

Takto obvykle vyapdají programy z učebnic. Načíst řadu čísel, něco s ní udělat (v našem případě jsme je jen naskládali do pole), přičemž konec řady je indikován nějakým číslem mimo rozsah povolených hodnot.

Zde je použita funkce ReadLn poprvé k něčemu lepšímu než k čekání na stisk klávesy Enter. Jde o vylepšenou versi proceury Read, která umí načíst ze vstupu (nebo souboru, to uvidíme později) data několika základních typů: Celé číslo, reálné číslo, jeden znak a řetězec znaků (uvidíme dále).

Už v tomoto programu používáme pole s volným koncem, a proměnné Hodnoty a PocetHodnot jsou úzce svázány. Mohlo by nás napadnot spojit je do jednoho záznamu a chápat je jako jednu (strukturovanou) proměnnou. Námi používaná verse Pascalu nám umožňuje lepší řešení:


program OpenArrayTest;

var Hodnoty : array of real;
    x : real;
    kam : integer;
begin
 Writeln('Zadejte libovolný počet kladných reálných čísel');
 Writeln('Zadávání ukončete záporným číslem nebo nulou.');
 
 ReadLn(x);  
 while x>0 do begin
   Kam:=High(hodnoty)+1; //Low(Hodnoty) je 0
   SetLength(Hodnoty,Kam+1);
   Hodnoty[Kam] := x;
   ReadLn(x);
 end;

 Writeln('Načetl jsem ', High(hodnoty)+1, ' čísel. Tady jsou jejich druhe mocniny:');

 for kam := 0 to High(hodnoty) do
   Writeln(Hodnoty[Kam],sqr(Hodnoty[Kam]));

 readln;
end.

Od předešlého se program hlavně liší tím, že pole Hodnoty má jako první index nulu. Jeho počet prvků můžeme měnit procedurou SetLength(Pole,JakDlouhe). Aktuální horní mez zjistíme pomocí funkce High. Dolní mez si můžeme zjistit s pomocí funkce Low, ale bude to vždy nula. Obě funkce můžeme použít i proběžná pole ale tam nám vrátí konstantu danou příslušnou mezí intervalu indexu, a může to být třeba i znak, když je má příslušné pole z index.

První položka pole s volným koncem má index nula.

 

Řetězcové konstanty

Již víme vše o znacích a řetězce jsou speciální pole znaků. Protože by bylo nepříjemné psát něco jako
('A','h','o','j',' ','l','i','d','i','!')
lze v Pascalu zapisovat řetězcové konstanty takto 'Ahoj lidi!'.

Typ String

Naneštěstí přes všechnu snahu o pravidelsnot se ukázalo, že užitečné věci jsou nepravidelné. Příkladem jsou třeba řetězce. Jde o složitý problém a z mnoha variant řetězcových typů si povíme jen o typu string.

var s,t: string;

begin
  s := 'Ahoj lidi!'; {Přiřazení konstanty do řetězce}
  t := s;            {Přiřazení hodnoty  s do t}
  t := copy(s,2,3);  {Přiřazení výsledku funkce do proměnné, kus počínaje druhým dlouhý tři}
  Writeln(t);        {Vypíše 'hoj'}

  Delete(s,1,4);     {Vypustí 'Ahoj')
  t := 'Ne'+s;  {Složení řetězce z kousků}
  Insert('blaznete',t,3);
  Writeln(t);    

  Writeln(Length(t)); {délka řetězce ve znacích}
  Writeln(Pos('li',t));{nalezení polohy podřetězce jinak 0}

  Setlength(t,40);
  t[40]:='6';

  str(Pi:20:10,s);  {Nacpi Pi do retezce}
  Writeln(s);
end.

Ještě máme po ruce další spoustu procedur a funkcí např. UpperCase

Velmi důležitá je někdy funkce

procedure Val(S; var V; var Code: Integer)

která umí do celočíselné nebo reálné proměnné V dosadit hodnotu zapsanou v řetězci. Code je 0 pokud nenastanou problémy, jinak je to index problematického znaku v řetězci.

Typ Text

I přes snahu o co nejméně výjimek, je i Pascal plný nepravidelností. Jedno z nich je vstup a výstup do textového souboru. Cheme-li pracovat s (textovým) souborem, musíme si uvědomit, že je to objekt spadající do kompetence OS. V jazyce pak potřebujeme vrátka, skrze která nám budo dovoleno provádět se soubory užitečné operace. Dnes již je jakýkoli soubor posloupností bytů (nikoli štítků či bloků). Textový soubor je pak charakterizován tím, že co byte to znak nebo řídící znak ( Ale ani to již není tak prosté: Unicode! ). Protikladem může být třeba binární soubor nějakého obrázku: Nehomogenní skládačka z binární hlavičky souboru následované bloky, každý se svojí hlavičkou a komprimovanými daty ....

V pascalu je textový soubor (tedy ony dveře do světa) zastoupe proměnnou typu Text. Takto zapíšeme do textového souboru krátkou zprávu:

var T: Text;
begin
 Assign(T,'Soubor.txt');
 Rewrite(T);
 Writeln(T);
 Close(T);
end.

Co se souborem můžeme dělat nám velmi omezuje OS, on je za něj zodpovědný. Proto i proměnné representující soubory zdědí jistá omezení. Především, je zakázáno přiřazení do proměnné typu Text. Navíc nemá pro nás přístupnou vnitřní strukturu, takže nemůžeme psát něco jako T.Name := 'Soubor.txt'. Zbývá tak jen třetí způsob práce s proměnnou typu Text, a to pomocí parametrů procedur a funkcí. I zde jsme omezeni, nesmíme předávat soubor hodnotou. Proto všechny oprace se soubory budou mít formu volání procedur, kde jako první parametr bude proměnná typu Text.

Assign(TextVar, Retezec)
Rewrite(TextVar)
Close(TextVar) !!!!
Append(TextVar)
Reset(TextVar)

Write, WriteLn

Formátování:

  Writeln(Pi);
  Writeln(Pi:5);
  Writeln(Pi:10);
  Writeln(Pi:15);
  Writeln(Pi:20);
  Writeln(Pi:25);

U reálných čísel máme ještě desetinná místa.

  Writeln(Pi);
  Writeln(Pi:30);
  Writeln(Pi:30:0);
  Writeln(Pi:30:4);
  Writeln(Pi:30:8);
  Writeln(Pi:30:12);
  Writeln(Pi:30:16);
  Writeln(Pi:30:20);
  Writeln(Pi:30:24);