Podstawy Programowania
Wykład jedenasty:
Moduły, programowanie modularne
1.Moduły i programowanie modularne
Język Pascal jest językiem strukturalnym. Oznacza to, że programy napisane w
tym języku mogą zostać podzielone na odrębne jednostki kodu zwane
funkcjami lub procedurami. Dzięki nim struktura programu staje się
przejrzysta i łatwiejsza do zrozumienia. Programowanie modularne jest
rozwinięciem tego pomysłu. Pozwala ono na zgrupowanie logicznie powiązanych
podprogramów wraz z innymi elementami, takimi jak zmienne, typy danych
i stałe, w osobnych plikach z rozszerzeniem pas , zwanych modułami1. Pliki
zawierające kod zródłowy programu i modułów nazywamy jednostkami
translacji. Przed użyciem w programie moduł musi zostać skompilowany.
W wyniku tego procesu powstaje pliku o rozszerzeniu tpu 2. Moduły są nie
tylko zbiorem gotowych elementów programu. Pozwalają również określić, które
z tych elementów zostaną udostępnione osobie korzystającej z nich, a które
pozostaną ich wewnętrzną częścią. Elementy modułu dostępne na zewnątrz
stanowią jego interfejs3, czyli pośredniczą między modułem a programem, który
z niego korzysta. Elementy dostępne jedynie wewnątrz moduł stanowią jego
implementację, czyli mechanizm działania. Na moduł można spojrzeć z punktu
widzenia dwóch osób: programisty użytkującego moduł, zwanego dalej
użytkownikiem modułu i programisty tworzącego moduł zwanym dalej twórcą
modułu4. Pierwszy programista, aby skorzystać z modułu musi jedynie wiedzieć
jak wywołać funkcje i procedury zgromadzone w module, lub jakie są nazwy
stałych, typów i zmiennych, z tego modułu. Drugi z nich, może chcieć
zachować możliwość modyfikowania sposobu działania umieszczonych
w module podprogramów, ale również uniknąć sytuacji kiedy po wprowadzeniu
zmian w module użytkownik musi dokonać modyfikacji w swoim programie.
Rozwiązaniem tego problemu jest zawarcie niepisanej umowy między twórcą
i użytkownikiem modułu. Twórca modułu umieszcza w jego interfejsie, czyli
w części dostępnej publicznie te elementy, które nie ulegną zmianie
w przyszłych jego wersjach, natomiast w części prywatnej, te które mogą ulec
zmianie5. Najczęstszą przyczyną wprowadzania zmian do kodu modułów jest
odkrycie błędów w ich poprzedniej wersji lub chęć zoptymalizowania (np.:
przyspieszenia) działania tego kodu.
1 W innych językach programowania moduły są określane mianem bibliotek.
2 W przypadku Turbo Pascala. Free Pascal stosuje rozszerzenie ppu .
3 Słowo interfejs jest wieloznaczne i w zależności od kontekstu może oznaczać różne rzeczy. Wywodzi się ono od
angielskiego słowo interface , które nie da się wprost przetłumaczyć na język polski.
4 W niektórych przypadkach jest to jedna i ta sama osoba.
5 Dobrą ilustracją takiego postępowania jest przykład producenta samochodu i kierowcy, który jest jego użytkownikiem.
Interfejsem samochodu jest jego układ kierowniczy, mechanizm działania obejmuje cały system napędowy. Jeśli
kierowca, który jezdził samochodem określonej marki zdecyduje się na zakup nowego modelu o, powiedzmy, większym
silniku, to może być pewien, że tym samochodem będzie można kierować w ten sam sposób co poprzednim, bo
producent zachowa poprzedni rozkład elementów układu kierowniczego.
2
2.Struktura modułu w języku Pascal
Jak już wyjaśniono we wstępie kod zródłowy modułu musi być umieszczony
w osobnym pliku z rozszerzeniem pas (w osobnej jednostce kompilacji6).
Struktura kodu zródłowego modułu jest następująca:
unit nazwa_modułu;
interface
instrukcje włączające inne moduły;
definicje stałych;
definicje typów;
deklaracje zmiennych;
deklaracje procedur i funkcji;
implementation
deklaracje etykiet;
definicje stałych;
deklaracje zmiennych;
definicje typów;
definicje procedur i funkcji;
begin
część inicjalizująca;
end.
Kod zródłowy modułu zaczyna się obowiązkowym słowem kluczowym unit, po
którym występuje nazwa modułu. Jest ona taka sama7 jak nazwa pliku,
w którym jest umieszczony kod modułu, ale nie zawiera kropki i rozszerzenia.
Część modułu występująca po słowie kluczowym interface jest częścią publicz-
ną, tzn. wszystkie zawarte w niej elementy będą dostępne w programach lub in-
nych modułach, w których dany moduł zostanie użyty. Ta część może być pu-
sta. W takim wypadku po słowie kluczowym interface umieszczamy słowo
kluczowe implementation. Jeśli do modułu włączamy inne moduły, to instruk-
cja uses je włączająca musi znajdować się zaraz za słowem kluczowym
interface. Pozostałe sekcje części publicznej mogą występować w dowolnej kolej-
ności, a nawet się powtarzać. Deklaracji podprogramów dokonuje się w module
trochę inaczej niż w programie. Do zadeklarowania podprogramu wystarczy
6 Jest to bardziej szczegółowe określenie niż jednostka translacji .
7 W przypadku Turbo Pascala może być dłuższa, ale pierwsze osiem znaków musi być takie samo.
3
podanie jego nagłówka, bez słowa kluczowego forward. Tak zadeklarowanie
procedury i funkcje muszą zostać zdefiniowane w części prywatnej modułu
rozpoczynającej się słowem kluczowym implementation. W definicji procedury
można pominąć listę parametrów, a w definicji funkcji również typ wartości
zwracanej, ale jeśli je powtarzamy, to muszą one być takie same, jak
w deklaracji. Ta możliwość nie jest dostępna w środowisku Free Pascal.
Wszystkie elementy części prywatnej modułu, poza definicjami podprogramów,
które zostały zadeklarowane w części publicznej, są opcjonalne, tzn. nie muszą
występować w module. Oprócz zadeklarowanych w części publicznej
podprogramów, można w części prywatnej deklarować inne podprogramy
i elementy. Nie będą one dostępne dla użytkownika modułu. Gdybyśmy np.
umieścili w module podprogramy liczące pierwiastki równania kwadratowego,
to podprogram liczący deltę mógłby występować jedynie w części prywatnej
modułu. Nie musi on być wywoływany poza modułem, wystarczy aby
wywoływały go procedury, czy też funkcje liczące pierwiastki. Po części
implementacyjnej modułu występuje część inicjalizująca8, która jest
opcjonalna. Jeśli chcemy np. zainicjalizować jakąś zmienną z modułu, to może-
my tego dokonać właśnie w tej części umieszczając np. odpowiednią instrukcję
przypisania między słowami kluczowymi begin i end. Po słowie end występuje
kropka. Część inicjalizująca modułu jest wykonywana na początku działania
programu, w którym moduł został użyty. Jeżeli do programu było włączone
więcej modułów, to są one inicjalizowane według kolejności w jakiej ich nazwy
zostały wymienione po słowie kluczowym uses. Jeśli nie chcemy, aby moduł
miał część inicjalizującą, to wystarczy, że po części implementacyjnej wystąpi
słowo kluczowe end zakończone kropką.
3.Użycie modułu
Moduł może być użyty w innym module lub w programie. Aby móc z niego
korzystać należy go najpierw skompilować. Kod wynikowy9 modułu należy
umieścić w tym samym katalogu co kod zródłowy programu, w którym moduł
ma być użyty lub w specjalnym katalogu, który jest wymieniony w opcjach
kompilatora. Pisząc własne moduły będziemy stosować pierwsze rozwiązanie,
czyli kod wynikowy modułu, a nawet kod zródłowy będziemy umieszczać w tym
samym katalogu, co kod zródłowy programu. Moduł włączamy do programu
lub innego modułu za pomocą instrukcji uses. Jeśli chcemy włączyć większą
liczbę modułów do programu, to ich nazwy rozdzielamy przecinkiem. Jeśli nie
umieścimy w ogóle instrukcji uses w programie, to kompilator i tak niejawnie
8 Dosyć słynny jest przypadek części inicjalizującej modułu crt. W tej części znajdował się kod ustalający szybkość
komputera, na którym program został uruchomiony. W nowszych komputerach generował on błąd dzielenia przez
zero.
9 Czyli zawartość pliku z rozszerzeniem tpu lub ppu .
4
włączy do naszego programu moduł o nazwie System. Zawiera on między
innymi takie procedury jak: insert, delete, assign. Jeśli w programie lub innym
module zdefiniujemy procedurę lub inny element o takiej samej nazwie, jak
element zawarty w module którego używamy, to nastąpi tak zwane przykrycie
elementu z modułu użytego i poprzez tę nazwę dostępny będzie jedynie element
z programu lub tworzonego modułu. Nie oznacza, to że nie możemy skorzystać
z elementu zawartego w użytym module. Możemy to zrobić podając jego nazwę
kwalifikowaną, którą tworzymy według wzorca:
nazwa_modułu.nazwa_elementu
Nazwami kwalifikowanymi posługujemy się również wtedy, gdy do programu
włączamy dwa lub większą liczbę modułów, które zawierają elementy o tej
samej nazwie i próbujemy się do nich odwołać. W takim przypadku dostępny
jest jedynie element z ostatniego włączonego modułu, a w przypadku
elementów z pozostałych modułów trzeba podać ich nazwę kwalifikowaną.
4.Moduły zależne
Istnieje możliwość, choć jest dosyć rzadko używana, napisania modułów
zależnych. Są to moduły, które wzajemnie wykorzystują elementy w nich
zawarte. Jeśli ich liczba ogranicza się do dwóch, to w części implementacyjnej
pierwszego następuje włączenie drugiego, a w części implementacyjnej
drugiego, włączenie pierwszego.
5.Przykłady użycia
Zdefiniujmy własny moduł, który będzie zawierał procedury pozwalające
wyświetlić na ekranie wartości zawarte w zmiennych typu byte, word, shortint
i integer w postaci binarnej i użyjmy tego modułu w programie ilustrującym
działanie operatorów logicznych. Oto kod modułu zawierający odpowiednie
procedury:
unit d2b;
interface
procedure print_integer(x:integer);
procedure print_word(x:word);
procedure print_byte(x:byte);
procedure print_short(x:shortint);
5
implementation
const
iw = 32768;
bs = 128;
procedure print_integer;
var
i:byte;
begin
for i:=0 to 8*sizeof(x)-1 do
if x and (iw shr i) = 0 then write(0) else write(1);
writeln;
end;
procedure print_word;
var
i:byte;
begin
for i:=0 to 8*sizeof(x)-1 do
if x and (iw shr i) = 0 then write(0) else write(1);
writeln;
end;
procedure print_byte;
var
i:byte;
begin
for i:=0 to 8*sizeof(x)-1 do
if x and (bs shr i) = 0 then write(0) else write(1);
writeln;
end;
procedure print_short;
var
i:byte;
6
begin
for i:=0 to 8*sizeof(x)-1 do
if x and (bs shr i) = 0 then write(0) else write(1);
writeln;
end;
end.
Procedury print_byte, print_shortint, print_word, print_integer realizują algorytm,
który został opisany na jednym z pierwszych wykładów. Proszę zwrócić uwagę,
że te procedury zostały zadeklarowane w części publicznej modułu, ale
zdefiniowane w części prywatnej. Nagłówki procedur w definicjach nie zawierają
listy parametrów formalnych, gdyż nie jest wymagane jej powtarzanie, ale
możemy posługiwać się tymi parametrami. Stałe, które są wykorzystywane
wewnątrz procedur, ale nie są dostępne na zewnątrz modułu zostały również
zdefiniowane w części prywatnej modułu.
Oto program, który korzysta z tego modułu:
program przyklad_dla_modulu;
uses
crt,d2b;
var
a,b:byte;
c,d:shortint;
e,f:word;
g,h:integer;
begin
clrscr;
a:=3;
b:=5;
write('a = ');
print_byte(a);
write('b = ');
7
print_byte(b);
write('a and b ');
print_byte(a and b);
c:=-4;
d:=-7;
write('c = ');
print_short(c);
write('d = ');
print_short(d);
write('c or d ');
print_short(c or d);
e:=16000;
f:=3000;
write('e = ');
print_word(e);
write('f = ');
print_word(f);
write('e xor f ');
print_word(e xor f);
g:=-32;
h:=-100;
write('g = ');
print_integer(g);
write('h = ');
print_integer(h);
write('g xor h ');
print_integer(g xor h);
readln;
end.
Trochę bardziej złożony przykład będzie dotyczy sortowania tablic. W module
o nazwie msort zostanie zdefiniowany typ tablicowy oraz procedury wypelnij
i wypisz. Z tego modułu będą korzystały moduły ssort, isort i bsort oraz
program główny. W trzech wymienionych modułach będzie umieszczony kod
związany z sortowaniem przez wybór, sortowaniem przez wstawianie
i sortowaniem bąbelkowym. Z tych modułów będzie korzystał program główny.
8
Oto kod zródłowy modułu msort:
unit msort;
interface
type
tablica = array [1..15] of byte;
procedure wypelnij(var t:tablica);
procedure wypisz(const t:tablica);
implementation
procedure wypelnij;
var
i:byte;
begin
randomize;
for i:=low(t) to high(tablica) do t[i]:=random(256);
end;
procedure wypisz;
var
i:byte;
begin
for i:=low(t) to high(t) do write(t[i]:4);
writeln;
end;
end.
Tak jak napisano poprzednio, zawiera on tylko definicję typu tablicowego
(tablica o 15 elementach typu byte) oraz dwie procedury pierwsza służąca do
wypełniania tablic wartościami losowymi, druga do wypisywania na ekran
zawartości tej tablicy. Z tego moduł będą korzystać następne moduły, ze
względu na definicję typu tablicy, którą będą sie one posługiwały. Oto kod
modułu zawierającego procedurę sortującą przez wybór:
9
unit ssort;
interface
uses crt,msort;
procedure sortuj(var t:tablica);
implementation
procedure swap(var x,y:byte);
var
tmp:byte;
begin
tmp:=x;
x:=y;
y:=tmp;
end;
procedure sortuj(var t:tablica);
var
i,j,k,tmp,min:byte;
begin
for i:=low(t) to high(t)-1 do
begin
k:=i;
for j:=i+1 to high(t) do
if t[k] > t[j] then k:=j;
if i<>k then swap(t[k],t[i]);
end;
end;
begin
clrscr;
writeln('Sortowanie przez wybór.');
end.
10
Pozostałe moduły będą skonstruowane w podobny sposób. Moduł ten zawiera
jedną procedurę, która będzie udostępniona publicznie, jest nią procedura
sortuj. Procedura swap, służy do zamiany miejscami wartości elementów i jest
procedurą pomocniczą dla sortuj. Z tego względu nie powinna być
udostępniana na zewnątrz i została umieszczona w części prywatnej modułu.
Moduł korzysta ze opisanego modułu msort, oraz z modułu crt. Posiada także
część inicjalizującą, która czyści ekran i wypisuje jaki rodzaj sortowania będzie
wykonywany. Oto kod modułu isort związanego z sortowaniem przez
wstawianie:
unit isort;
interface
uses crt,msort;
procedure sortuj(var t:tablica);
implementation
procedure sortuj;
var
i,j,key:byte;
begin
for j:=low(t)+1 to high(t) do
begin
key:=t[j];
i:=j-1;
while (i>pred(low(t))) and (t[i]>key) do
begin
t[i+1]:=t[i];
i:=i-1;
end;
t[i+1]:=key;
end;
end;
begin
clrscr;
writeln('Sortowanie przez wstawianie.');
end.
11
Ten moduł nie zawiera prywatnej procedury swap, bo jest ona tutaj zbędna.
Kolejny moduł zawiera kod związany z sortowaniem bąbelkowym:
unit bsort;
interface
uses crt,msort;
procedure sortuj(var t:tablica);
implementation
procedure swap(var x,y:byte);
var
tmp:byte;
begin
tmp:=x;
x:=y;
y:=tmp;
end;
procedure sortuj;
var
i,j,tmp:byte;
begin
for i:=low(t) to high(t)-1 do
for j:=high(t) downto i+1 do
if t[j-1] > t[j] then swap(t[j],t[j-1]);
end;
begin
clrscr;
writeln('Sortowanie bąbelkowe.');
end.
12
W programie będzie użyty jeden z trzech przedstawionych modułów
zawierających procedurę sortowania. Oto kod tego programu:
program moduly_i_sortowanie;
uses
bsort,msort;
var
tab:tablica;
begin
wypelnij(tab);
wypisz(tab);
sortuj(tab);
wypisz(tab);
readln;
end.
Aby zmienić sposób sortowania tablicy w tym programie wystarczy zmienić
moduł zawierający procedurę sortuj. W kodzie, który jest zamieszczony powyżej
wykonywane jest sortowanie bąbelkowe. Jeśli zmienimy nazwę modułu bsort,
na ssort, to program będzie korzystał z tego ostatniego i sortował przez wybór.
Należy zauważyć, że gdyby nie część inicjalizacyjna modułów, to nie bylibyśmy
w stanie poznać jaką metodą została posortowana tablica.
13
Wyszukiwarka
Podobne podstrony:
PP1 wykladPP1 wyklad 8PP1 wyklad 3PP1 wyklad 5PP1 wyklad 1PP1 wyklad 2PP1 wykladPP1 wyklad 4PP1 wyklad 5PP1 wyklad 9PP1 wykladPP1 wyklad 4Sieci komputerowe wyklady dr FurtakWykład 05 Opadanie i fluidyzacjaWYKŁAD 1 Wprowadzenie do biotechnologii farmaceutycznejwięcej podobnych podstron