Programowanie modularne, funkcje i procedury, sposoby definiowania funkcji i procedur oraz ich użycia, parametry procedur i funkcji. Przykłady wykorzystania procedur i funkcji w programach
Procedurą lub funkcją (modułem, podprogramem) nazywamy wyodrębnioną część programu, stanowiącą pewną całość, posiadającą jednoznaczną nazwę i ustalony sposób wymiany informacji z pozostałymi częściami programu. Posiadają jedno wejście i wyjście. Użycie podprogramu nie wymaga znajomości jego wewnętrznej budowy.
Zalety wykorzystania modułów:
pozwalają uzyskać czytelną strukturę programu,
mogą być wykonywane wielokrotnie lub użyte w innych programach
umożliwiają modularyzację programu, niezależne tworzenie i testowanie modułów
bardziej efektywne wykorzystanie pamięci. (definicja każdego podprogramu występuje tylko raz, operują na zmiennych lokalnych)
Różnica pomiędzy procedurą a funkcją polega na sposobie wywołania i przekazywania wartości.
Możliwe jest łączenie procedur i funkcji w moduły biblioteczne i dołączanie do różnych programów za pomocą deklaracji uses.
Definicja funkcji
function nazwa_funkcji(lista parametrów formalnych): typ_wyniku;
część opisowa
begin
ciąg instrukcji
end;
W ciągu instrukcji musi wystąpić co najmniej jedna instrukcja przypisania w postaci: nazwa_funkcji := wyrażenie;
Lista parametrów formalnych zawiera deklaracje parametrów formalnych, określa sposób w jaki poszczególne parametry formalne zostaną zastąpione parametrami aktualnymi podczas wywołania procedury lub funkcji.
Wywołanie funkcji następuje za pomocą podania nazwy funkcji jako argumentu dowolnego wyrażenia. Liczba argumentów i ich typ muszą być zgodne z argumentami formalnymi funkcji.
Przykład:
program tangens;
function tg(x:real):real;
begin
tg := sin(x)/cos(x);
end;
var x, f : real;
begin
write(`Podaj wartość kąta');
readln(x);
x:=x*2*PI/360; {zamiana stopni na radiany}
f:=tg(x);
writeln(`Wartość funkcji tangens dla podanego kąta wynosi `, f:8:3);
end.
Przykład (funkcja określająca czy znak jest literą):
function litera(znak:char):boolean;
begin
if ((znak>='A') and (znak<='Z')) or ((znak>='a') and (znak<='z'))
then litera:=TRUE
else litera:=FALSE;
end;
Przykład (funkcja zwracająca wartość mniejszą z dwóch):
function min(a, b : real) : real;
begin
if a<= b then min:= a
else min:=b;
end;
Definicja procedury
procedure nazwa_procedury(lista parametrów formalnych);
część opisowa
begin
ciąg instrukcji
end;
Do wywołania procedury służy instrukcja procedury typu
nazwa_procedury;
lub nazwa_procedury(lista_parametrów_aktualnych);
Przykład
Procedura wypisująca w wierszu zadaną parametrem liczbę gwiazdek:
procedure gwiazdki ( ile:byte);
var i : byte;
begin
for i:=1 to ile do
write ( `*' );
writeln;
end;
i jej wykorzystanie do wyświetlenia trójkąta złożonego z 20 wierszy:
for i:= 1 to 20 do
gwiazdki ( i );
Komunikacja pomiędzy programem wywołującym a wywoływanym podprogramem może odbywać się przez zmienne globalne lub parametry.
Wszystkie zmienne używane w programie powinny zostać zadeklarowane. Zakresem zmiennej jest blok w którym zmienna została zadeklarowana (od miejsca zadeklarowania). Jeśli zmienna została zadeklarowana w programie głównym jest wówczas zmienną globalną dostępną zarówno w programie głównym jak i w podprogramach zdefiniowanych w tym programie. Zmienne globalne tworzone są w segmencie danych i istnieją do zakończenia programu. Jeśli jednak w podprogramie zadeklarowana zostanie zmienna o tej samej nazwie co zmienna globalna to następuje przesłanianie pierwotnej deklaracji zmiennej. Zmienne zadeklarowane w procedurze lub funkcji (również parametry) są zmiennymi lokalnymi. Tworzone są na stosie i istnieją tak długo jak długo trwa wykonanie podprogramu. Następnie obszar ten jest zwalniany i może być przydzielany na zmienne lokalne kolejnego wywołanego podprogramu.
Parametry do procedury lub funkcji mogą być przekazywane przez wartość lub zmienną:
lista_parametrów : typ; {parametr aktualny musi być wyrażeniem}
var lista_parametrów : typ; { parametr aktualny musi być zmienną}
Przykład:
Procedura porządkowania dwóch wartości korzystająca ze zmiennych globalnych nie wymaga deklaracji parametrów.
program Zamiana;
var X, Y : integer;
procedure Ustaw;
var T ; integer;
begin
if X>Y then
begin
T:=X; X:=Y; Y:=T;
end;
end;
begin
write(`Podaj dwie liczby całkowite');
readln(X, Y);
Ustaw; { nie wymaga podawania parametrów}
writeln(`Uporządkowane liczby `, X:5, Y:5);
end.
Metoda komunikowania się programu głównego z podprogramem za pomocą zmiennych globalnych nie jest zalecana ze względu na:
brak elastyczności wykorzystania podprogramów (jak ustawić zmienne A i B?)
trudności z użyciem podprogramów w innym programie
brak hermetyzacji danych (efekty uboczne, trudności w lokalizacji usterek itp.)
Przykład:
Procedura porządkowania dwóch wartości korzystająca z parametrów przekazywanych przez wartość.
program Zamiana;
var X, Y : integer;
procedure Ustaw(A, B : integer);
var T ; integer;
begin
if X>Y then
begin
T:=A;
A:=B;
B:=T;
end;
end;
begin
write(`Podaj dwie liczby całkowite');
readln(X, Y);
Ustaw(X, Y);
writeln(`Uporządkowane liczby `, X:5, Y:5);
end.
Przekazywanie parametrów przez wartość sprawia, że wartości zmiennych X i Y kopiowane są na stos do zmiennych lokalnych A i B. Ustawienie wartości odbywa się na stosie i jest tracone po zakończeniu procedury. W rezultacie procedura nie wykonuje swojego zadania. Przekazywanie parametrów tą metodą ma zastosowanie w przypadku gdy komunikacja jest w jedną stronę: od programu głównego do podprogramu. W przypadku gdy wyniki obliczeń lub np. wczytane w podprogramie dane muszą być przekazane do programu wywołującego należy zastosować inny sposób przekazywania parametrów: przez zmienną (adres).
Przykład:
Procedura porządkowania dwóch wartości korzystająca z parametrów przekazywanych przez zmienną (adres).
program Zamiana;
var X, Y : integer;
procedure Ustaw(var A, B : integer);
var T ; integer;
begin
if X>Y then
begin
T:=A; A:=B; B:=T;
end;
end;
begin
write(`Podaj dwie liczby całkowite');
readln(X, Y);
Ustaw(X, Y);
writeln(`Uporządkowane liczby `, X:5, Y:5);
end.
Przykład:
Rozwiązywanie równania kwadratowego.
procedure kwadratowe(a, b, c :real; var x1, x2 :real);
var delta : real; {zmienna lokalna}
Deklaracja funkcji lub procedury jest potrzebna wtedy gdy odwołanie do nich występuje poza zasięgiem definicji np. w definicji jednej procedury występuje wywołanie innej, której definicja występuje poza pierwszą procedurą. Wówczas przed definicją pierwszej procedury należy zadeklarować drugą w sposób następujący:
procedure identyfikator_procedury; forward;
Przykład
Przekazywanie funkcji jako parametru funkcji:
type funkcja=function(x:real):real; {definicja typu funkcja}
function calka(a, b :real;
f : funkcja) : real;
begin
calka := (b-a)*(f(a)+f(b))/2; {całkowanie metodą trapezów}
end;
program funkcja_wykladnicza;
var x: Real;
function E(x:Real): Real;
const eps=1e-8; { dokładność obliczeń }
var w, sum: Real;
i:Integer;
begin
i:=0; { numer wyrazu szeregu }
sum:=1; { suma szeregu - na razie podstawiamy pierwszy wyraz }
w:=1; { pierwszy wyraz szeregu - następne określimy w pętli }
while abs(w)>eps do { dopóki dodawane wyrazy szeregu są dostatecznie duże }
begin
i:=i+1;
w:=w*x/i; { kolejny wyraz szeregu }
sum:=sum+w; { dodajemy go do poprzednio zsumowanych wyrazów }
end;
E:=sum
end; { funkcji wykładniczej }
(*********** program główny ************)
begin
writeln('Dla jakiej liczby obliczyć wartość funkcji?');
readln(x);
writeln('EXP(x)= ',E(x));
writeln(' ',Exp(x)); { dla porównania podajemy wartość
funkcji z biblioteki Turbo Pascala}
readln;
end.
Rekurencja
Wiele zagadnień w technice i matematyce można zdefiniować rekurencyjnie. Definicja rekurencyjna oznacza takie zdefiniowanie problemu gdzie odwołujemy się do definiowanej właśnie definicji. Przykładem takiej definicji może być potęga całkowita nieujemna N liczby rzeczywistej x:
x n = jeśli n > 0 to x n-1 * x
jeśli n = 0 to 1
Rekurencja w językach programowania realizowana jest przy pomocy podprogramów wywołujących same siebie. Podprogramy rekurencyjne aby poprawnie działały muszą spełniać następujący warunek: podprogram musi zawierać warunek, który powoduje zakończenie rekurencji, czyli nie wywołuje po raz kolejny samego siebie.
Rekurencja daje proste i eleganckie programy - niestety ma także poważną wadę: każde wywołanie podprogramu wymaga wykonania przez procesor pewnych czynności administracyjnych co spowalnia działanie programu, oraz powoduje odłożenie na stos systemowy dużej ilości danych.
Przykład - wyznaczanie potęgi N z liczby X:
program potega1;
{ wyznaczanie potęgi całkowitej nieujemnej N z liczby x
metoda rekurencyjna }
function PotegaN( x:real; N:integer ):real;
{ funkcja wyznacza potęgę N z liczby X }
begin
if N=0 then
PotegaN := 1
else
potegaN := PotegaN(x,N-1)*x;
end;
begin
Writeln('2^10=',PotegaN(2,10):0:0);
end.
Przykład - wyznaczanie silni z liczby x:
program Silnia;
{ Wyznaczanie silni liczby X metoda rekurencyjna }
function SilniaX(x:longint):longint;
begin
if x = 1 then
SilniaX := 1
else
SilniaX := SilniaX(x-1)*x;
end;
begin
Writeln('Silnia z liczby 6= ',SilniaX(6));
end.
6