W Delphi procedura nie zwraca rezultatu za pomocą konstrukcji właściwej funkcjom. Jeżeli wydzielony blok kodu ma obliczyć wartość i przekazać ją do miejsca wywołania, należy użyć funkcji, której typ zwracany zapisuje się po dwukropku w nagłówku.
Procedura wykonuje określoną czynność, natomiast funkcja dodatkowo zwraca rezultat działania. Przykładowo poprawna deklaracja procedury wygląda tak: procedure Moja_Procedura(X, Y : Integer);, a funkcji tak: function Moja_Funkcja(X, Y : Integer) : Integer;
Procedura a funkcja
Procedura/funkcja to wydzielony blok kodu realizujący określone zadanie. Funkcje od procedury odróżnia to, że ta pierwsza zwraca rezultat działania.
Ktoś mądry wymyślił wtedy, że można by było dzielić program na mniejsze części, tzw. procedury. Teraz, właściwie w każdym języku programowania można pewien fragment kodu umieścić w procedurze i za każdym razem, kiedy zajdzie potrzeba jego wykonania wywołać procedurę!
Jeżeli napisano kod, który wyświetla pewien komunikat i kończy działanie programu, a ów fragment jest używany wiele razy w tym programie, to należałoby go dublować wiele razy. Powoduje to nie tylko zwiększenie objętości kodu, ale również potęguje podatność na błędy.
Budowa procedury
Przykładowa deklaracja procedury wygląda następująco:
procedure Nazwa_Procedury;
begin
{ kod procedury }
end;
Jak widać, procedurę deklaruje się z użyciem słowa kluczowego Procedure, po którym następuje jej nazwa. Nazwa procedury musi być unikalna dla każdego modułu/programu, nie może się powtarzać (jest to drobne uproszczenie, ponieważ tak w rzeczywistości możemy mieć kilka procedur o tych samych nazwach, lecz różnych parametrach (mowa wtedy o tzw.przeładowywaniu procedur)).
Od teraz za każdym razem, gdy gdzieś w kodzie wpiszemy słowo Nazwa_Procedury, zostanie wykonany kod umieszczony w jej wnętrzu. Trzeba zapamiętać, że procedury deklaruje się poza blokiem begin. end!
Przykład procedury
Wyobraźmy sobie, że w danym programie trzeba często wykonywać ten sam blok kodu, np.wyświetlanie kilku tekstów w konsoli. W takim przypadku za każdym razem należałoby wpisywać po kolei instrukcje Writeln.
Dzięki zastosowaniu procedur możesz wpisać te instrukcje tylko raz, później pozostaje już tylko umieszczenie w odpowiednim miejscu nazwy procedury, aby wykonać kod w niej zawarty.
program ProcApp;
{$APPTYPE CONSOLE}
procedure Quit;
begin
Writeln('Wyjście z programu! Naciśnij Enter, aby zakończyć!');
Readln;
end;
var Key : Char;
begin
Writeln('Naciśnij literę Q, aby zakończyć!');
Readln(Key);
if Key = 'Q' then Quit else begin Writeln('Fajnie, że jesteś z nami'); Readln; end;
end.
W powyższym programie zadeklarowałem procedurę Quit. Od tej pory za każdym razem, gdy w programie znajdzie się słowo Quit, nastąpi wyświetlenie informacji i po naciśnięciu klawisza Enter zakończenie działania procedury (w naszym przykładzie zakończenie działania aplikacji).
Procedura z parametrem
Wraz z wywołaniem danej procedury lub funkcji jest możliwe przekazanie do niej danych, tzw. parametrów. Deklaracja procedury lub funkcji zawierającej parametry wygląda następująco:
procedure SetName(Name : String);
begin
{ kod procedury }
end;
Od tej pory, chcąc wywołać procedurę SetName, należy podać wartość parametru. Parametr ten musi być typu String.
program ProcParam;
{$APPTYPE CONSOLE}
procedure SetName(Name : String);
begin
Writeln('Nazywam się ' + Name);
end;
begin
SetName('Adam Boduch');
Readln;
end.
Procedura SetName odczytuje wartość przekazanego do niej parametru i wyświetla go wraz z pozostałym tekstem na konsoli.
Kilka parametrów
Możliwe jest przekazanie do procedury lub funkcji kilku parametrów tego samego bądź innego typu. W takim przypadku wszystkie parametry należy oddzielić znakami średnika:
function SetName(FName : String; SName : String; Age : Byte) : String;
Jeżeli jednak występuje kilka parametrów tego samego typu, można oddzielić je przecinkami w ten sposób:
function SetName(FName, SName : String; Age : Byte) : String;
Dzięki temu zapis funkcji jest nieco krótszy. Naturalnie z punktu widzenia kompilatora nie ma znaczenia, w jaki sposób jest zapisana deklaracja, jest więc możliwe zapisanie tego również w taki sposób:
function SetName(FName, SName : String; Age : Byte ) : String;
begin
end;
Funkcja zwracająca wartość
Funkcje są w swoim działaniu bardzo podobne do procedur. Podstawowa różnica pomiędzy procedurami a funkcjami polega na tym, że te drugie zwracają jakąś wartość.
Deklaracja funkcji jest bardzo specyficzna, następuje bowiem poprzez użycie słowa kluczowego Function. Oprócz tego funkcja, jak już mówiłem, musi zwracać jakąś wartość, po znaku dwukropka należy wpisać typ zwracanej wartości:
function GetName : String;
begin
end;
Po słowie function następuje nazwa funkcji, w nawiasie podane są zmienne o typie danych określonym po dwukropku, które zostaną przekazane do funkcji podczas jej wywoływania, następnie (już po nawiasie) określony jest typ zwracanej przez funkcje wartości.
function Moja_Funkcja(X, Y : Integer) : Integer;
Słowo "function", mówi kompilatorowi, że ma do czynienia z funkcja. Następnie w nawisanie przedstawiane są dwie zmienne typu "Integer". Można się obyć bez tych zmiennych, lecz do tego przykładu potrzebne nam będą te właśnie zmienne. Na samym końcu typ wyniku, "Integer".
Jak zwrócić rezultat funkcji
program FuncApp;
{$APPTYPE CONSOLE}
function GetName : String;
begin
Result := 'Jasio Stasio';
end;
begin
Writeln('Nazywam się ' + GetName);
Readln;
end.
Po uruchomieniu takiego programu na konsoli zostanie wyświetlony tekst Nazywam się Jasio Stasio. To wszystko stanie się za sprawą wiersza:
Result := 'Jasio Stasio';
Słowo Result oznacza "ukrytą" zmienną; po przypisaniu jej wartości zostanie ona zwrócona przez funkcję.
W Turbo Pascalu nie istniała niejawna zmienna Result; zwracanie wartości przez funkcję następowało poprzez wykonanie takiego fragmentu: nazwa_funkcji := wartosc;
W miejsce Result należało wstawić nazwę funkcji, wówczas program się kompilował, a funkcja zwracała odpowiednią wartość. Jeśliby spróbować to samo zrobić z procedurami - po prostu się to nie uda, ponieważ procedura nie może zwrócić wartości.
Funkcja jako wyrażenie
Rezultat funkcji można wykorzystać bezpośrednio w wyrażeniu, przypisać do zmiennej albo przekazać jako parametr innej procedury.
function TForm1.MojaFunkcja(X, Y: Integer): Integer;
begin
// rezultat funkcji:
Result := X + Y;
end;
Tak przedstawia się nasza funkcja. Słowo "Result" śluzy do zwracania wyniku. Rezultatem działania takiej funkcji będzie dodanie do siebie dwóch zmiennych: X i Y (oczywiście typu integer).
Teraz napiszemy procedurę która by eksportowała do funkcji liczby, a następnie importowała by wynik działania funkcji.
Label1.caption := IntToStr(Moja_Funkcja(2,2));
Funkcja IntToStr konwertuje typ Integer zmiennej na typ String, który można już wyświetlić w etykiecie.
Konwersja typu wyniku
- IntToStr - konwertuje zmienna "Integer" na "String"
- StrToInt - "String" na "Integer"
- StrPCopy - "String" na "PChar"
- StrPas - "PChar" na "String"
- FloatToStr - typ zmiennoprzecinkowy (np. 0.3) na "String"
- StrToCurr - "String" na "Currency" (typ zmiennoprzecinkowy)
- TimeToStr - zmienna "Time" na "String"
- DateToStr - zmienna "Date" na "String"
- StrToDate - "String" na "Date"
- StrToTime - "String" na "Time"
- CurrToStr - typ zmiennoprzecinkowy Currency (np. 0.5) na "String"
Przekazywanie wyniku przez parametr var
Jeżeli potrzebny jest procedurze sposób przekazania danych na zewnątrz, można użyć parametru przekazywanego przez referencję. Przekazywanie parametrów przez referencję polega na umieszczeniu przed parametrami słowa kluczowego Var.
Dzięki temu kod znajdujący się wewnątrz procedury może zmienić wartość parametru. Sposób przekazywania danych przez referencję jest optymalny, gdyż nie jest tworzona kopia zmiennej. W związku z tym jest możliwe przekazywanie danych z procedury na zewnątrz.
procedure SetValue(var Message : String);
begin
{ próba nadania nowej wartości dla parametru }
Message := 'Hello there!';
end;
var S : String;
begin
S := 'Hello World';
SetValue(S);
Writeln(S);
end.
Dzięki dodaniu słowa Var i ponownemu uruchomieniu programu można przekonać się, że zmienna S będzie miała wartość Hello there. Oznacza to, że naszej procedurze udało się zmienić wartość tego parametru.
Parametr przez wartość i przez stałą
Przekazywanie parametrów przez wartość jest sposobem najprostszym z możliwych. Deklaracja procedury nie musi zawierać żadnych dodatkowych słów kluczowych - wystarczy poniższa konstrukcja nagłówka procedury bądź funkcji:
procedure Foo(S : String);
Przekazanie parametru przez wartość wiąże się utworzeniem jego kopii lokalnej do wykorzystania jedynie przez procedurę lub funkcję. Oryginalna wartość zmiennej przekazanej do procedury nie zostaje w żaden sposób naruszona.
Umieszczenie w deklaracji parametrów słowa kluczowego const spowoduje przekazywanie parametrów jako stałych.
procedure Show(const Message : String);
begin
Writeln(Message);
end;
Procedura nie może w żaden sposób wpływać na zawartość parametru. Próba nadania przez procedurę jakiejś wartości spowoduje komunikat o błędzie: Left side cannot be assigned to.
Przekazując parametry przez stałą, pozwalamy kompilatorowi na maksymalną optymalizację kodu.
Parametry domyślne
Delphi oferuje przydatną możliwość deklarowania parametrów domyślnych. Oznacza to, że podczas wywoływania procedury lub funkcji taki parametr może ale nie musi zostać wpisany; w takim przypadku Delphi zastosuje wartość domyślną, ustaloną przez programistę.
procedure SetName(FName : String; SName : String = 'Nieznany'; Age : Byte = 0 );
Parametry domyślne wpisuje się po znaku równości. W przypadku powyższej procedury możliwe jest albo wywołanie jej w ten sposób:
SetName('Moje imię');
albo wpisanie wszystkich parametrów:
SetName('Moje imię', 'Moje nazwisko', 100);
Obydwa sposoby są prawidłowe. Jeżeli nie wpiszemy dwóch ostatnich parametrów, Delphi za domyślne wartości uzna te, które zostały wpisane w deklaracji procedury.
Niestety, nie jest możliwe deklarowanie kilku parametrów tego samego typu i jednoczesne przypisanie wartości jednemu z nich:
procedure SetName(FName, SName : String = 'Wartość'; Age : Byte = 0 );
Powyższa instrukcja spowoduje błąd; zawsze podczas określania wartości domyślnych należy wpisać typ parametru.
Należy jeszcze poczynić jedno istotne zastrzeżenie dotyczące deklaracji wartości domyślnych. Otóż błędna jest deklaracja wyglądająca tak:
function Funkcja(X : Integer = 0; Y : Integer) : Integer;
Jedynym wyjściem jest umieszczenie domyślnych parametrów na samym końcu lub deklaracja wartości domyślnych dla każdego parametru. Taki kod będzie już jak najbardziej prawidłowy:
function Funkcja(Y : Integer; X : Integer = 0) : Integer;
Tablica jako parametr
Istnieje możliwość deklaracji parametru procedury jako tablicy dynamicznej (czyli takiej bez określonego z góry rozmiaru), wtedy jest mowa o tzw.open array:
procedure ArrayProc(A : array of String);
begin
Writeln(Length(A));
end;
W takim przypadku procedura wyświetli w konsoli ilość elementów w tablicy.
var A : array[0..1] of String;
begin
A[0] := 'Delphi';
A[1] := '2005';
ArrayProc(A);
Readln;
end;
Nie można w standardowy sposób zadeklarować funkcji, która zwracałaby tablicę jako rezultat operacji:
function ArrayFunc : array of String;
begin
end;
Można jednak zadeklarować nowy typ. Poniższy kod zostanie skompilowany bez żadnych problemów:
type TBar = array of String;
function Foo : TBar;
begin
end;
Łańcuchy jako parametr
W parametrach procedur i funkcji nie można przekazywać łańcuchów z ograniczoną długością - np.:
procedure Foo(S : String[20]);
begin
end;
Jeżeli chcemy, aby max. długość łancucha S ograniczała się do 20 znaków, należy zadeklarować nowy typ:
type MyString = String[20];
procedure Foo(S : MyString);
begin
end;
Procedury w module Delphi
Kod w języku Delphi zawarty jest w procedurach, zwykle każde zdarzenie ma swoją procedurę np. Jeśli najedziesz na przycisk kursorem myszy wywołana zostanie procedura OnMouseEnter jednak jeśli kursor opuści pole przycisku wywołana zostanie procedura OnMouseLeave oznaczająca, że kursor myszy znalazł się poza obszarem przycisku (tak naprawdę są to procedury obsługujące tzw. komunikaty systemowe - można o nich poczytać w artykule "Delphi - Komunikaty").
Procedury takie umieszczane są po słowieimplementation. Po słowie "private" i "public" istnieje możliwość wpisywania własnych procedur, zależnie gdzie je wpiszemy będą one dostępne w innych modułach.
Ogólna postać modułu formularza z procedurą zdarzeniową kliknięcia przycisku:
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs;
type TForm1=class(TForm) Button1: TButton; //deklaracja procedury procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end;
var Form1: TForm1;
implementation {$R *.dfm}
//wywołanie procedury poprzez zdarzenie kliknięcia myszą na przycisku procedure TForm1.Button1Click(Sender: TObject); begin //okno z komunikatem ShowMessage('Okienko z komunikatem !'); end; end.
Dyrektywy przy deklarowaniu procedur i funkcji
W deklaracjach procedur i funkcji mogą występować dyrektywy określające sposób ich implementacji, wywoływania albo powiązania z kodem zewnętrznym.
- assembler - procedura assemblerowa, np. procedure dodaj; assembler; asm Instrukcje; end;
- export - brak opisu
- external - definiuje procedury i funkcje zewnętrzne (np. z DLLa), np. function Zsumuj(X,Y:Integer):Integer far; external 'TestDll' name 'DODAJ';
- far - zastepuje dyrektywy kompilatora {$F+}. {$F-}
- interrupt - występuje w deklaracji procedury sterującej przerwaniami
- near - oznacza tzw. bliskie wywołanie
- forward - funkcja lub procedura zapowiadająca. Domyślna dla deklaracji metod obiektu
- virtual - procedura (funkcja) dynamiczna
- overload - przeciążanie funkcji lub procedury
- dynamic - sposób deklarowania metody dynamicznej
- static - sposób deklarowania metody statycznej
- stdcall - konwencja wywołania funkcji
W Delphi zachowane tylko w celu wstecznej kompatybilności, procedury/funkcje całkowicie assemblerowe można deklarować równie dobrze bez użycia słowa kluczowego.
Tak samo niekonieczne jest używanie far, near, interrupt.
Dyrektywa forward
Dyrektywa forward służy do zapowiedzenia funkcji lub procedury, której pełna implementacja znajduje się później.
forward - funkcja lub procedura zapowiadająca. Domyślna dla deklaracji metod obiektu.
Przeciążanie procedur i funkcji
Przeciążanie pozwala używać tej samej nazwy dla kilku procedur lub funkcji, jeżeli różnią się listą parametrów.
Overloading w artykułach kiedyś opisałem: "Przeciążanie funkcji" a jeszcze dostępne są: dynamic static stdcall w helpie opisane co powodują.
Przykład zastosowania overload. Mamy jakąś klasę bazową TKlasaBazowa, która posiada np. funkcję "Save(FileName: String)". Załóżmy, że chcialibyśmy teraz napisać klasę pochodną od TKlasaBazowa o nazwie TKlasaPochodna. Chcemy żeby klasa TKlasaPochodna też posiadała funkcję "Save(FileName: String)". W takim przypadku w klasie TKlasaPochodna deklarujemy tą funkcję i obok niej wpisujemy overload;
To opisałeś klauzulę override, a nie overload - odsyłam do lektury helpa ( a poza tym u Ciebie jest to statyczna metoda klasy więc i tak nie będzie działać).
Procedura, która „zwraca” kilka wartości
W przeciwieństwie do funkcji procedura nie ma pojedynczego rezultatu przypisywanego przez Result. Może jednak zmieniać parametry przekazane przez var, dlatego jedna procedura może przekazać kilka wartości do kodu wywołującego.
procedure PobierzWyniki(var Suma: Integer; var Tekst: String);
begin
Suma := 4;
Tekst := 'Gotowe';
end;
Taki sposób przekazywania danych jest innym mechanizmem niż zwracanie wartości przez funkcję. W praktyce funkcja jest właściwym wyborem, gdy istnieje jeden główny rezultat, a procedura z parametrami var może być użyteczna, gdy trzeba zmienić kilka zmiennych albo zwrócić dodatkowe informacje.
Procedura a generator kodu w postaci stringa
W miarę elastyczny (na ile się da) generator procedur. Wybierał bym szablon, co procedury mają na wejściu i co ma wyjść, podstawowe typy wcześniej okodowane.
Interesują mnie możliwe sposoby generowania kodów procedur do stringa; właściwie, sposoby rozkładu takiego kodu na części pierwsze bo dzięki temu będę mógł kod złożyć w druga stronę.
Może jaszcze sugerowane sposoby przechowywania takiego kodu albo struktury generującej, ale tylko odnośnie pojedynczej procedury.
Może zacznij od obejrzenia: TJvInterpreterProgram z biblioteki Jedy.
TJvInterpreterProgram - obejrzyj sobie kod, to pełny interpretator obiektowego Pascala.
Najważniejsze rozróżnienie
Jeżeli chcesz tylko wykonać wydzielony fragment kodu, użyj procedury:
procedure NazwaProcedury(parametr1: Integer);
Jeżeli chcesz otrzymać wartość, użyj funkcji:
function NazwaFunkcji(parametr1: Integer): TypZwracany;
Typ wartości wpisuje się na końcu po dwukropku, a rezultat funkcji przypisuje się do zmiennej Result. Procedura nie może zwrócić wartości za pomocą Result, ale może przekazywać dane na zewnątrz przez parametry oznaczone słowem var.

tags: #procedura #zwracajaca #wartosc #delphi