Rozdział 2 - podstawy języka Object Pascal

Spis treści:

...podstawy Object Pascal'a
Trochę o modułach
Komentarze
Zmienne
Stałe
Instrukcje if, then, else
Operatory
Instrukcja case
Typy
Rzutowanie
Konwersja
Tablice
Procedury i funkcje
Moduły
Rekordy
Instrukcja wiążąca with
Operacje matematyczne
Pętle

Pętla for
Pętla repeat
Pętla while

Słowo o algorytmach
Polecenia Continue i Break
Etykiety
Funkcje Pred i Succ
Podsumowanie


 

Cóż to jest ten Object Pascal? Delphi wykorzystuje język Pascal. Język ten unowocześniono nadając mu nową nazwę - Object Pascal. Jedni mówią, że programują w Pascalu, drudzy, że w Object Pascalu, a jeszcze inni mówią, że w Delphi. Ja na tym etapie będę używał nazwy Object Pascal...

W poprzednim rozdziale napisałeś swój pierwszy program w Delphi. Cóż pisać nic nie musiałeś, ale program stworzyłeś prawda? Na samym początku, aby łatwiej Ci było przyswoić ten właśnie język nie będziemy używać formularzy ani komponentów. Jeżeli masz otwarte Delphi to zamknij okno formularza oraz edytor kodu. Delphi powinno Cię spytać przy zamykaniu, czy chcesz zachować zmiany w formularzu ( Save changes to Unit1.pas? ). Naciśnij No. Ok, formularz jest już zamknięty, edytora kodu także nie widać. Teraz z menu Project wybierz View Source ( w Delphi 2 jest to View -> Project Source ). Na ekranie pojawił się znów edytor kodu, a w nim zawartość pliku DPR: 

program Project1;

uses
  Forms;

{$R *.RES}

begin
  Application.Initialize;
  Application.Run;
end.

Napiszemy teraz program bez wykorzystania formularza ani komponentów. Na początek...

...podstawy Object Pascal'a

Przyswajaj się z informacją, że prawie każda linia komend zakończona jest znakiem średnika. Są oczywiście pewne wyjątki. Ogólnie możesz przyjąć, że wszystkie wyrazy, które Delphi wytłuszcza nie kończą się znakiem średnika ( wyjątek - słowo end ). Właściwy kod programu wpisywany jest pomiędzy słowa begin oraz end. Ilość beginów musi się równać ilości endów inaczej Delphi zasygnalizuje błąd, że brak słowa end. Pierwsza linia kodu źródłowego musi zaczynać się słowem kluczowym program po którym następuje wpisanie nazwy programu. Idziemy dalej. Słowo kluczowe uses. Po tym słowie wpisywane są moduły.


Moduł
- jest to plik tekstowy, który może być kompilowany do postaci wykonywalnej. Programujący wcześniej w Turbo Pacalu albo w C++ wiedzą o co chodzi. Czytaj dalej, a dowiesz się czegoś więcej. 

Już niebawem więcej o modułach. Przeanalizujmy dalszy ciąg programu. Następuje tam taka linia: {$R *.RES}. Jest to tzw. dyrektywa. Na razie się tym nie przejmuj - umówię to później. Na razie tę dyrektywę możesz usunąć. Idziemy dalej, słowo kluczowe begin. Sygnalizuje ono początek programu - od tego miejsca program będzie wykonywał polecenia. Następne dwie linijki też możesz usunąć - nie będą nam teraz potrzebne. No i na końcu słowo end ( z kropką na końcu ) "mówi", że w tym miejscu program się zakończył. Możesz swój projekt zapisać gdzieś na dysku. Zauważ, że podczas zapisywania program nie będzie Cię prosił o zapis formularza, bo go przecież zamknąłeś, nie? No i w katalogu z programem jest mniej plików. 

Trochę o modułach

Doprowadź program do takiej postaci:

program drugi;

uses
  Forms;

begin
  MessageBox(0, 'Cześć, jestem oknem!', 'Hiehie', MB_OK);
end.

Teraz spróbuj skompilować program ( Ctrl + F9 ). Nie można! Występuje błąd: [Error] drugi.dpr(7): Undeclared identifier: 'MessageBox'
Program nie wie, co to za polecenie - MessageBox. Wszystko za sprawą modułów. Tak jak powiedziałem wcześniej w modułach znajdują się deklaracje różnych poleceń. Akurat deklaracja polecenia MessageBox znajduje się w module Windows. Tak więc zamiast słowa Forms wpisz Windows. 

uses
  Windows;

Teraz możesz uruchomić program ( klawisz F9 ). Po skompilowaniu wyświetli się okienko z tekstem. Dobrze. Zacznijmy omawiać komendę MessageBox. Jest to pierwsze polecenie, które poznałeś! Jak widzisz po nazwie komendy w nawiasach trzeba wpisać potrzebne jej informacje. Informacje te wpisuje się po przecinkach. Pierwszy parametr to tzw. uchwyt. Na razie nie zawracaj sobie tym głowy - nie mamy formularza to na to miejsce wpisujemy 0. Kolejny parametr to tekst, który ma pojawić się w okienku - wpisujemy go w apostrofach. Następny parametr to tekst, który pojawi się na belce tytułowej okna. Ostatni parametr to przycisk, który pojawi się w oknie. Na razie warto wiedzieć, że MB_OK to znaczy, że będzie wyświetlany jedynie przycisk OK. 

Spójrz teraz do katalogu, w którym masz zapisany projekt. Powinien być w nim plik EXE, który zajmuje jedynie... 16 kB! Niesamowite co? Pierwszy nasz program zajmował aż ~300, a ten tylko 16. Wszystko dlatego, że nie zastosowaliśmy formularzy, ani modułu Forms, który drastycznie zwiększa rozmiar EXEka. 

Komentarze

Komentarze są najprostszym elementem języka ( każdego języka ). Właściwie nie ma nic do wyjaśnienia. Jak sama nazwa wskazuje służą do komentowania kodu. Nie są one brane przez kompilator przy kompilacji i możesz wpisywać w nich co chcesz. Jest kilka stylów komentarzy: 

// To jest komentarz jednej linii
{
     To może być komentarz wielu linii 
}
(*
    To jest jeszcze jeden komentarz jednej linii { może zawierać nawiasy klamrowe }
*)

Zmienne

Zmienne są bardzo ważnym elementem programowania. Praktycznie nie ma programu bez wykorzystania zmiennych. Otóż zmienne umożliwiają przechowywanie w pamięci komputera jakiś wartości jak tekst, liczby itp. Zmienne deklaruje się za pomocą słowa var. Oto przykład zmiennej:

var
  MojaZmienna : String;

Po słowie var może znaleźć się bardzo wiele ( nieskończenie wiele ) zmiennych. Budowa przedstawia się następująco: najpierw unikalna nazwa zmiennej, która nie może się powtarzać. Następnie po znaku dwukropka typ zmiennej oczywiście zakończony średnikiem. Różne typy zmiennych omówię później. Na razie najważniejsze jest to, że typ String umożliwia przechowywanie wartości tekstowych. Przy deklarowaniu zmiennych można od razu przypisać im wartość domyślną:

var
  MojaZmienna : String = 'To jest moja pierwsza zmienna';

Zauważ, że przypisanie domyślnej wartości odbywa się po znaku równości. Oto podstawowe typy zmiennych:

Nazwa				Zakres

Byte				0..255
Integer				-2 147 483 648..2 147 483 647
Int64				-9 223 372 036 854 775 808..9 223 372 036 854 775 807
Double				zmiennoprzecinkowy
Currency			zmiennoprzecinkowy
Word				0..65 000
Char				znak

Pewnie wcześniej się zastanawiałeś "Skąd komputer wie ile pamięci ma zarezerwować dla mojej zmiennej?". Otóż nie wie. Wynika to z typu zmiennej. Np. typ Integer zajmuje w pamięci 4 bajty, a typ Int64 ( nie występuje w Delphi 2 ) już 8 bajtów. Typ Char, który może przechować tylko pojedynczy znak zajmuje 1 bajt. Najczęściej używanym typem do przechowywania wartości liczbowych jest typ Integer. Zastosujmy zmienne w praktyce. Dokonaj zmiany w poprzednim tworzonym przez nas programie: 

{
   Copyright (c) 2001 - Adam Boduch
}

program zmiene;

uses
  Windows;

var Imie : PChar; // zmienna tekstowa

begin
  Imie := 'Adam'; // przypisanie wartości zmiennej
  MessageBox(0, 'Cześć, to moje imię', Imie, MB_OK);
end.

Zauważ, że zmienne deklarowane są poza blokiem begin..end. Zauważ, że nowy typ deklarowany w programie to PChar. PChar jest również typem, który umożliwia przechowywanie tekstu, ale o tym później. No więc w bloku begin nastąpiło przypisanie wartości do zmiennej. Przypisanie wartości następuje za pomocą znaku :=. Tekst, który ma być przypisany oczywiście musi być wpisany w apostrofach. Jeżeli deklarujesz typ, który przechowuje liczby to oczywiście liczby nie wpisuj w apostrofach. Przy wypisywaniu zmiennych nie ma znaczenia w jaki sposób to zapisujesz. Możesz więc napisać tak:

var
  MojPiesek, MojKotek : String;

Albo tak:

var
  MojPiesek : String;
  MojKotek : String;

Stałe

Stałe także służą do przechowywania wartości tyle, że w odróżnieniu od zmiennych tych wartości, które zostały przypisane podczas pisania programu nie można później zmieniać. Stałe deklaruje się po słowie kluczowym const.

const
  MojaStala = 1;
  Imie = 'Adam';

Przy deklaracji stałych nie musisz podawać typu zmiennych aczkolwiek możesz. Dla wartości liczbowych Delphi automatycznie przypisuje jej typ Integer. Dla tekstu jest to String. Nie musisz się na to godzić. Jeżeli deklarujesz stałą tekstową to Delphi jak już powiedziałem przypisuje jej typ String. Jeżeli chcesz, aby to był typ PChar to piszesz tak:

const
  Imie : PChar = 'Adam';

No i teraz stała Imie jest stałą typu PChar. 

Instrukcje if, then, else

Instrukcje if, then można przetłumaczyć na polski tak: "Jeżeli coś tam, coś tam będzie tak to wtedy zrób coś tam coś tam.". Ta instrukcja służy do porównywania wartości. Przy okazji poznasz nowe polecenia... Do programu dodaj zmienną "Wylosowanaliczba" typu Integer. Cały kod programu powinien wyglądać tak:

 

{
   Copyright (c) 2001 - Adam Boduch
}

program ifthen;

uses
  Windows;

var WylosowanaLiczba: Integer;

begin
  Randomize; // uruchamiamy bęben losujący :)
  WylosowanaLiczba := Random(20); // losuj z zakresu 19
  if WylosowanaLiczba < 10 then
    MessageBox(0, 'Wylosowana liczba jest mniejsza niż 10', '{none}', MB_OK);
  if WylosowanaLiczba > 10 then
    MessageBox(0, 'Wylosowana liczba jest większa niż 10', '{none}', MB_OK);
end.

Ufff, to już jest trudniejsze, prawda? Najpierw opiszę procedurę losowania liczby. Na samym początku należy użyć polecenia Randomize. Polecenie Random(20) losuje liczbę od 0 do 19. Tak się składa, że Delphi nie losuje od 1 do 20 tylko właśnie od 0 do 19. Tak już jest. No więc losuje i wylosowaną wartość przypisuje do zmiennej WylosowanaLiczba. Następnie następuje sprawdzenie jaka liczba została wylosowana. Jeżeli jest mniejsza od 10 to zostaje wyświetlony odpowiedni komunikat. Jeżeli jest większa od 10 to także wyświetlany jest komunikat. Dosłownie instrukcje if można przetłumaczyć tak: "Jeżeli zmienna <<WylosowanaLiczba>> jest mniejsza od 10 to...". Porównanie następuje za pomocą znaku < oraz >, które oczywiście tak jak w matematyce oznaczają większe i mniejsze. 

W tym miejscu chciałbym wspomnieć o nowym typie, z którego w przyszłości korzystać będziesz często. Ten typu to Boolean. Nie przybiera on wartości liczbowych ani tekstowych. Może przyjmować wartość TRUE ( prawda ), albo FALSE ( fałsz ). 

if ProgramUruchomiony = true then
  { coś }

Jeżeli napiszesz tak:

if ProgramUrhcomiony then
   { coś }

Delphi będzie wiedział, że jak nie napisałeś true, ani false to chodzi Ci o TRUE.

Operatory

Operatory bardzo często są wykorzystane właśnie wraz z instrukcją if. Oto podstawowe operatory: 

Symbol Opis
:= Operator przypisania;
Operator równości
> Większe od...
< Mniejsze od...
>= Większe, równe od..
<= Mniejsze, równe od
<> Różne od...
+ Operator dodawania
Odejmowanie
/ Dzielenie.
* Mnożenie
div Dzielenie z odcinaniem reszty z dzielenia.
mod Dzielenie z pozostawianiem jedynie reszty z tego dzielenia
and Operator and ( ang: lub ). Porównywanie wielu warunków.
or Operator or ( ang. lub ). 
not Operator not (ang. nie ). 

A oto przykład połączenia operatorów z instrukcją if, then: 

  if (Liczba > 100) and (Liczba < 150) then { jeżeli liczba jest większa od 100, ale mniejsza od 150 }
  if (Liczba <> 100) and not (Liczba = 120) then { jeżeli liczba jest rożna od 100 i nie jest to 120 }
  if (Liczba < 100) or (Liczba < 150) then { jeżeli liczba jest mniejsza od 100 oraz mniejsza od 150 }

Zauważ, że jeżeli masz więcej warunków do spełnienia to musisz je wsiąść w nawias. 

Do czego w takim razie służy else? Można to przetłumaczyć jako: "w przeciwnym wypadku". Tzn., co program ma robić jeżeli warunek nie zostanie spełniony:

  if Liczba = 100 then { jezeli liczba jest rowna 100 to rób cos tam }
    MessageBox(0, 'Jest to liczba 100', '', MB_OK)
    { w przeciwnym wypadku }
  else MessageBox(0, 'Cholera, nie wiem co to za liczba', '', MB_OK);

Jeżeli wylosowana liczba to 100 wyświetl okienko. Zauważ, że po poleceniu MessageBox nie ma średnika! Wynika to dlatego, że następnym poleceniem będzie else. Ale uważaj! Jeżeli po instrukcji if, then będzie więcej niż jedna komenda to musisz wszystko wsiąść dodatkowo w słowa begin i end:

  if Liczba = 100 then
  begin { <-- trzeba begin bo beda po tym dwa polecenia! }
    MessageBox(0, 'Jest to liczba 100', '', MB_OK); { <-- jest średnik!!! }
    MessageBox(0, 'Zgadłeś! Brawo!', '', MB_OK); { <-- tu także jest średnik! }
  end else MessageBox(0, 'Cholera, nie wiem co to za liczba', '', MB_OK);

Niestety na początku może to być mylące, ale niestety trzeba to zapamiętać. Jeżeli nie ma else na końcu to cały listing przedstawiał będzie się tak:

  if Liczba = 100 then
  begin { <-- trzeba begin bo beda po tym dwa polecenia! }
    MessageBox(0, 'Jest to liczba 100', '', MB_OK); { <-- jest średnik!!! }
    MessageBox(0, 'Zgadłeś! Brawo!', '', MB_OK); { <-- tu także jest średnik! }
  end;
 

Instrukcja case

Instrukcja case jest w działaniu podobna do instrukcji if. Wymyślił ją pewnie ktoś komu nie chciało się pisać tyle razy if. Stosujemy ją bowiem wtedy kiedy mamy dużo warunków if.

var Liczba: Integer;
    sText : String;

begin
  Randomize; // uruchamiamy bęben losujący :)
  Liczba := Random(20); // losuj z zakresu 20
  case Liczba of
     1: sText := 'Kurde, wylosowałeś 1!';
     10: sText := 'Sam środek!';
     19: sText := 'Sam koniec';
  end;
end.

Budowa instrukcji case jest także specyficzna. Najpierw słowo kluczowe case, później zmienna, która będzie porównywana, a na końcu słowo of. Później wartości zmiennej "Liczba". Np. jeżeli wylosowana zostanie 1 to potem średnik. Następnie chcemy, aby do zmiennej sText przypisać jakiś tekst. Itd. W instrukcji case możesz także wykorzystać instrukcję else:

 case Liczba of
     1: sText := 'Kurde, wylosowałeś 1!';
     10: sText := 'Sam środek!';
     19: sText := 'Sam koniec';
  else sText := 'Eeee, już nic.';
  end;

 Tak jak w przypadku instrukcji if jeżeli masz dwie linie komend musisz wszystko wziąć pomiędzy blok begin i end:

 case Liczba of
     1: begin { będą dwie linie - trzeba użyć słowa begin }
           sText := 'Kurde, wylosowałeś 1!';
           Liczba := 2;
        end; { koniec całego bloku }
    { ... }
  end;

Instrukcja case ma jeszcze jedną zaletę. Można w niej określić wartość od do - spójrz:

 case Liczba of
     1..10: sText := 'Kurde, wylosowałeś 1.. albo 10!';
     11..20: sText := 'Sam środek!';
     21..40: sText := 'Sam koniec';
  end;

Rozumiesz? Tekst "Sam koniec" zostanie wyświetlony wtedy, gdy zmienna Liczba będzie wartości od 21 do 40.

Typy


Nowe typy danych można w programie rejestrować stosując słowo kluczowe type. Np.:

type
  TPartie = (ppSLD, ppSamoobrona, ppPO, ppPiS);

Można również deklarować typy liczbowe:

type
  NowyTyp = 0..20;

W tym wypadku zmienna korzystająca z tego typu może mieć wartość od 0 do 20. Tak, teraz możemy normalnie korzystać z tego typu:

var NT : NowyTyp;

No, i wszystko jasne.. 

Napiszmy teraz przykładowy program korzystający z typów. Oto cała treść programu - przyjrzyj mu się, a ja zaraz go opiszę:

{
   Copyright (c) 2001 - Adam Boduch
}

program typy;

uses
  Windows;

type
  TPartie = (ppSLD, ppSamoobrona, ppPO, ppPiS);

var
  sText : PChar;
  Wybory : TPartie;

begin
  Randomize; // uruchamiamy bęben losujący :)

  Wybory := TPartie(Random(4));

  case Wybory of
    ppSLD: sText := 'Sebastian Florek pomógł! Miler się cieszy';
    ppSamoobrona: sText := 'Idziemy blokować drogi! Lepper na prezydenta!';
    PPPO : sText := 'Nie finansować partii z budżetu państwa!';
    ppPiS : sText := 'Przywrócić karę śmierci!';
  end;

  MessageBox(0, sText, 'Wybory 2001', MB_OK);
end.

Co my tu mamy... Zadeklarowałem nowy typ TPartie ( reguła nakazuje, aby nowy typ nazywać od litery T ). Później utworzyłem zmienną do tego typu i nazwałem ją Wybory. Następnie do zmiennej Wybory przypisana zostaje losowa wartość ( albo ppSLD, albo ppSamoobrona, ppPo lub ppPiS ). Zwróć uwagę na specyficzny zapis procedury losowania:

  Wybory := TPartie(Random(4));

Losujemy spośród czterech bo tyle mamy partii. To co tutaj użyłem nazywa się rzutowaniem. O rzutowaniu będzie mowa później. Na razie powiem, że jest to sposób na oszukanie kompilatora. 

Idziemy dalej: następnie w zależności od wylosowanej partii do zmiennej sText zostaje przypisany odpowiedni tekst. Następnie ten tekst zostaje wyświetlony w oknie. 

Rzutowanie

W poleceniu MessageBox trzeba podawać kilka parametrów. W przypadku drugiego i trzeciego parametru ma być to tekst, który zostanie wyświetlony w okienku. Ten tekst musi być typu PChar. Możesz więc napisać tak i będzie dobrze:

var
  Tekst, Tytul : PChar;
begin
  Tekst := 'Delphi jest fajne!';
  Tytul := 'Nie wiem?';
  MessageBox(0, Tekst, Tytul, MB_OK);

Tak, będzie dobrze. Dlaczego? Bo zmienne Tekst oraz Tytul są typu PChar. Teraz zamiast typu PChar napisz String:

var
  Tekst, Tytul : String;
begin
  Tekst := 'Delphi jest fajne!';
  Tytul := 'Nie wiem?';
  MessageBox(0, Tekst, Tytul, MB_OK);

Takie coś już nie przejdzie. Kompilator wyświetli błąd: [Error] Project1.dpr(21): Incompatible types: 'String' and 'PChar'. Kompilator próbuje Ci powiedzieć: "Co Ty wyprawiasz! Przecież typy PChar i typ String to dwie różne rzeczy!". Częściowo ma racje, ale to tylko głupia maszyna i da się ją oszukać. Do tego właśnie służy rzutowanie. Polega to na oszukaniu kompilatora. On "myśli", że jest to typ PChar, a ty stosujesz typ String:

var
  Tekst, Tytul : String;
begin
  Tekst := 'Delphi jest fajne!';
  Tytul := 'Nie wiem?';
  MessageBox(0, PChar(Tekst), PChar(Tytul), MB_OK);

Jest to rzutowanie typu String na typ PChar. Kompilacja się powiedzie i wszystko będzie dobrze. Fajnie, nie? Ale takie coś przejdzie tylko do czasu. Bo takie coś już nie wyjdzie:

var
  Licznik : Integer;

begin
  Licznik := 1;
  MessageBox(0, PChar(Licznik), '', MB_OK);
end.

Fachowca by powiedział: "To oczywiste. Przecież typy Integer i PChar zapisane są w odrębnych komórkach pamięci i bleblelble". Ktoś jednak pomyślał i stworzył:

Konwersja

Konwersja polega na wykorzystaniu gotowych poleceń do zmiany typów. Jeżeli np. typ Integer chcesz przedstawić w postaci tekstu ( String ) stosujesz polecenie IntToStr. Zaraz przetestujemy to w praktyce. Żeby użyć tego polecenia do listu modułów uses musisz dodać moduł SysUtils. Gotowe? A, wiesz co? Dodaj jeszcze moduł Dialogs. Powinno to wyglądać tak:

uses
  Windows,
  SysUtils, { <-- ten moduł jest konieczny, aby zastosować konwersje }
  Dialogs; { <-- ten zawiera jedno polecenie - ShowMessage }

Moduł Dialogs zawiera polecenie ShowMessage, którego będziemy używali. Jak łatwo się domyśleć polecenie to wyświetla w oknie tekst ( to taka odmiana polecenia MessageBox ). Wpisz taki tekst do programu ( komentarze możesz oczywiście pominąć ):

var
  Data : TDateTime; { nowy typ - przechowuje datę }
  Rozdzial : Integer; { znany już typ Integer }
  Imie : String; { również znany typ String }
begin
  Data := Now; { przypisuje zmiennej aktualną datę }
  Rozdzial := 2;
  Imie := 'Komputer';
  ShowMessage('Cześć, mam na imię ' + Imie + ', a Ty właśnie czytasz ' + IntToStr(Rozdzial) +
  ' rozdział książki o Delphi! Dzisiaj jest: ' + DateToStr(Data));
end.

Hohhoho. To coś trudniejszego, ale jakże przydatne w programowaniu. Zacznijmy od początku. Polecenie ShowMessage ma tylko jeden parametr - jest nim tekst, który ma być wyświetlony w okienku. Tekst ten ma być typu String. Operator + służy do połączenia odpowiednich części ( zmiennych ). Po prostu gdy chcesz w dane miejsce wstawić wartość zmiennej to kończysz tekst stawiając apostrof, następnie znak + mówiący o podłączeniu zmiennej. Później wpisujesz zmienną, która ma być w to miejsce wstawiona. W powyższym wypadku jeżeli będziesz chciał wstawić zmienną, która nie jest typu String trzeba będzie zastosować konwersje. Program możesz uruchomić i sprawdzić jak działa. 

Oto inne polecenia konwertujące typy:

IntToStr - konwertuje zmienna "Integer" na "String"
StrToInt - "String" na "Integer"
StrPas - "PChar" na "String"
StrPCopy - "String" na "PChar"
TimeToStr - zmienna "Time" na "String"
DateToStr - zmienna "Date" na "String"
StrToDate - "String" na "Date"
StrToTime - "String" na "Time"
CurrToStr - "Currency" ( zmiennoprzecinkowy ) na "String"
StrToCurr - "String" na "Currency"
FloatToStr - zmiennoprzecinkowy na "String"

Tablice

Tablice to zbiór zmiennych tego samego typu. Popatrz zamiast deklarować kilka zmiennych tego samego typu:

var
  Zmienna1, Zmienna2, Zmienna3, Zmienna4 : String;

Możesz zapisać to w formie tablicy:

var
  Zmienna : array[0..3] of String;

Tablice deklaruje się za pomocą słowa kluczowego array. Następnie w nawiasach klamrowych wpisujesz z ilu elementów ma się składać tablica. W tym wypadku z czterech ( 0 to także element tablicy ).  Na końcu jakiego typu mają być elementy tablicy. Teraz jeżeli chcesz poszczególnym elementom przypisać jakieś wartości robisz tak:

Zmienna[0] := 'Adam';
Zmienna[1] := 'Marta';
Zmienna[2] := 'Zuzia';
Zmienna[3] := 'Bronia';

Nic nadzwyczajnego. Elementy tablicy mogą być wykorzystane jak zwykłe zmienne:

ShowMessage(Zmienna[0] + ' ' + Zmienna[1]);

Począwszy od Delphi 4 można deklarować w programie tablice dynamiczne. Tzn., podczas pisania programu nie określasz z ilu elementów będzie się składać tablica - robisz to później. 

var Dynamiczna : array of String;

Z ilu elementów składa się tablica? Nie wiadomo. Deklarujesz to później. W tym celu musisz zastosować polecenie SetLength. Oto przykładowy program:

{
   Copyright (c) 2001 - Adam Boduch
}

program tablica;

uses
  Windows,
  Dialogs;

var Tab : array of String; // tablica dynamiczna

begin
  SetLength(Tab, 4); // deklarujesz, z ilu elementów będzie się składać tablica
  { wpisujesz elementy }
  Tab[0] := 'Adam';
  Tab[1] := 'Marta';
  Tab[2] := 'Zuzia';
  Tab[3] := 'Bronia';

  Randomize;
  ShowMessage(Tab[Random(4)]);
end.

W tym wypadku w okienku pojawi się losowo wybrany element tablicy. Pytanie: W jakich wypadkach stosować tablice dynamiczne? W takich wypadkach, w których programista sam nie wie podczas pisania programu z ilu elementów będzie się składać tablica. Może to być np. określane podczas trwania programu przez użytkownika. 

Z tablicami wiążą się jeszcze dwa bardzo przydatne polecenia - Low i High. Podają one kolejno najmniejszy element tablicy jak i największy. Jeżeli więc tablica wygląda tak:

var Tab : array[10..100] of String;

To zastosowanie polecenia Low(Tab) zwróci liczbę 10 - pierwszy element tablicy. Polecenie High natomiast zwraca liczbę 100 - ostatni element tablicy. 

Pozostało jeszcze omówienie tablic dwuwymiarowych. Oczywiście tablice można deklarować także jako stałe. 

const
  Tab : array[0..1, 0..2] of String =
  (('Fiat', 'Uno', '45 KM'),
   ('Fiat', 'Punto', '80 KM'));

W tym wypadku elementów będzie dwa, ale każdy z elementów będzie zawierał po 3 pozycje. Może z początku nie będzie to dla Ciebie jasne. Teraz trzeba odwołać się do poszczególnych elementów. Np. element 'Fiat' znajduje się pod indeksem [0][0]. 

const
  Tab : array[0..1, 0..2] of String =
  (('Fiat', 'Uno', '45 KM'),
   ('Fiat', 'Punto', '80 KM'));

begin
  ShowMessage(Tab[0][0] + Tab[0][1] + Tab[0][2]);
end.

Element 'Uno' jest pod indeksem [0][1]. Np. element 'Punto' jest pod indeksem [1][1]. Rozumiesz? 

Procedury i funkcje

Procedury i funkcje obecne są w każdym języku programowania ( w C++ są tylko funkcje ). Są to wydzielone bloki kodu, które wykonują jakąś czynność. Np. potrzebujesz wiele razy użyć jakiegoś kodu. Żeby nie zapisywać tego tyle razy możesz użyć procedury. Piszesz kod tylko raz a jeżeli będziesz go potrzebował w dalszej części programu to po prostu piszesz nazwę tej procedury. 

Procedury deklaruje się tak:

procedure MojaPierwszProcedura;
begin
  { tutaj wpisujesz kod procedury }
end;

Funkcje deklaruje się tak:

function MojaPierwszaFunkcja : Integer;
begin
  { kod funkcji }
end;

Zauważ specyficzną budowę funkcji. Funkcja ZAWSZE musi zwrócić rezultat wykonanej operacji, a procedura nie.  Napiszemy program, który będzie podnosił liczbę do potęgi. Ale od początku. Funkcje mogą zawierać parametry. Nie muszą, ale mogą. Parametry funkcji to dane potrzebne do jej wykonania. Oto przykład deklaracji funkcji z parametrem:

function DoPotegi(Liczba : Integer) : Integer;

W takim wypadku jeżeli będziesz chciał wykonać funkcję DoPotegi będziesz musiał podać jeden parametr, którym jest liczba typu Integer. Oczywiście parametrów może być o wiele więcej:

function DoPotegi(Liczba1 : Integer; Liczba2 : Integer) : Integer;

Żeby ułatwić sobie zadanie możesz to napisać w ten posób:

function DoPotegi(Liczba1, Liczba2 : Integer) : Integer;

To co? Piszemy ten program? Cały kod wygląda tak:

{
   Copyright (c) 2001 - Adam Boduch
}

program potega;

uses
  Windows, SysUtils, Dialogs;

function DoPotegi(Liczba: Integer) : Integer;
begin
  Result := Liczba * Liczba;
end;

begin
  ShowMessage(IntToStr(DoPotegi(2)));
end.

Zwróć uwagę na naszą funkcje. Po słowie kluczowym Result deklarujemy rezultat wykonanej funkcji. Rezultatem jest mnożenie dwóch parametrów dzięki czemu mamy funkcję, która podnosi liczbę do potęgi. W programie rezultat wykonania procedury zostaje wyświetlony w okienku. Teraz rozumiesz czym się różni procedura od funkcji? Gdybyśmy zamiast funkcji użyli procedury to program by się nie skompilował ( procedura nie może zwrócić rezultatu wykonanej operacji ).

Bodajże także od Delphi 4 wprowadzono możliwość tzw. przeciążania procedur i funkcji. Polega to na tym, że w programie mogą istnieć dwie procedury/funkcje o tej samej nazwie pod warunkiem, że ich parametry będą różne. W takim wypadku funkcję/procedurę trzeba opatrzyć dodatkowo klauzurą overload. 

function DoPotegi(Liczba: Integer) : Integer; overload;
begin
  Result := Liczba * Liczba;
end;

function DoPotegi(Liczba: Currency) : Currency; overload;
begin
  Result := Liczba * Liczba;
end;

W pierwszym przypadku Liczba jest typu Integer, a w drugim typu Currency. Przypomnę, że typ Currency to typ zmiennoprzecinkowy. Teraz możemy wykorzystać jedną bąć drugą funkcje: 

  DoPotegi(2.2);

Pytanie: "Skąd program ma wiedzieć, która funkcję wykorzystać?". Po parametrze. Jeżeli parametr jest zmiennoprzecinkowy to znaczy, że chcesz wywołać funkcję drugą, jeżeli jest to liczba cała - pierwszą procedurę. 

Istnieje możliwość deklaracji "procedury w procedurze". Np:

procedure Glowna;
   procedure Child;
   begin
      { kod procedury }
   end;
begin

end;

Teraz możesz procedurę Child używać wiele razy w procedurze Glowna jeżeli nie chcesz wiele razu przepisywać treści procedury Child. 

Deklarując procedurę lub funkcję można przypisać im wartości domyślne tak jak w wypadku zmiennych. Oto przykład: 

procedure NowaGra(ImieGracza : String; NumerGracza : Integer = 1);

Teraz jeżeli chcesz daną procedurę wywołać możesz, ale nie musisz podawać drugiego parametru:

procedure NowaGra('Bleblelele');

Albo:

procedure NowaGra('Bleblgble', 2);

Tak i tak będzie dobrze. 

Moduły

Dotąd wykorzystywaliśmy moduły gotowe, dostarczone wraz z Delphi. Teraz stworzymy swój moduł. Możemy w nim zamieszczać procedury i funkcje, które później będą wykorzystywane w programie. Nowy moduł możesz utworzyć z menu File wybierając New. W oknie, które się pojawi kliknij na ikonę Unit. W edytorze kody pojawi się nowa zakładka. Jej zawartość wygląda tak:

unit Unit1;

interface

implementation

end.

Otóż KAŻDY moduł musi się składać ze słów kluczowych interface oraz implementation. W sekcji Interface wpisuje się tylko nagłówek procedury lub funkcji, czyli jej nazwę. Fachowo nazywa się to deklaracją. W sekcji Implementation natomiast treść procedury/funkcji. Zapisz moduł wybierając z menu File -> Save All. Podaje nazwę modułu - ja podałem nazwę Tools - Delphi stworzył plik Tools.pas. Zauważysz, ze w programie, w miejscu gdzie wypisane są moduły dodany został nowy - nasz:

uses
  Windows,
  SysUtils,
  Dialogs,
  Tools in 'Tools.pas';

Dobrze. Zajmijmy się naszym modułem. Doprowadź go do takiej postaci:

unit Tools;

interface

uses Windows;

 { deklaracje }
  procedure About;
  function DoPotegi(Liczba: Integer) : Integer;

implementation

  { definicje }

function DoPotegi(Liczba: Integer) : Integer;
begin
  Result := Liczba * Liczba;
end;

procedure About;
begin
  MessageBox(0, '© by A.B.', 'O programie...', MB_OK);
end;

end.

 Zapisz swój moduł. Teraz w programie do listy uses możesz dodać nazwę swojego modułu - Tools. W programie możesz wykorzystać procedury, które zadeklarowałeś w swoim module pisząc po prostu nazwę tej procedury.

Musisz wiedzieć, że to co jest zawarte w sekcji implementation modułu nie będzie widoczne dla innych modułów. Znaczy to, że program będzie ciągle wyświetlał błąd, że nie wie co to za procedura, której chcesz użyć. Możesz więc pisać w module procedury bez deklarowania ich w sekcji interface pod warunkiem, że nie chcesz, aby były wykorzystywane w innych modułach. 

Dodatkowo masz możliwość umieszczenia w module dwóch słów kluczowych, które są opcjonalne. Są to initialization oraz finalization. Umieszczasz je przed słowem kluczowym end. ( z kropką! ). Po słowie initialization możesz zapisać instrukcje, które będą wykonywane na samym początku, a po finalization są zapisywane instrukcje, które mają być wykonane przed zakończeniem programu. 

{ instrukcje modułu... }
initialization
   ShowMessage('Rozpoczącie pracy z modułem.');
finalization
  ShowMessage('Koniec pracy z modułem.');
end. 

Rekordy

Rekordy to taka paczuszka. Jest to jeden typ ( lub zmienna ), która może zawierać w sobie inne zmienne. 

type
  TDane = record { <-- brak średnika na końcu! }
    Imie: String;
    Nazwisko: String;
  end;

Właśnie zadeklarowałeś nowy typ. Jest to rekord. Zawiera on w sobie inne zmienne. Rekordy mają specyficzną budowę. Deklaruje się je poprzez słowo kluczowe record. Na początku zapewne trudno Ci będzie zrozumieć istotę stosowania rekordów. Sprawdźmy to w praktyce. Na początek trzeba stworzyć zmienną,  która będzie wskazywać na rekord:

var Dane : TDane; // zmienna wskazuje na rekord

Następnie należy wypełnić pola rekordu. Cały kod programu wygląda teraz tak:

{
   Copyright (c) 2001 - Adam Boduch
}

program rekord;

uses
  Windows;

type
  TDane = record { <-- brak średnika! }
    Imie: String;
    Nazwisko: String;
  end;

var Dane : TDane; // zmienna wskazuje na rekord

begin
  Dane.Imie := 'Adam';
  Dane.Nazwisko := 'Boduch';
  MessageBox(0, PChar('Nazywam się: ' + Dane.Imie + ' ' + Dane.Nazwisko), 'Hiehie', MB_OK);
end.

Użyliśmy tutaj operatora, który nie został omówiony wcześniej. Operator ten to . Tak kropka... Jest to operator odwołania. W przyszłych rozdziałach operatora tego będziemy używali całkiem często. Za jego pomocą możemy odwołać się do poszczególnych elementów obiektu. W tym wypadku elementem obiektu są zmienne zawarte w rekordzie, a samym obiektem rekord. Tak więc do zmiennych zawartych w rekordzie przypisujemy wartości. Następnie w okienku wartości te są wyświetlone. 

W Delphi 5 ( w Delphi 4 także ) zastosowano pewne ułatwienie. Mamy możliwość "podglądu" jakie elementy znajdują się w rekordzie. Wystarczy, że po nazwie obiektu ( tzn., zmiennej, która wskazuje na rekord ) postawimy kropkę i odczekamy parę sekund. Pojawi się takie okienko:

Żeby stworzyć rekord niekoniecznie trzeba deklarować nowy typ. Można to zrobić od razu w zmiennej:

var
  Dane : record { <-- brak średnika! }
    Imie: String;
    Nazwisko: String;
  end;

Trzeba jednak uważać, aby nie postawić znaku równości ( = ). Jeżeli deklarujesz rekord jako zmienną to stawiasz znak dwukropka tak jak to zostało pokazane na powyższym kodzie. 

Podczas zapisywania rekordów możesz przed słowem record dodać jeszcze jedno - packed. Powoduje ono zmniejszenie rozmiaru jaki zajmuje rekord w pamięci komputera. Różne zmienne zajmuje w pamięci określoną ilość bajtów. W rekordzie dodaje się wszystkie zmienne ( ilość bajtów zajmowanych przez zmienne ) i wiemy ile zajmuje w pamięci rekord. Jeżeli nie użyjesz słowa packed to ta ilość zaokrąglana jest dodatkowo do 8 bajtów. Tak więc uzycie słowa kluczowego packed powoduje pakowanie rekordu. 

Instrukcja wiążąca with

Instrukcja ta nie robi nic specjalnego. Po prostu ułatwia programiście pracę. Zamiast pisać:

  Dane.Imie := 'Adam';
  Dane.Nazwisko := 'Boduch'; 

Można zastosować instrukcję wiążącą i zmniejszyć ilość wypisanych słów: 

with Dane do
begin
  Imie := 'Adam';
  Nazwisko := 'Boduch';
end;

Wszystkie elementy 

 

Operacje matematyczne

W Object Pascal'u wykonywanie operacji matematycznych to nic trudnego. Wykorzystuje się podstawowe operatory jak = + -  / *. Wszystko jasne. Chciałbym jednak omówić jeszcze dwie komendy - div, mod. Pierwsza z nich oddziela resztę z dzielenia, a druga pozostawia tylko resztę z dzielenia. Spójrz na poniższy przykład:

 

var
  X, Y, Z : Integer;

begin
  X := 13;
  Y := 4;
  Z := X / Y;
end.

W przykładzie tym próbujemy podzielić dwie liczby - 13 na 4. Przy próbie skompilowania pojawia się błąd: [Error] rekord.dpr(17): Incompatible types: 'Integer' and 'Extended'. Skąd on się bierze? Próbujemy podzielić dwie całkowite liczby, a następnie wynik przypisać do zmiennej, która także może przechowywać tylko liczby całkowite. Kompilator próbuje nam powiedzieć, że nie wie jaki będzie rezultat wykonanego działania i czy wynik nie będzie zmiennoprzecinkowy. Jeżeli byłby zmiennoprzecinkowy to nie może przypisać go do typu całkowitego. Jeżeli zamiast znaku / zastosujesz div: 

  Z := X div Y;

To wszystko będzie dobrze. Ewentualna reszta z dzielenia będzie obcięta. Możesz używać operatora / ale tylko wtedy jeżeli zmienne będą typu zmiennoprzecinkowego - np. Currency: 

var
  X, Y, Z : Currency;

begin
  X := 13;
  Y := 4;
  Z := X / Y;
end.

Jeżeli wynik będzie z resztą to program ją wyświetli - jeżeli nie - wyświetli bez reszty. Istnieją w Object Pascal'u procedury, które zaokrąglają wartość zmiennoprzecinkową do góry lub na dół. Round zaokrągla do góry, a Trund na dół. 

Round(2.55566); // w rezultacie da 3
Trund(2.55566); // w rezultacie da 2

Pętle

Czas przejść do elementu programowania, który występuje we wszystkich językach programowania czyli pętli...

Pętla - instrukcja, która jest wykonywana tak długo aż nie zostanie spełniony warunek jej zakończenia. Pętle można zastosować do ciągłego wykonywania tej samej czynności. 

 

Załóżmy, ze chcesz wykonać kilka razy tę samą czynność, czyli np. kilka razy wyświetlić to samo okienko. Zamiast pisać to tak: 

ShowMessage('okienko');
ShowMessage('okienko');
ShowMessage('okienko');

można zastosować pętle. 

Pętla for

Jest to najprostsza ze wszystkich pętli. Stosuj ją wtedy gdy wiesz konkretnie podczas pisania programu ile razy ta pętla ma być używana. Budowa pętli for przedstawia się następująco:

var
  I : Integer;

begin
  for I := 0 to 3 do
   { polecenia }
end.

W tym wypadku instrukcje zawarte po słowie "do" będą wykonywane czterokrotnie. Zmienna I przechowuje wartości. Może to zabrzmieć niezrozumiale więc wyjaśniam. Po pierwszym wykonaniu danej czynności zmienna I ma wartość 0, po drugim wykonaniu wartość 1, itd., itd. Jeżeli stosujesz pętle for to zawsze musisz używać zmiennej. Regułą jest nadawanie zmiennej nazwy "I". Wiadomo wtedy, że dana zmienna wykorzystywana jest do pętli for. 

Ok, budowa pętli przedstawia się następująco: na samym początku słowo kluczowe for, następnie wartość od której pętla ma się rozpoczynać - w naszym wypadku jest to 0. Po słowie "to" wartość na której pętla ma się zakończyć. Na samym końcu słowo "do" i operacje do wykonania. Jeżeli po słowie "do" jest więcej niż jedna instrukcja to należy wszystko wsiąść dodatkowo w blok begin i end. Oto treść pierwszego programu z pętlami:

{
   Copyright (c) 2001 - Adam Boduch
}

program petlafor;

uses
  Windows, Dialogs, SysUtils;

var
  I : Integer;

begin
  for I := 1 to 3 do
    ShowMessage('To jest okienko nr: ' + IntToStr(i));
end.

Dodatkowo w oknie jest wyświetlony nr. okienka! 

Jak dotąd wykonywaliśmy pętle od dołu do góry. Można odwrotnie - od góry do dołu. Wtedy zamiast słowa kluczowego "to" stosujemy słowo "downto". 

  for I := 3 downto 1 do
    ShowMessage('To jest okienko nr: ' + IntToStr(i));

Pętla repeat

Przy okazji omawiania tej pętli poznasz nowe właściwości funkcji MessageBox. Otóż oprócz standardowego przycisku OK w okienku mogą być wyświetlone również inne. Np. Tak i Nie. Funkcja MessageBox zwraca również jaki przycisk został naciśnięty. W naszym przykładzie wyświetlane będzie okienko dopóki użytkownik nie naciśnie przycisku Tak. 

{
   Copyright (c) 2001 - Adam Boduch
}

program petlaepeat;

uses
  Windows;

var
  Results : Integer;

begin
  { wyświetlaj okienko dopóki użytkownik kliknie na przycisk Tak }
  repeat
    Results := MessageBox(0, 'Cześć! Lubisz Delphi?', 'Pytanie', MB_YESNO);
  until Results = ID_YES;
end.

Zmienna Results przechowuje dane jaki przycisk został naciśnięty. Jeżeli użytkownik nacisnął przycisk Tak to zmienna Results zawiera wartość ID_YES. Jeżeli kliknął na Nie to zawiera ID_NO. Skąd to wiem? Poczytaj pomoc do Delphi. Naciśnij F1 i wpisz Messagebox, a zobaczysz informację na temat tej funkcji. 

Budowa pętli jest następująca: najpierw słowo kluczowe repeat po którym następują czynności, które będą powtarzane. Na końcu słowo unitl po którym następuje sprawdzenie, czy użytkownik rzeczywiście nacisnął przycisk "Tak". 

Pętla repeat BĘDZIE wykonywana przynajmniej raz w przeciwieństwie do...

Pętla while

Która to może nie zostać wykonana wcale jeżeli warunek jej wykonania został spełniony. Pętla repeat jest rzadziej używana od pętli while, ale są one do siebie podobne. Oto przykład:

var
  I : Integer;

begin
  I := 3; // przypisanie początekowej wartości zmiennej I
  while I > 0 do // wykonuj dopóki wartość zmiennej będzie większa od 0
  begin
    ShowMessage('Okienko nr: ' + IntToStr(i)); // wyświetl okienko
    Dec(i); // zmniejsz o jeden wartość zmiennej I
  end;
end.

Zacznijmy od końca, czyli objaśnienie polecenia Dec. Otóż zmniejsza ono o jeden wartość zmiennej I. Znaczy to samo co:

I := I -1;

Istnieje także polecenie Inc, które zwiększa wartość zmiennej o jeden. Oba polecenia mogą zawierać jeden dodatkowy parametr, który "mówi" o ile wartość ma być zwiększona lub zmniejszona. Domyślnie jest to 1, ale możesz zapisać:

Inc(i, 2);

I będzie zwiększona o 2.  

Wróćmy do pętli. Na samym początku zmiennej I należy przypisać wartość domyślną. W naszym przypadku będzie to 3. Sprawdź, co będzie jeżeli nie zadeklarujesz tej wartości. Program się wogule nie wykona. Tzn. nie program, a pętla. Otóż w przeciwieństwie do pętli repeat pętla while może się nie wykonać jeżeli będzie spełniony warunek jej wykonania. A jeżeli nie przypiszesz zmiennej I wartości domyślnej to Delphi zrobi to za Ciebie i będzie to cyfra 0. W pętli jest dokładnie napisane, że ma się wykonać TYLKO wtedy, gdy zmienne I będzie większa od 0. Po wykonaniu czynności, czyli wyświetleniu okienka wartość zmiennej I zostaje zmniejszona. 

Słowo o algorytmach 

Algorytm - skończona sekwencja komend, która ma na celu rozwiązanie jakiegoś problemu. Innymi słowy jest to rozwiązanie jakiegoś problemu. 

 

Napiszmy jakiś algorytm. Zadanie będzie wyglądało następująco: przeszukać w tablicy interesującego elementu, a następnie zwrócić indeks pod którym się znajduje. Najpierw deklarujmy tablice: 

const
 { deklarujemy tablice imion wśród których będziemy szukać }
  Tablica : array[0..4] of String =
  ('Zenowefa', 'Anita', 'Aga', 'Magda', 'Sylwia');

Teraz najważniejsza część zadania - napisanie algorytmu, który realizował będzie proces przeszukiwania. 

function Szukaj(Element: String) : Integer;
var
  I : Integer;
begin
{
   Funkcja poszukuje w tablicy elementu określonego w parametreze "Element".
   Jeżeli znajdzie to zwraca numer pod którym element się znajduje.
}
  I := 0; // wartość początkowa
  { Kontynuj dopóki nie przeszukasz całej tablicy i dopóki nie znajdziesz elemntu }
  while (I < High(Tablica)) and (Tablica[i] <> Element) do
    Inc(i); // zmień obszar poszukiwać o jeden
   { jeżeli nie znajdziesz to zwróć -1 }
    if Tablica[i] <> Element then Result := -1 else
  Result := i; { jeżeli znajdziesz to zwróć numer pod którym się znajduje }
end;

Tylko się nie przestrasz! Większą część stanowią komentarze. Wykorzystałem do tego pętle while. Na samym początku deklaruje wartość zmiennej I - od tego elementu zaczynamy poszukiwania. Następnie następuje sprawdzenie, czy przeszukano już całą tablicę i czy "przeglądany" element jest różny od szukanego. Jeżeli tak to zwiększ obszar poszukiwań o jeden i sprawdź, czy "przeglądany" aktualnie element nie jest tym którego szukamy. Jeżeli nie znaleziono to zwróć -1, a w przeciwnym wypadku zwróć indeks poszukiwanego elementu.  

{
   Copyright (c) 2001 - Adam Boduch
}

program algo1;

uses
  Windows, Dialogs, SysUtils;

const
 { deklarujemy tablice imion wśród których będziemy szukać }
  Tablica : array[0..4] of String =
  ('Zenowefa', 'Anita', 'Aga', 'Magda', 'Sylwia');

function Szukaj(Element: String) : Integer;
var
  I : Integer;
begin
{
   Funkcja poszukuje w tablicy elementu określonego w parametrze "Element".
   Jeżeli znajdzie to zwraca numer pod którym element się znajduje.
}
  I := 0; // wartość początkowa
  { Kontynuuj dopóki nie przeszukasz całej tablicy lub dopóki nie znajdziesz elementu }
  while (I < High(Tablica)) and (Tablica[i] <> Element) do
    Inc(i); // zmień obszar poszukiwać o jeden
   { jeżeli nie znajdziesz to zwróć -1 }
    if Tablica[i] <> Element then Result := -1 else
  Result := i; { jeżeli znajdziesz to zwróć numer pod którym się znajduje }
end;

begin
  if Szukaj('Anita') <> -1 then  // sprawdź, czy znaleziono
    ShowMessage('Znalazłem szukany element pod indeksem: ' + IntToStr(Szukaj('Anita'))) else
  ShowMessage('Nie znalazłem');
end.

Czasem zamiast poszukiwać tej, czy innej komendy do wykonania jakiegoś zadanie wystarczy, że trochę pomyślimy i być może uda nam się dane zadanie zrealizować. Nie jest to łatwe, przyznaje. Ale kto powiedział, że programowanie jest łatwe? 

Polecenia Continue i Break

Polecenia te wykorzystywane są wyłącznie w połączeniu z pętlami. Pierwsze z nich powoduje przejście do samego początku pętli. Program nie wykonuje dalszego członu pętli, a powraca na jej początek ( sprawdza warunek jej zakończenia ). Drugie natomiast powoduje natychmiastowe przerwanie działania pętli - wyjście z niej. Oto przykład użycia polecenia Continue:

const
 { deklarujemy tablice imion wśród których będziemy szukać }
  Tablica : array[0..4] of String =
  ('Zenowefa', 'Anita', 'Aga', 'Magda', 'Sylwia');

procedure Szukaj(Element: String);
var
  I : Integer;
begin
  for I := Low(Tablica) to High(Tablica) do // punkt A
  begin
    if Tablica[i] = Element then  // jeżeli element zostanie znaleziony...
    begin
      ShowMessage('Znalazłem - kontynuuje'); //... wyświetl informacje
      Continue;  // do punktu A
      ShowMessage('Ten tekst się nie wyświetli...');
    end;
  end;
end;

Drugie polecenie Continue nigdy się nie wyświetli. Spowodowało to polecenia Continue. Dzięki niemu nastąpił skok do początku pętli. Teraz kolejny bardzo podobny przykład tyle, że z poleceniem Break:

const
 { deklarujemy tablice imion wśród których będziemy szukać }
  Tablica : array[0..4] of String =
  ('Zenowefa', 'Anita', 'Aga', 'Magda', 'Anita'); { dwa razy Anita! }

procedure Szukaj(Element: String);
var
  I : Integer;
begin
  for I := Low(Tablica) to High(Tablica) do 
  begin
    if Tablica[i] = Element then  // jeżeli element zostanie znaleziony...
    begin
      ShowMessage('Znalazłem - przerywam'); //... wyświetl informacje
      Break;  // koniec programu
      ShowMessage('Ten tekst się nie wyświetli...');
    end;
  end;
end;

Zwróć uwagę na to, że przy deklaracji tablicy dwa razy wystąpiło imię "Anita". Lecz program jeżeli znajdzie je raz nie będzie tego robił powtórnie. Dlaczego? Bo zastosowaliśmy komendę Break, która spowodowała właśnie zakończenie pętli. 

Etykiety

Większość programistów uważa, że stosowanie etykiet jest "nieetyczne" i nie ma takiej konieczności. Ja jednak opiszę tutaj o co chodzi w tych etykietach. Najprościej mówiąc powodują one "skok" do wybranego punktu. 

procedure Szukaj(Element: String);
var
  I : Integer;
label
  Info;
begin
  for I := Low(Tablica) to High(Tablica) do
  begin
    if Tablica[i] = Element then  // jeżeli element zostanie znaleziony...
    begin
      ShowMessage('Znalazłem'); //... wyświetl informacje
      goto Info;
    end;
  end;
  Info:
  ShowMessage('już znalazłem');
end;

Na początek etykietę należy zadeklarować. Robi się to za pomocą słowa kluczowego Label. Następnie nazwa etykiety. Jeżeli chcesz do niej natychmiast przeskoczyć stosujesz słowo kluczowe goto, a następnie nazwę etykiety. To nie wszystko bo co ma etykieta robić? Gdzieś w programie musisz napisać jej kod. Najpierw piszesz nazwę etykiety, dwukropek, a później już kod. 

Być może podałem nie jasny przykład, ale chyba rozumiesz o co chodzi? 

Funkcje Pred i Succ

Funkcje te mają prawie takie same działanie jak polecenia Inc oraz Dec. Jedyna ważna różnica to taka, że Inc i Dec to procedury, a Pred i Succ to funkcje. 
Tak więc funkcja Pred zmniejsza o jeden - tzn:

Pred(100);

daje rezultat 99. Użycie funkcji Succ powoduje zwiększenie o jeden. Także napisanie Succ(99) daje liczbę 100. 

Operowanie na łańcuchach

Wiesz już, że tekst możesz połączyć za pomocą operatora +. To nie wszystko. Przede wszystkim do łańcuchów możesz dodawać znaki ASCII. Zakładam, że wiesz co to są te znaki ASCII. Wstawia się je poprzedzając kod znakiem #. Np. znak ENTER ma kod 13 i jeżeli chcesz zrobić linię odstępu piszesz tak:

  MessageBox(0, 'Pierwsza linia' + #13 + 'Druga linia', '', MB_OK);

Pewnie teraz powiesz: "Dobrze, ale skąd mam wiedzieć jaki numer posiada konkretny znak?". Skorzystaj z programu mojego autorstwa, który znajdziesz na dyskietce dołączonej do książki. 

Możesz także mieć dostęp do poszczególnych znaków danego ciągu znaków. Wszystko za sprawą nawiasów klamrowych []:

var
  Imie : String;
  Znak : Char;

begin
  Imie := 'Delphi';
  Znak := Imie[1];
  ShowMessage('Pierwsza linia mojego imienia to: ' + Znak);
end.

Zgadnij co zostanie wyświetlone w okienku? Tak, litera D. 

Również deformacji z łańcuchami możesz dokonać stosując typ PChar. Oto przykład:

var
  S : PChar;

begin
  S := 'Delphi';
  S := S + 1;
  ShowMessage(S);
end.

Z wyglądu może to wydać się niezrozumiałe. Do zmiennej tekstowej dodałem cyfrę 1? W rzeczywistości zmniejszyłem długość tej zmiennej obcinając pierwszą literę. To możliwe jest tylko w typie PChar - String już tego nie umożliwia. 

Deklarując zmienną typu String możesz określić z ilu znaków będzie się mogła max. składać. Robi sięto tak:

var
  S : String[20];

W takim wypadku zmienna S będzie ciągiem znaków o max. długości 20.

Bardziej skomplikowane zagadnienia związane z operacją na łańcuchach poznasz czytając dalsze rozdziały książki.

Wskaźniki

Nie zawaham się powiedzieć, że wskaźniki to najbardziej skomplikowany element programowania dla początkujących programistów. Otóż wskaźnik wskazuje na adres zmiennej w pamięci. Ale zacznijmy od początku. Normalnie pamięć w Delphi przydzielana jest statycznie. Tzn., że w trakcie uruchamiania programu Windows "wie" ile dla niego przeznaczyć pamięci. Takie coś nosi nazwę Stosu.


Sterta
jest to cała dostępna pamięć komputera.  

 

Dzięki wskaźnikom możemy przydzielać pamięć dynamicznie w trakcie działania programu. Oto przykład: 

type
  TRec = record
    Imie : String[20];
    Nazwisko : String[20];
    Kod : Word;
  end;
  PRec = ^TRec;

var
  Rec : PRec;
begin
  New(Rec); // przydzielamy pamięć dla rekordu

  Rec^.Imie := 'Jan';
  Rec^.Nazwisko := 'Kowalski';
  Rec^.Kod := 00000;

  Dispose(Rec); // zwalniamy pamięć
end.

Wskaźnik do danego obiektu można utworzyć za pomocą znaku ^.  W tym wypadku stworzyliśmy rekord, a następnie stworzyliśmy wskaźnik do rekordu. Późnie deklarujemy zmienną, która jest typu PRec, czyli wskaźnikowego. Zanim rozpoczniemy przydzielanie danych do rekordu musimy przydzielić pamięć do rekordu. Później trzeba na końcu pamięć zwolnić za pomocą polecenia Dispose. Są jeszcze inne sposoby na przydzielanie pamięci, ale na razie nie zawracaj sobie tym głowy. 

Tak jak napisałem wcześniej wskaźniki wskazują na adres pamięci, w której zapisana jest wartość danej zmiennej. Mając adres komórki pamięci możemy tę wartość zmienić.

var
  I : Integer;
  P : ^Integer;
begin
  I := 1; // przypisujemy wartość zmiennej
  P := @I; // uzyskujemy komórkę pamięci, w której zapisana jest wartość zmiennej I
  P^ := 2; // nadajemy nową wartość w komórce
  { teraz następuje wyświetlenie }
  ShowMessage('Zmienna I po modifikacji ma wartość: ' + IntToStr(i));
end.

Jeżeli chcemy odczytać wartość danej zmiennej to kompilator odczytuje wartość komórki, w której dana zmienna jest zapisana. Przy pomocy takiego zapisu:

P := @I;

Otrzymujemy adres komórki pamięci, w której zapisana jest zmienna I. Adres ten zapisany jest do wskaźnika P. Teraz w tej komórce chcesz wpisać nową wartość - robisz tak:

P^ := 2; // znak ^ jest potrzebny gdyż jest to wskaźnik!

Podsumowanie

W tym rozdziale nauczyłeś się bardzo dużo. Możesz powiedzieć, że podstawy Object Pascal'a masz opanowane. Jeżeli czegoś nie rozumiesz to przeczytaj jeszcze raz ten rozdział.. Jeżeli nadal nie będziesz rozumiał to przejdź do następnego rozdziału. Później powróć do tego i wszystko się wyjaśni. W następnym rozdziale zajmiemy się programowaniem obiektowym bo w końcu do tego służy Delphi. Powodzenia!