Podprogramy - funkcje
Stosowanie procedur i funkcji (podprogramów) umożliwia rozbicie złożonego ciągu operacji na mniejsze struktury. Zwiększa to czytelność programu, poprawia kontrolę nad rozbudowanym programem i umożliwia wielokrotne wykorzystanie bloków instrukcji ze zmienionymi danymi wejściowymi. Podprogramy mogą mieć strukturę hierarchiczną, a zatem kolejna procedura może być definiowana we "wnętrzu" innej.
Zadaniem funkcji jest obliczenie pojedynczej wartości zaś procedura wykonuje sekwencję działań i może wyprowadzać wiele obliczonych wartości.
Definicja procedury lub/i funkcji umieszczana jest w części deklaracyjnej programu głównego (także innej procedury lub funkcji):
function nazwa (lista parametrów formalnych): typ prosty;
definicje, deklaracje
begin
....
nazwa := wyrażenie;
....
end;
W nagłówku definicji muszą się znaleźć parametry przekazywane do wnętrza funkcji z określeniem ich typów (elementy listy oddzielane są średnikami) oraz typ funkcji (prosty).
Funkcja zapewnia w zasadzie zwrot pojedynczej wartości poprzez jej nazwę (obiekt określonego typu prostego), chociaż można również stosować przekazanie przez zmienną (jak w procedurze, patrz następne ćwiczenie). W bloku wykonawczym funkcji musi zatem znaleźć się instrukcja dokonująca nadanie wartości funkcji (np. instrukcja przypisania dla nazwy funkcji).
Przykładowe nagłówki deklaracji funkcji:
function suma (x1, x2: real):real;
function sprawdzaj(x1, x2: real; y1, y2: integer ):boolean;
Wykonanie (wywołanie) funkcji odbywa się w wyrażeniu odpowiedniego typu (także w procedurze wyprowadzenia write(ln)). Parametry wywołania, zwane parametrami aktualnymi, mogą być stałymi, zmiennymi lub wyrażeniami, oddzielane są przecinkami. Liczba i typ parametrów aktualnych musi być zgodna z deklaracją funkcji
Przykładowy program ilustruje możliwe sposoby wywołania funkcji:
program p10_1;
var a, b,c :real;
{definicja funkcji}
function iloczyn (x1, x2: real): real;
begin
iloczyn := x1*x2; {nadanie wartości funkcji}
end; {koniec definicji funkcji}
begin {program główny}
a := 3.4;
b := 5.6;
writeln (iloczyn( a, b ) ); {użycie funkcji jako parametru instrukcji writeln}
c := 3 iloczyn (a, b);
writeln (c:10:5);
c := 2 iloczyn (b, 1- a)/iloczyn (a, 2*b +1); {użycie funkcji w wyrażeniu}
writeln( c:10:5);
c := 1/iloczyn (c-1, a);
writeln( c:10:5);
end.
Wywołanie (wykonanie) funkcji w programie dokonywane jest zatem tak jak użycie zmiennej (podobnie jak funkcje standardowe). Wartości parametrów aktualnych (lub obliczonych wyrażeń) przekazywane są do funkcji (kopiowane na parametry formalne), następnie wykonywane są instrukcje w ciele funkcji i wartość obliczona zwracana jest pod nazwą funkcji do miejsca wywołania.
Poniższy program tabelaryzuje wartości funkcji opisanej przedziałami, w 21 punktach zakresu (0.0, 2.0):
program p10_2;
uses crt;
var i: 0..20;
function fun(x: real): real; {definicja funkcji}
var xkw: real;
begin
xkw := xx;
if x := 0.5 then fun := 0.2xkw-x+0.1
else fun := xkw/(xkw-0.1)
end; {koniec definicji funkcji}
begin {początek programu głównego}
clrscr;
writeln;
writeln ('Tabela wartosci funkcji':45);
writeln;
writeln ('x':30, 'f(x)':20);
for i := 0 to 20 do
begin
writeln(i:12, i0.1:20:3, fun(i0.1):20:5)
end;
repeat until keypressed
end .
Ćwiczenie
Wykonać programy przykładowe p10_1, p10_2. Przeanalizować postać deklaracji funkcji, cel jej stosowania, postaci instrukcji wykorzystującej funkcje.
Wykonywać program krokowo (Trace into) obserwując przejście do bloku funkcji w momencie napotkania wywołania funkcji. W oknie Watch obserwować wartości zmiennych i moment nadania wartości zmiennej reprezentującej funkcję.
Napisać program zawierający deklarację funkcji określonej jako:
f(x) = -x +2 dla x<0
f(x) = x - 2 dla x>=0
Program główny wyznacza i wyprowadza na ekran wartości funkcji dla x = -5, -2, 1, 4.5 i 6.34.
Zmodyfikować poprzedni program tak, aby funkcja była wykorzystywana w iteracji wyprowadzającej wartości w 20 punktach z przedziału (-5.0; 5.0).
Sporządzić program, którego zadaniem będzie wykorzystanie funkcji typu logicznego sprawdzającej czy dwie wprowadzone wartości typu całkowitego są sobie równe.
Podprogramy - procedury
Procedury mogą wykonać sekwencję działań na wartościach wprowadzonych do jej wnętrza i wyprowadzać wiele obliczonych wartości.
Definicja procedury podobnie jak funkcji umieszczana jest w części deklaracyjnej programu głównego (także innej procedury lub funkcji):
procedure nazwa (parametry formalne);
definicje, deklaracje
begin
....
instrukcje
....
end;
Parametry formalne służą do przekazywania danych pomiędzy blokiem, w którym wykonywana jest procedura, a samą procedurą. Niektóre z nich przekazują dane wejściowe do funkcji lub procedury, inne mogą zwrócić wyliczone w podprogramie wartości do programu głównego lub nadrzędnego podprogramu. Deklaracje parametrów są oddzielane średnikami, każda deklaracja składa się z listy zmiennych i nazwy typu.
Przykładowe nagłówki deklaracji procedury :
procedure drukuj (x1, x2: real);
procedure suma (liczba1, liczba2: real; var wynik: real);
Poprzedzające listę słowo kluczowe var określa przekazanie danych przez zmienną, jego brak przekazanie danych przez wartość. Przekazanie przez wartość stosuje się w celu wniesienia danych wejściowych do podprogramu, natomiast przekazanie przez zmienną ma na celu zwrot wyników działania procedury lub funkcji do miejsca wywołania.
Przekazanie przez wartość traktowane jest jako nadanie wartości początkowej zmiennej lokalnej, w momencie zakończenia podprogramu parametr formalny przestaje istnieć, wartość argumentu nie zostaje zmieniona.
Przekazanie przez zmienną może przenieść wartość do procedury (wejście), po jej zakończeniu powoduje zwrot nowej wartości do programu wywołującego procedurę.
Wykonanie procedury jest odrębną instrukcją. Parametry aktualne odpowiadające pozycji wywołania przez wartość mogą być stałymi, zmiennymi lub wyrażeniami, natomiast parametry aktualne odpowiadające przekazaniu przez zmienną muszą być identyfikatorami (nazwami) zmiennych.
program p11_1;
var a, b, c :real;
procedure suma (liczba1, liczba2: real; var wynik: real);
begin
wynik := liczba1 + liczba2;
end;
begin {początek programu głównego}
a := 2;
b := 2;
{przykładowe wykonania procedury}
suma (6.7, 2*a, c); {trzeci parametr aktualny musi być zmienną}
suma (a , b , c);
suma (c , c , c);
suma (c, b , a)
end.
Zasięg deklaracji zmiennych
Deklaracje obiektów w programie głównym zwane są globalnymi, "widoczne" są w całym programie, także i w podprogramach (czasami mogą być tam "przesłonięte" czyli ponownie deklarowane. Deklaracje obiektów w definicji podprogramu to deklaracje lokalne dla podprogramu, nie będą "widoczne" w programie wywołującym. Jeśli w ciele definicji podprogramu jest deklaracja zagnieżdżonego podprogramu to obiekty lokalne stają się dla niego zewnętrznymi.
Zasięg deklaracji ilustruje poniższy rysunek:
deklaracje globalne widoczne w całym programie (chyba że przesłonięte w B, C lub D)
deklaracje lokalne w B (zewnętrzne dla C)
deklaracje lokalne w C (deklaracje A i B można przesłaniać)
tu można wykorzystać procedurę C
deklaracje lokalne w D (deklaracje A można przesłaniać, procedura C niewidoczna)
blok główny programu, tu można wywołać procedury B i D lecz nie procedurę C
Rys 11.1 Zasięg deklaracji w Turbo Pascalu.
Ćwiczenie
W przykładzie p11_1 zastanowić się nad działaniem poszczególnych instrukcji wywołujących procedurę. Przewidzieć rezultaty kolejnych wykonań procedury, w razie potrzeby wzbogacić program w wydruki kontrolne, sprawdzające przewidywane wartości parametrów wyprowadzonych przez zmienną.
Napisać program zawierający deklarację procedury obliczającej iloczyn dwóch zmiennych zespolonych, określonej jako:
(a + jb)(c+jd) = ac - bd + j (ad + bd)
Zastosować zmienną typu rekordowego o strukturze:
var zesp= record
re, im :real;
end;
W programie głównym wykonać obliczenia iloczynu dowolnych dwóch liczb zespolonych.
Napisać program, którego zadaniem będzie utworzenie niewielkiej tablicy jednowymiarowej zawierającej liczby rzeczywiste oraz zdefiniowanie i wykorzystanie procedury znajdującej w tablicy element o maksymalnej wartości.
Zadanie domowe:
Przygotować na dyskietce kod programów przykładowych następnego ćwiczenia
Pliki
Typ plikowy
Plik w pojęciu Turbo Pascala jest ciągiem elementów tego samego typu, odwzorowującym fizyczny zbiór danych gromadzonych w pliku umieszczonym w pamięci zewnętrznej. Dostęp do pliku jest sekwencyjny, tzn. po otwarciu pliku dostępny jest pierwszy element, zaś inne elementy mogą być udostępniane tylko po wykonaniu pewnych operacji (czytanie kolejnych elementów, przesunięcie pozycji czytania).
Aby uzyskać dostęp do pliku fizycznego należy zadeklarować w programie zmienną typu plikowego, którą trzeba skojarzyć z plikiem dyskowym. Zmienną tę można kojarzyć kolejno z różnymi plikami.
Plik może mieć dowolny typ składowych (oprócz typu plikowego).
Definicja typu plikowego ma postać:
type nazwa = file of typ; plik elementowy (zdefiniowany)
type nazwa = file; plik blokowy (niezdefiniowany)
type nazwa = text; plik tekstowy (predefiniowany)
Przykład użycia pliku elementowego:
type
osoba = record
nazw: string[30];
imie: string[30]
end;
grupa = file of osoba; {typ plikowy}
var
student: osoba;
gr1, gr2, gr3: grupa; {zmienne typu plikowego}
begin
...
end.
Dla całościowych zmiennych plikowych możliwe są jedynie niektóre operacje systemu plików, natomiast na składowych możemy wykonywać operacje w zależności od typu składowych.
W pliku elementowym zapis dokonuje się w postaci binarnej, a w tekstowym nawet elementy liczbowe zapisywane są jako tekst.
Obsługa plików wymaga:
opisu zmiennej plikowej w części deklaracyjnej,
skojarzenia zmiennej plikowej z plikiem fizycznym,
otwarcia pliku (do zapisu lub do odczytu),
wykonania niezbędnych operacji (czytanie, zapis i inne),
zamknięcia pliku.
Operacje plikowe
W poniższej tabeli przedstawiono podstawowe operacje plikowe, w których użyto określeń:
f - nazwa zmiennej typu plikowego,
Wt - wyrażenie tekstowe,
W - wyrażenie odpowiedniego typu,
Z - nazwa zmiennej.
TABELA 12.1. Podstawowe operacje plikowe
Operacja |
Typ |
Zapis |
Przykład |
skojarzenie |
procedura |
assign (f , Wt) |
assign(p1,'C:\DANE\gr.dt'); |
otwarcie do odczytu i odczytu (istniejący plik) |
procedura |
reset (f); |
reset (p1); |
otwarcie do zapisu (nowy plik) |
procedura |
rewrite (f); |
rewrite (p2); |
dopisanie (na końcu pliku tekstowego ) |
procedura |
append (f); |
append (p2); |
zamknięcie pliku |
procedura |
close (f); |
close (p3); |
detekcja końca pliku |
funkcja logiczna |
eof (f) |
if eof (p1)= true then ... if eof (p1) then ... while not eof(gr1) do... |
odczyt |
procedura |
read (f,Z1,..,Zn); |
read (gr1, student.nazw); |
zapis |
procedura |
write(f, W); |
write (gr2, nazwisko); |
Uwagi:
Po otwarciu do odczytu wskaźnik czytania ustawia się na początku pliku,
Po przeczytaniu elementu następuje przesunięcie wskaźnika odczytu do następnego elementu, aby przeczytać dowolny element wewnątrz pliku należy uprzednio przeczytać elementy poprzedzające,
Otwarcie nowego pliku powoduje utworzenie pustego pliku,
Gdy eof(f) = true - wskaźnik odczytu (zapisu) znajduje się na końcu pliku,
Gdy po wykonaniu procedury reset okaże się że eof(file) = true to plik jest pusty,
Instrukcja writeln istnieje tylko dla pliku typu tekstowego
Zapis dopuszczalny tylko na końcu pliku (niemożliwy zapis "w środku" pliku),
Każdy zapis przesuwa wskaźnik o jedną pozycję.
Dopisanie "w środku" pliku wykonuje się przez etapowe przepisywanie do innego pliku.
Przykłady
Poniższy program zawiera modyfikację wcześniejszego przykładu z użyciem procedury oraz zapisu tabeli funkcji do pliku tekstowego:
program p12_1;
uses crt;
var i: 0..20;
y: real;
w: char;
plik: text;
procedure oblicz(x:real; var f:real; var wsk:char); {definicja procedury}
var xkw: real;
begin
xkw := xx;
if x = 0.5 then f := 0.2xkw-x+0.1
else f := xkw/(xkw-0.1);
if f < 0 then wsk := '' else wsk := '+'
end;
begin {początek akcji programu}
assign(plik, 'c:\tp\moje\fun.txt'); {skojarzenie pliku}
rewrite(plik); {otwarcie pliku do zapisu}
clrscr;
writeln;
writeln('Tabela wartosci funkcji':45);
writeln;
writeln('x':30, 'f(x)': 20);
for i := 0 to 20 do
begin
oblicz(i0.1, y, w); {wywołanie procedury}
writeln(i:12, i0.1:20:3, y:20:5, w:3);
writeln(plik, i:12, i0.1:20:3, y:20:5); {zapis do pliku}
end;
close(plik); {zamknięcie pliku}
repeat until keypressed
end .
Poniższy przykład ilustruje wykorzystanie bardziej złożonych struktur i algorytmów:
program p12_2;
{Ilustracja użycia typu rekordowego wraz z zapisem/odczytem listy z dysku}
{ program wczytuje listę osób z klawiatury do tablicy rekordów, a następnie wyświetla listę posortowana według zadanego pola }
uses crt;
type
TOsoba = record { rekord zawierający dane osobowe }
Imie :string[ 25 ];
Nazwisko :string[ 25 ];
Adres :string[ 25 ];
wiek :integer;
Plec :(K,M);
end;
const
MaxOsob = 30; { maksymalny rozmiar tablicy z danymi osobowymi }
var
ListaOsob : array [ 1..MaxOsob ] of TOsoba;
IloscOsob : integer;
NastepnaOsoba : boolean;
Znak :char;
i,n : integer;
Zamienic : boolean;
pom :TOsoba; {zmienna pomocnicza do sortowania tablicy }
f : file of TOsoba;
Nazwa : string;
begin {początek programu}
Clrscr;
repeat
writeln('0 - wprowadzanie danych z klawiatury');
writeln('1 - odczyt danych z dysku');
write('Twoj wybór [ 0/1 ]:');readln(znak);
until (znak = '0') or (znak = '1');
if znak = '0' then
begin { wczytanie danych z klawiatury }
NastepnaOsoba := true;
Iloscosob := 0;
while NastepnaOsoba and (IloscOsob <= MaxOsob) do
begin
inc(IloscOsob); {zwiększ N o 1}
with ListaOsob[ IloscOsob ] do
begin
write('Imie: '); readln(Imie);
write('Nazwisko: '); readln(Nazwisko);
write('Adres: '); readln(Adres);
write('Wiek: '); readln(Wiek);
write('Plec [ K/M ]:');
repeat
readln(Znak);
Znak := UpCase(Znak);
until (Znak = 'K') or (Znak = 'M');
case znak of
'K': Plec := K
else Plec := M
end;
end; {koniec with}
if IloscOsob < MaxOsob then
begin
writeln;
writeln('Wprowadziles do tej pory ',IloscOsob,' osob');
write('Chcesz podac nastepna osobe? [ T/N ]');
repeat
readln(Znak);
Znak := UpCase(Znak);
until (Znak = 'T') or (Znak = 'N');
NastepnaOsoba := Znak='T';
end;
end;
writeln;
write('Zapisać wprowadzone osoby na dysku [ T/N ]? ');
repeat
readln(Znak);
Znak := UpCase(Znak);
until (Znak = 'T') or (Znak = 'N');
if znak = 'T' then
begin{ zapis listy na dysk }
write( 'Podaj nazwe pliku:' ); readln(nazwa);
if nazwa <> '' then
begin
assign(f , nazwa);
rewrite(f);
for i := 1 to IloscOsob do write(f ,ListaOsob[ i ]);
Close(f);
end;
end;
end { koniec wprowadzania z klawiatury }
else { odczyt z dysku }
begin
repeat
write('Podaj nazwe pliku:');
readln(Nazwa);
until Nazwa < > '';
assign(f,Nazwa); {przypisanie zmiennej plikowej pliku dyskowego}
reset(f); {otwarcie pliku do odczytu}
while not eof(f) and (IloscOsob < MaxOsob) do
begin
inc(IloscOsob);
read(f,ListaOsob [Iloscosob ]);
end;
Close(f); {zamknięcie pliku}
end; { koniec odczytu z dysku }
{sortowanie i wyswietlanie listy }
repeat {wybor sposobu sortowania }
writeln('Wybierz porzadek sortowania:');
writeln(' I - imie');
writeln(' N - nazwisko');
writeln(' W - wiek');
writeln(' P - Plec');
readln(znak);
znak := UpCase(znak);
case znak of
'I' :writeln('Lista posortowana według imion');
'N' :writeln('Lista posortowana według nazwisk');
W' :writeln('Lista posortowana według wieku');
'P' :writeln('Lista posortowana według plci');
else writeln('Lista nieposortowana');
end;
{sortowanie }
for i := 2 to IloscOsob do
begin
n := i;
repeat
case znak of
'I' : Zamienic := ListaOsob[ n ].Imie< ListaOsob[ n -1 ].Imie;
'N' :Zamienic := ListaOsob[ n ].Nazwisko< ListaOsob[n -1]. Nazwisko;
'W' : Zamienic := ListaOsob[ n ].Wiek < ListaOsob[ n-1 ].Wiek;
'P' : Zamienic := ListaOsob[ n ].Plec < ListaOsob[ n -1 ].Plec;
end;
if Zamienic then
begin
pom := Listaosob[ n ];
Listaosob[ n ] := ListaOsob[ n -1 ];
Listaosob[ n -1 ] := pom;
end;
Dec(n); {zmniejsz N o 1}
until (n = 1) or (not Zamienic);
end; {koniec sortowania}
{ wyswietlenie listy }
for i := 1 to IloscOsob do
begin
write(i:2,' ',ListaOsob[ i ].Imie,' ',ListaOsob[ i ].Nazwisko,' lat ',ListaOsob[ i ].Wiek,' ');
if ListaOsob[ i ].Plec=K then write('K')
else write('M');
writeln( ' ',Listaosob[ i ].Adres);
end;
writeln;
write('Wyświetlić powtórnie [ T/N ]:');readln(znak);
until Upcase(znak) = 'N';
end.
Ćwiczenie
Zainstalować przygotowane w domu kody programów p12_1 i p12_2.
Zastanowić się nad ich strukturą i realizacją algorytmów. Znaleźć w kodzie instrukcje wykorzystujące procedury obsługi plików.
Prześledzić krokowo program p12_1. Sprawdzić w DOS-Shell pojawienie się pliku tekstowego - obejrzeć jego zawartość dowolnym edytorem ASCII.
Przy pomocy programu p12_2 założyć niewielki plik elementowy. Sprawdzić poprawność wykonania programu przez jego ponowne wykonanie z odczytem i sortowaniem danych
Sporządzić program do zakładania pliku elementowego składającego się z kilku rekordów o strukturze AUTO (marka, rocznik, cena). Następnie napisać program odczytujący ten plik, wypisujący na ekranie: wszystkie rekordy, tylko auta o wybranej marce, auta o cenie wyższej niż 10000 zł.
Algorytmy rekurencyjne
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 rzędu N liczby rzeczywistej x:
xn = jeśli n > 0 to xn-1*x
jeśli n = 0 to 1
Rekurencja w językach programowania jest realizowana przy pomocy podprogramów wywołujących kolejno same siebie ze zmienianymi parametrami wywołania. Podprogramy rekurencyjne aby poprawnie działały muszą zawierać warunek zakończenia rekurencji, czyli wywołanie jest wykonywane skończoną liczbę razy.
Rekurencja daje proste 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 liczby danych.
Przykłady:
program p13_1;
{wyznaczanie potęgi całkowitej nieujemnej N z liczby x metodą rekurencyjną}
function PotegaN(x:real;N:integer):real;
{ funkcja wyznacza potege N z liczby X }
begin
if N=0 then PotegaN := 1
else PotegaN := PotegaN(x, N - 1)*x; { w definicji funkcji wykorzystanie tejże funkcji}
end;
begin {program główny}
writeln('2^10=',PotegaN(2,10):0:0);
end.
program p13_2; {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.
Ćwiczenie
Sporządzić kody programów przykładowych z rozdziału.
Wykonywać programy krokowo (Trace into - F7) obserwując wielokrotne wykonywanie funkcji z odkładaniem wartości na stos, a po osiągnięciu warunku końcowego przeliczenie odłożonych watości (potęgowanie, obliczanie silni jako iloczynu). Obserwować warość parametru funkcji w oknie Watch.
Wykonać samodzielnie program z wykorzystaniem rekurencji dla wyznaczania sumy kolejnych liczb naturalnych parzystych. Parametrem funkcji jest zakres obliczeń - np. 200.
Moduły
Moduły są podstawą programowania modularnego i służą do grupowania funkcji i procedur w biblioteki. Moduł nie stanowi samodzielnego programu. Użycie procedur, funkcji, zmiennych definiowanych w module wymaga deklaracji modułu (uses moduł) w programie użytkowym. Moduł powinien być wcześniej skompilowany i umieszczony jako plik w katalogu UNITS.
Opis struktury modułu
unit nazwa_modułu;
interface {część opisowa }
deklaracja modułów {zawsze pierwsza}
definicja stałych
definicja typów
definicja zmiennych
lista nagłówków procedur i funkcji
implementation {część implementacyjna}
deklaracje i definicje stałych, typów i zmiennych elementów dostępnych tylko w module
definicje procedur i funkcji
initialization {wystepuje rzadko}
działania inicjacyjne
finalization {wystepuje rzadko}
działania finalizacyjne
end .
Przykład modułu:
unit cmplx;
interface
type complex = record
im,re:real
end;
procedure dodaj (a,b:complex; var c:complex);
procedure odejmij (a,b:complex; var c:complex);
............ {i inne}
implementation
procedure dodaj(a,b:complex; var c:complex);
begin
c.re := a.re + b.re;
c.im := a.im + b.im
end;
procedure odejmij (a,b:complex; var c:complex);
begin
c.re := a.re - b.re;
c.im := a.im - b.im
end;
end.
Tak utworzony moduł powinien być poprawnie skompilowany na dysk (opcja Destination - disk w menu Compile). Jego skompilowany plik z rozszerzeniem *.tpu powstanie w katalogu określonym w menu Options - Directories... jako katalog EXE & TPU. Następnie należy go przenieść (lub skopiować) do katalogu określonego w menu Options - Directories... jako Units directory, wówczas można z niego korzystać w dowolnym programie.
Przykład wykorzystania modułu:
Program m12;
uses cmplx;
var x,y,z : complex; {można korzystać z typów definiowanych w module}
begin
........ {wczytanie x i y}
dodaj (x, y, z);
writeln ( ' Suma= ' , z.re:12:5 , z.im:12:5);
odejmij (x , y , z);
writeln ( ' Różnica=', z.re:12:5 , z.im:12:5)
end.
Jeśli jeden moduł wykorzystuje wewnątrz funkcje lub procedury innego modułu, deklaracja powinna wyglądać następująco:
unit A;
interface
.............
implementation
uses B;
..............
jeżeli zaś będzie
unit B;
interface
.............
implementation
uses A;
..............
to moduły A i B są wzajemnie zależne.
Moduły standardowe TurboPascala
Moduł Printer
Zawiera deklarację pliku tekstowego Lst, dzięki któremu możemy drukować tekstowo na drukarce:
program drukuj;
uses Printer;
begin
writeln(Lst, 'Tekst wyprowadzany')
end.
Moduł System
(Uwaga: nie trzeba go deklarować w bloku Uses)
Zawiera:
Predefiniowane stałe i zmienne dla identyfikacji błędów, obsługi wejścia i wyjścia.
Procedury przerwania iteracji (pętli for, while, repeat) i programu:
Break - przerwanie pętli,
Exit - wyjście z bieżącego bloku,
Halt - przerwanie programu.
Funkcje i procedury dynamicznego przydziału pamięci: New, Dispose i inne, adresowe i wskaźnikowe.
Funkcje konwersji: Chr, Ord, Round, Trunc.
Arytmetyczne funkcje standardowe,
Procedury porządkowe: Inc, Dec, Odd, Pred, Succ,
Funkcje i procedury łańcuchowe: Concat, Copy, Length,
Concat (lista_łańcuchów) - połączenie łańcuchów,
Copy (łańcuch, pozycja, długość) - wycięcie z łańcucha,
Length (łańcuch) - długość łańcucha,
Str (argument numeryczny, zmienna łańcuchowa), - zamiana wartości na łańcuch,
Val(łańcuch, zmienna numeryczna, kod) - zamiana łańcucha na wartość (kod - pozycja pierwszego błędnego znaku),
Procedury i funkcje obsługi zbiorów: Assign, Reset, Rewrite, Close, Eof, Eoln i inne,
Inne procedury i funkcje, np.:
Randomize - inicjacja generatora liczb losowych,
Random(zakres) - wartość losowa z zakresu Randomize.
Moduł Crt
Zawiera predefiniowane identyfikatory: trybu ekranu, kolorów
Podprogramy sterowania trybami ekranu:
ClrScr, Window, Gotoxy (x,y), TextColor, TextBackground i inne
Funkcje:
Readkey - wczytanie jednego znaku
Procedury dźwiękowe:
Delay(opóźnienie w milisekundach);
Sound (częstotliwość);
NoSound; - przerwanie dźwięku
Moduł Graph - funkcje i procedury graficzne.
Moduł ten zawiera funkcje i procedury użyteczne przy tworzeniu grafiki ekranowej:
procedury udostępniające tryb i sterowniki graficzne, inisjujące i zamykające tryb graficzny.
procedury ustalające kolor, styl dla tła, linii itp.,
procedury rysowania figur prymitywnych,
Szczegółową postać podprogramów dostępnych w module Graph oraz opis ich parametrów znaleźć można literaturze lub w pomocy ekranowej.
Inne moduły
Strings - Funkcje i procedury operacji na tekstach,
Dos i WinDos - Funkcje i procedury do bezpośredniego kontaktu z systemem operacyjnym.
Ćwiczenie
Sporządzić moduł gromadzący podprogramy działań na liczbach zespolonych:
unit cmplx;
interface
type complex = record
im,re:real
end;
procedure dodaj (a,b:complex; var c:complex); {nagłowki funkcji i procedur}
procedure odejmij (a,b:complex; var c:complex);
{i inne}
implementation
procedure dodaj(a,b:complex; var c:complex);
begin
c.re := a.re + b.re;
c.im := a.im + b.im
end;
procedure odejmij (a,b:complex; var c:complex);
begin
c.re := a.re - b.re;
c.im := a.im - b.im
end;
end.
Skompilować moduł na dysk (menu Compile - opcja Destination - disk). Jego skompilowany plik z rozszerzeniem tpu powstanie w katalogu określonym w menu Options - Directories... jako katalog EXE & TPU. Następnie należy go przenieść (lub skopiować) do katalogu określonego w menu Options - Directories... jako Units directory.
Napisać program wykorzystujący utworzony moduł:
program p14_1;
uses cmplx; {deklaracja modułu}
var a,b,c:complex; { z typu opisanego w module też można korzystać}
begin
a.re:=1;
a.im:=1;
b.re:=1;
b.im:=1;
dodaj(a,b, c);
writeln(c.re,c.im)
end.
Przetestować program.
Uzupełnić moduł o definicję procedury mnożenia liczb zespolonych i wykorzystać ją w programie głównym.
Napisać program zawierający dowolną iterację. Zastosować procedurę biblioteczną break powodującą przerwanie iteracji. Wypróbować jej działanie. Deklaracja modułu System zawierającego procedurę break nie jest potrzebna.
W dowolnym, krótkim programie wypróbować działanie procedury halt.
Typ zbiorowy
Typ zbiorowy to zbiór potęgowy danego typu porządkowego, czyli zbiór wszystkich podzbiorów tego typu.
Definicja typu:
type nazwa_typu = set of typ_porządkowy;
Liczba elementów nie może przekraczać 256 - czyli typ bazowy może być tylko typem Byte, okrojonym całkowitym, znakowym (w tym okrojonym) wyliczeniowym i logicznym.
Wartości typu zbiorowego zapisujemy w nawiasach kwadratowych.
Przykłady definicji typu zbiorowego:
type
dni = set of (pon,wto,sro,czw,pia,sob,nie);
znaki = set of 'a'..'z';
miesiac = (sty,lut,mar,kwi,maj,cze,lip,się,wrz,paz,lis,gru);
zbior_miesiecy = set of miesiac;
var nazwa_miesiaca : miesiac;
zbior_nazw : zbior_miesiecy;
UWAGA: nazwa_miesiaca może przyjąć wartość tylko jednej z nazw;
zbior_nazw może przyjąć wartość dowolnego zbioru z nazw bazowych.
Zmiennych typu zbiorowego nie wolno używać w instrukcjach czytania i wyprowadzania wyników, używa się ich jedynie w operacjach wykonawczych, testujących itp.
Operacje logiczne - relacje (porównania) teoriomnogościowe:
a = b równość zbiorów, te same elementy w obu zbiorach,
a <> b różność zbiorów, różne elementy w obu zbiorach (chociaż niektóre mogą się powtarzać)
a <= b zawieranie zbioru a w zbiorze b (prawda jeśli każdy element zbioru a jest w zbiorze b)
a >= b zawieranie zbioru b w zbiorze a (prawda jeśli każdy element zbioru b jest w zbiorze a)
c in a czy element c jest w zbiorze a
Przykład:
program p15_1;
uses crt;
type miesiace = set of 1..12;
var v, x, y, z:miesiace;
lit :char;
begin
clrscr ;
y:=[1..8];
x:=[1..12];
z:=[1,3,5 ,7 ,8, 10, 12]; {miesiące posiadające 31 dni}
lit:='a' ;
if x<>y then writeln('tak') else writeln( 'nie' ); {tak}
if x<=y then writeln('tak') else writeln( 'nie' ); {nie}
if x>=y then writeln('tak') else writeln( 'nie' ); {tak}
if x=y then writeln('tak') else writeln( 'nie' ); {nie}
if 1 in x then writeln('tak') else writeln( 'nie' ); {tak}
if 9 in y then writeln('tak') else writeln( 'nie' ); {nie}
if lit in ['a'..'z'] then writeln('tak') else writeln( 'nie' ); {tak}
end.
Operacje zbiorowe:
+ suma zbiorów
- różnica zbiorów
∗ iloczyn zbiorów
Przykład:
program p15_2;
type zbiorowy = set of 1..6;
var x , y, v: zbiorowy;
begin
x :=[2,3,4] + [4,5,6] ; {wynik [2,3,4,5,6]}
y := [2,3,4] - [4,5,6] ; {wynik [2,3]}
v := [2,3,4] * [4,5,6] ; {wynik [4] (część wspólna)}
y := v * x ; { przeanalizować wynik}
end.
Ćwiczenie
Sporządzić kod programu p15_1 i wykonać go. Przeanalizować poszczególne instrukcje.
Sporządzić kod programu p15_2 i wykonać go. Przeanalizować poszczególne operacje zbiorowe.
Uzupełnić program o różnicę zbioru wszystkich miesięcy i zbioru miesięcy posiadających 31 dni. Wynik odejmowania przechować w nowej zmiennej p. Sprawdzić przy pomocy instrukcji:
if 2 in p then writeln('luty nie ma 31 dni');
zawieranie się lutego w zbiorze wynikowym.
Napisać program definiujący trzy zmienne typu zbiorowego bazujące na typie znakowym char. Dokonać przypisań dla dwóch z tych zmiennych wartości dowolnych podzbiorów. Wyniki działań (dodawania, odejmowania, mnożenia) obu zmiennych przechować w trzeciej. Sprawdzić przy pomocy menu Debug-Watch że wynik operacji mnożenia stanowi część wspólną obu podzbiorów.
DODATEK
Dodatek zawiera materiały i informacje dla samodzielnego poszerzenia wiedzy w zakresie programowania w języku TurboPascal.
1. Typ proceduralny
Procedury i funkcje mogą być traktowane nie tylko jako części programu do wykonania lecz także jako elementy przypisane zmiennym i przekazywane do innych funkcji lub procedur jako parametry
type nazwa = procedure ;
type nazwa = procedure (lista parametrów);
type nazwa = function :typ;
type nazwa = function (lista parametrów):typ;
Przykłady:
type proc : procedure;
proc1 = procedure (x : Real; k : Integer ; var y : Real);
fun = function (x : Real) : Real;
obl = procedure (x : Real; f : fun ; var y : Real) ;
var p : proc;
f : fun;
Przykład programu:
program typ_proceduralny;
uses crt;
type f=function(x:real):real;
var fun:f;
{definicje funkcji}
function sinus(x:real):real;
begin
sinus:=sin(x*pi/180)
end;
{----------------------------}
function cosinus(x:real):real;
begin
cosinus:=cos(x*pi/180)
end;
{----------------------------}
procedure wartosc(x:real;wej:f);
begin
writeln(wej(x):0:10);
end;
{----------------------------}
begin {program główny}
clrscr;
wartosc(0,sinus);
fun := cosinus;
wartosc(0,fun)
end.
UWAGA: nie wolno używać funkcji modułu System (standardowych)
2. Zmienne wskaźnikowe
Do tej pory używaliśmy zmiennych statycznych. Zmienna statyczna, zadeklarowana deklaracją VAR jest tworzona w chwili uruchomienia programu. Jej rozmiar definiujemy w trakcie pisania programu - tak więc w przypadku tablic pisząc program musimy przyjąć jakąś maksymalną liczbę elementów tablicy. Dodatkowym ograniczeniem w TurboPascal-u jest to, że zmienne zadeklarowane w jednym module nie mogą mięć więcej niż 64kB. Ominąć ten problem pozwalają tzw. zmienne dynamiczne nazywane popularnie wskaźnikami.
Zmienna statyczna z punktu widzenia procesora jest fragmentem pamięci o rozmiarze pozwalającym pomieścić dane określonego typu. W trakcie wykonywania programu procesor nic nie wie o nazwach zmiennych - posługuje się jedynie adresem, który mówi gdzie zmienna znajduje się w pamięci. W trakcie kompilacji na podstawie zadeklarowanego typu zmiennej kompilator przyporządkowuje odwołaniom do zmiennych konkretne procedury działające na zmiennych zadeklarowanego typu. Linker przydziela zaś zmiennym adresy, tak, aby nie nakładały się na siebie.
Zmienna wskaźnikowa pozwala w trakcie programu przydzielać pamięć zmiennym specjalnymi procedurami.
Zmienna wskaźnikowa jest więc adresem wskazującym na miejsce w pamięci, gdzie ma być przechowywana wartość zmiennej. Koncepcja wskaźników w Pascalu pozwala nam nie martwić się o długość zmiennej wskazywanej przez zmienną wskaźnikową (TurboPascal posiada mechanizmy pozwalające wyłączyć kontrolę kompilatora nad typem i długością zmiennej - mechanizmy te nie będą przedmiotem tego wykładu).
Deklaracja zmiennej wskaźnikowej
Zmienną wskaźnikową (czyli adres do zmiennej jakiegoś konkretnego typu) deklaruje się jako zmienną statyczną. Zmienna wskaźnikowa zajmuje 4 bajty.
var
nazwa_zmiennej_wskaznikowej :^typ_zmiennej;
lub
type
nazwa_typu =^typ_zmiennej;
var
nazwa_zmiennej_wskaznikowej :nazwa_typu;
"nazwa_zmiennej_wskaznikowej" oraz "nazwa_typu" są dowolnymi akceptowanymi przez Pascal nazwami identyfikatorów.
"typ_zmiennej" określa typ zmiennej wskazywanej przez zadeklarowany wskaźnik. Na tej podstawie kompilator rozpoznaje jaką ilość pamięci zawiera zmienna wskazywana przez wskaźnik i jakich procedur użyć do wykonywania operacji na niej.,
Typ ten musi być określony jednym identyfikatorem!
Zmienna wskaźnikowa dopuszcza jeden wyjątek w podstawowej regule składni języka Pascal, że można używać tylko identyfikatorów wcześniej zadeklarowanych:
Deklaracja zmiennej (lub typu) wskaźnikowej może odwoływać się do typu, który zostanie zadeklarowany po deklaracji zmiennej (lub typu) wskaźnikowej.
type
pOsoba =^TOsoba;
TOsoba =record
imie,nazwisko :string;
nastepny :pOsoba;
end;
var
pzmienna:TOsoba;
Zwykle przy deklaracji typów wskaźnikowych (podobnie jak i zmiennych) nazwę identyfikatora typu lub zmiennej zaczyna się od małej litery "p" (od "point" - wskazywać).
Odwołanie do zmiennej wskaźnikowej
W naszym programie, aby dostać się do wartości na którą wskazuje zmienna wskaźnikowa (inaczej mówiąc do zmiennej umieszczonej w pamięci pod adresem zawartym w zmiennej wskaźnikowej) musimy do nazwy zmiennej dodać znak "^":
var
x,y :^integer;
z :integer;
begin
{...}
z := 2
x^ := z+2;
y := x^*3
z := x^+y^;
{...}
end.
^typ - nazwa typu wskazywanego przez zmienną wskaźnikową
x - zawiera adres w pamięci pod którym przechowywana jest wartość zmiennej
x^ - zawiera wartość zmiennej
Operacje na wskaźnikach
Na zmiennych wskaźnikowych można wykonać niewiele operacji. Zmienne wskaźnikowe jak i zmienne przez nie wskazywane można przypisywać - należy wtedy pamiętać aby typy były zgodne. Istnieje możliwość wyłączenia kontroli typów zmiennych wskazywanych przez wskaźniki - pozwala to na różne interpretacje wartości zmiennej. W podanym poniżej przykładzie zastosowano operator "@" - zmienna poprzedzona tym operatorem zwraca adres (czyli wskaźnik) do danej zmiennej. W przykładzie do zmiennej p (wskaźnika do tablicy 6-cio bajtowej) wstawiamy adres do zmiennej x typu REAL (też 6-cio bajtowej). Umożliwia nam to dostęp do poszczególnych bajtów zmiennej x:
Przykład:
type
TByteArray=array[1..6] of byte;
var
x : real;
p : ^TByteArray;
i : integer;
begin
x := 2.5;
p := @x; {przypisanie wskazania na 6-bajtową zmienną typu real do wskazania na równiwż 6-bajtową tablicę)
writeln('Liczba typu real o wartosci:',x:0,' składa się z następujacych bajtów:');
for i := 1 to 6 do
writeln('Bajt ',i,' = ',p^[i]); {wydruk poszczególnych bajtów liczby 2.5}
end.
Oprócz przypisania wskaźniki można porównywać. Aby stwierdzić czy zmiennej wskaźnikowej nadano wartość wprowadzono definicję specjalnego adresu, który oznacza umowny "brak adresu" - "NIL". Tak więc, aby sprawdzić czy zmienna wskaźnikowa wskazuje na jakąś zmienną można użyć konstrukcji:
if zmienna < > nil then ...
Niestety należy pamiętać, że likwidowanie zmiennej procedurą DISPOSE (o tym dalej) nie nadaje zmiennej wskaźnikowej wartości NIL. Operację tę należy przeprowadzić samodzielnie.
Tworzenie i likwidowanie zmiennych dynamicznych
Najważniejszą rzeczą przy zmiennych dynamicznych jest ich tworzenie (inaczej alokacja) w pamięci, a następnie ich likwidowanie. Do utworzenia zmiennej (czyli rezerwacji miejsca w pamięci na zmienną wskazywana przez zmienną wskaźnikową) służy procedura NEW:
procedure NEW(var x:pointer);
Procedura NEW rezerwuje w pamięci RAM obszar pamięci, który pozwoli przechować zmienną typu wskazywanego przez zmienną wskaźnikową x. Adres początku tego obszaru pamięci wpisywany jest do zmiennej x.
Operację odwrotną wykonuje procedura DISPOSE:
procedure DISPOSE(var x:pointer);
Lista
Najciekawszą strukturą danych oferowaną przez zmienne dynamiczne jest lista. Lista składa się z rekordów, w których co najmniej jedno z pól wskazuje na kolejny rekord tego samego typu. Istnieją różne rodzaje list - jednokierunkowa, dwukierunkowa, drzewo itp.
W listach należy pamiętać o tym, że każdy utworzony element musimy dołączyć do łańcuszka elementów, w przeciwnym wypadku, jeśli "zgubimy" adres do niego jest on stracony dla naszej aplikacji.
Przykład
program wskaz;
uses crt;
const n=3;
type
Wsk = ^ Osoba;
Osoba = record
Kod: 1..100;
Nazw: string[15];
Nast: Wsk;
end;
var osob_1,osob,pom1,pom2:wsk;
k:integer;
p:text;
{***************************************************}
procedure czytaj(var osob:wsk;N:integer);
var pom1,pom2:wsk;
begin
osob := nil; {puste wskazanie}
writeln ('podaj nr i nazwisko ',n, ' osob');
for k:=1 to n do
begin
new(pom1);
readln (pom1^.kod);
readln (pom1^.nazw);
pom1^.nast. := nil;
if osob = nil then
osob:=pom1
else
begin
pom2 := osob;
while pom2^.nast <> nil do
pom2 := pom2^.nast;
pom2^.nast:=pom1
end;
end;
end;
{***************************************************}
procedure dopisz(var osob:wsk);
var pom1,pom2,pom3:wsk;
begin
writeln('podaj nr nazwisko jednej osoby');
new(pom1);
readln(pom1^.kod);
readln(pom1^.nazw);
pom1^.nast:=nil;
pom2:=osob;
if pom1^.nazw <= pom2^.nazw then
osob:=pom1 {jesli na poczatku listy}
else
begin {szukanie miejsca w szeregu}
while pom1^.nazw > pom2^.nazw do
begin
pom3:=pom2; {zapamietanie miejsca}
pom2:=pom2^.nast; {miejsce nastepne}
end;
pom3^.nast:=pom1; {wskazanie na wczytane dane}
end;
pom1^.nast:=pom2; {wskazanie nastepnych danych}
end;
{***************************************************}
procedure pisz(var osob:wsk);
begin
clrscr;
writeln('zawartosc listy');
osob_1 := osob; {przepisanie wskazania do pomocniczej zmiennej}
while osob_1<> nil do
begin
writeln(osob_1^.kod:4,' ':4,osob_1^.nazw);
osob_1:=osob_1^.nast {przesuwanie wskazań}
end;
repeat until keypressed
end;
{****************************************************}
begin {program głowny}
clrscr;
czytaj(osob,N);
pisz(osob);
dopisz(osob);
pisz(osob);
repeat until keypressed;
end.
Poniżej przedstawiono schemat logiczny tworzenia listy jednokierunkowej:
Wstawianie nowego elementu do listy (z sortowaniem):
Lista dwukierunkowa, w której dwa pola wskaźnikowe każdej zmiennej zawierają wskazanie na element następny i poprzedni.
Na podobnej zasadzie działa struktura drzewa, w którym zmienna wskaźnikowa oprócz pól statycznych zawiera wiele pól zawierających bądź wskazanie puste (NIL) bądź na inne zmienne wskaźnikowe tego samego typu.
3. Zadania
Podaj postać poniższego wyrażenia w składni języka Pascal:
B. Podaj postać poniższego wyrażenia w składni języka Pascal:
E. Podaj postać poniższego wyrażenia w składni języka Pascal:
Opracuj kod programu w którym tablica 5x5 wypełniana jest w pętli kolumnami, a jej elementy są wartościami średnimi obydwu indeksów. Tablica wyprowadzana jest na ekran.
0,5 |
1,5 |
2 |
.. |
|
1,5 |
2 |
2,5 |
.. |
|
2 |
2,5 |
3 |
.. |
|
.. |
.. |
.. |
.. |
|
|
|
|
|
|
Opracuj kod programu w którym tablica 5x5 wypełniana jest w pętli kolumnami, a jej elementy są sumami obydwu indeksów. Tablica wyprowadzana jest na ekran.
2 |
3 |
4 |
.. |
|
3 |
4 |
5 |
.. |
|
4 |
5 |
6 |
.. |
|
.. |
.. |
.. |
.. |
|
|
|
|
|
|
Opracuj kod programu w którym tablica 5x5 wypełniana jest w pętli kolumnami, a jej elementy są różnicami indeksu wiersza i kolumny. Tablica wyprowadzana jest na ekran.
0 |
-1 |
-2 |
.. |
|
1 |
0 |
-1 |
.. |
|
2 |
1 |
0 |
.. |
|
.. |
.. |
.. |
.. |
|
|
|
|
|
|
Opracuj kod programu, w którym podawane są dwie liczby, a następnie wyświetlana jest reszta z dzielenia pierwszej z nich przez drugą.
Opracuj kod programu, w którym wektor o rozmiarze 100 wypełniany jest kwadratami kolejnych liczb parzystych aż do przekroczenia wartości 20000. Tablica wyprowadzana jest na ekran.
Opracuj kod programu, w którym tablica 20x20 wypełniana jest w pętli zerami oprócz przekątnej głównej, która jest wypełniona jedynkami. Tablica wyprowadzana jest na ekran.
Opracuj kod programu, w którym tabelaryzowana jest funkcja y= ln x + x2 w przedziale od 1 do 2 z krokiem 0.1.
Opracuj kod programu, w którym wektor o rozmiarze 30 wypełniany jest kolejnymi liczbami naturalnymi, Lecz jeśli liczba jest podzielna przez 3 to wstawiane jest 0. Tablica wyprowadzana jest na ekran.
Opracuj kod programu, w którym podajemy dwie liczby, następnie na ekranie drukowany jest ich iloczyn i iloraz. Operacja powtarza się w pętli aż do podania wartości zero dla pierwszej z liczb zadawanych.
Opracuj kod programu w którym tablica 50x50 wypełniana jest w pętli znakiem '+' lecz tylko tyle wierszy i kolumn ile zadamy z klawiatury przed wypełnianiem. Wypełniony fragment tablicy wyprowadzany jest na ekran.
Opracuj kod programu w którym cała tablica 50x50 wypełniana jest znakiem '+' a następnie zadana z klawiatury kolumna wypełniana jest znakiem '-'.Tablica wyprowadzana jest na ekran.
Opracuj kod programu w którym tablica 3x20 wypełniana jest w pętli w pierwszej kolumnie kolejnymi liczbami naturalnymi zaś w dwóch pozostałych kolumnach kwadratami sześcianami tych liczb. Tablica wyprowadzana jest na ekran.
Opracuj kod programu w którym tablica 3-elementowa wypełniana jest w pętli rekordami o organizacji Student(nazwisko, ocena). Tablica wyprowadzana jest na ekran.
Opracuj kod programu w którym tablica 10x10 wypełniana jest w pętli dowolnymi liczbami np. kolejnymi całkowitymi parzystymi kolumnowo. Wyprowadź utworzoną tablicę oraz jej tablicę transponowaną (zamienione wiersze z kolumnami) na ekran.
Opracuj kod programu w którym tablica 10x10 wypełniana jest w pętli zerami, zaś jej przekątna główna jedynkami. Tablica wyprowadzana jest na ekran.
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
Opracuj kod programu w którym tablica 10x10 wypełniana jest w pętli zerami, jej przekątna główna jedynkami zaś przeciwprzekątna dwójkami. Tablica wyprowadzana jest na ekran.
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
2 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
2 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
2 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
2 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
2 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
2 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
2 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
2 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
2 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
2 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
Opracuj kod programu z tablicą 11x2 jako tabelą funkcji dla x [0 do 1] z krokiem 0.1 w pierwszej kolumnie oraz wartościami funkcji y=ex w drugiej kolumnie. Tablica jest wyprowadzana na ekran.
Opracuj kod programu w którym tablica o rozmiarze 10x10 zostanie wypełniona zerami oprócz elementów dla których obydwa indeksy są parzyste - te element będą jedynkami. Tablica jest wyprowadzana na ekran.
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
Opracuj kod programu w którym tablica 10x10 wypełniana jest jedynkami dla elementów, których suma podzielna jest przez 3, w przeciwnym razie element jest zerem. Tablica wyprowadzana jest na ekran.
Opracuj kod programu w którym tablica 10x10 wypełniana jest zerami oprócz przeciwprzekątnej wypełnionej jedynkami. Tablica wyprowadzana jest na ekran.
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
Opracuj kod programu w którym tablica 10x10 wypełniona jest zerami oprócz centralnego kwadratu tej macierzy o rozmiarze 4x4, który zawierał będzie jedynki. Tablica wyprowadzana jest na ekran.
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
Zbadaj funkcję sin(x) dl3a x od zera do 2 radianów z krokiem 0.1 radiana. W jednowymiarowej, 21-elementowej tablicy umieść znak '+' dla dodatniej, a znak '-' dla ujemnej wartości tej funkcji. Tablica wyprowadzana jest na ekran.
Opracuj kod programu, w którym wektor o rozmiarze 20 wypełniany jest w pętli kolejnymi liczbami naturalnymi lecz jeśli liczba jest podzielna przez 0 to wstawiane jest zero. Wyprowadź tablicę na ekran.
1 |
2 |
0 |
4 |
5 |
0 |
7 |
.. |
|
Opracuj kod programu w którym w pętli wprowadzane są dowolne liczby. Po wprowadzeniu każdej liczby ukazuje się komunikat czy liczba jest dodatnia czy ujemna. Pętla i program kończy się gdy wprowadzana liczba jest równa zeru.
Opracuj kod programu w którym w pętli wprowadzane są dowolne liczby. Po wprowadzeniu każdej liczby ukazuje się komunikat która z nich jest większa. Pętla i program kończy się gdy wprowadzana liczba jest równa zeru.
Opracuj kod programu, w którym zdefiniowana jest zmienna rekordowa o postaci (Pracownik, pensja, liczba lat pracy). Program umieszcza wczytywane dane w małej tablicy (np. 3-elementowej), a następnie wyświetla sumę pensji wszystkich pracowników.
Opracuj kod programu, w którym w tablicy o rozmiarze 10x10 pierwsza kolumna wypełniona jest samymi jedynkami zaś reszta zerami. Program drukuje tablicę na ekranie.
Opracuj kod programu, w którym zdefiniowana jest zmienna rekordowa o postaci (Marka samochodu, przebieg). Program umieszcza wczytywane dane w małej tablicy (np. 5-elementowej), a następnie wyświetla średni przebieg wszystkich aut.
Opracuj kod programu, w którym zadawane są trzy liczby, a następnie drukowane są w porządku rosnącym i malejącym.
Opracuj kod programu, w którym zadawanych jest 10 liczb umieszczanych w wektorze. Następnie obliczana i wyświetlana jest średnia tych liczb.
Opracuj kod programu, w którym w tablicy 5x5 umieszczone są znaki. Wypełnić tablicę znakiem gwiazdki '*' dla elementów, których suma indeksów jest parzysta, pozostałe wypełnić znakiem spacji. Wyświetlić tablicę na ekranie.
Napisać blok definicji funkcji o Napisać blok definicji funkcji o działaniu:
dla x < 0 : f(x)=0
dla x >= 0 : f(x)=1
Napisać blok definicji funkcji dwóch liczb rzeczywistych, której celem jest znalezienie i wyprowadzenie mniejszej z nich.
Napisać blok definicji funkcji logicznej , której argumentem jest liczba całkowita, a celem wyprowadzenie wartości prawda gdy liczba ta jest nieujemna i wartości fałsz w przeciwnym przypadku.
Napisać blok definicji funkcji, której argumentami są dwie liczby całkowite, funkcja wyprowadza wartość 1 gdy iloczyn tych liczb jest nieujemny zaś 0 gdy jest ujemny (sprawdzenie przeciwnych znaków liczb).
Napisać blok definicji funkcji, której argumentem jest zmienna rekordowa o strukturze (nazwisko, imie); funkcja wyprowadza liczbę znaków w nazwisku (długość łańcucha).
Opracuj kod programu, w którym dla dwóch rekordów o organizacji Czas(minuty, sekundy) zdefiniowana funkcja oblicza różnicę czasu i wyświetla wynik na ekran.
Opracuj kod programu, w którym 3-elementowa tablica rekordów o organizacji Auto(marka, rocznik) zapisywana jest do dyskowego pliku elementowego.
Opracuj kod programu, w którym podawane z klawiatury rekordy o organizacji Auto(marka, rocznik, Silnik(pojemność, moc)), gdzie Silnik jest polem typu rekordowego, zapisywane są do dyskowego pliku elementowego. Ograniczeniem wprowadzania danych jest podanie pustego łańcucha w polu marka.
Napisać program, w którym podawane z klawiatury liczby rzeczywiste aż do podania liczby 0 umieszczane są w tablicy. Następnie zdefiniowana procedura oblicza średnią arytmetyczną elementów tablicy. Program główny wykorzystuje tę procedurę i drukuje wartość średniej.
Opracuj kod programu, w którym wykorzystywana jest procedura służąca do dzielenia dwóch liczb zespolonych o organizacji rekordowej Zesp ( re , im ).
Utworzyć na dysku sieciowym przy pomocy dowolnego edytora ASCII plik tekstowy zawierający 3 do 4 wierszy tekstu. Napisać program wykorzystujący procedurę odczytu utworzonego pliku i umieszczającego wiersze tekstu w tablicy, a następnie drukującego wiersze tekstu w porządku odwrotnym.
Utworzyć na dysku sieciowym przy pomocy dowolnego edytora ASCII plik tekstowy zawierający dane kilku osób o organizacji Student(nazwisko, ocena1, ocena2, ocena3). Napisać program wykorzystujący procedurę odczytu utworzonego pliku, obliczającą i drukującą średnią ocen każdego studenta i średnią grupy.
Zdefiniować procedurę wyprowadzającą na ekran kolejnymi wartościami wyrażenia y = |2x − 5|. Parametrami wywołania są: wartości krańcowe przedziału p1 i p2 oraz krok dp. Wykorzystać procedurę w programie.
Zdefiniować procedurę obliczającą pole prostokąta. Do procedury przekazywać przez wartość wymiary boków, a przez zmienną wyprowadzać pole powierzchni i drukować na ekranie. Zadawanie danych, wykorzystanie procedury i wydruk wyniku umieścić w pętli kończącej się w przypadku podania wartości ujemnej jako wymiaru pierwszego boku.
Napisać program wczytujący dwie daty o typie rekordowym o organizacji Data (dzień, miesiąc, rok), a następnie przekazać te daty do własnej procedury drukującej na ekranie obie daty w kolejności czasowej.
Utworzyć na dysku przy pomocy dowolnego edytora ASCII plik tekstowy zawierający 20 dowolnych liczb, każda w nowym wierszu.. Napisać program wykorzystujący procedurę odczytu utworzonego pliku, obliczającą i drukującą maksymalną z liczb w pliku.
Napisać program drukujący sumę N liczb naturalnych wykorzystując funkcję sumowania rekurencyjnego n+ (n-1) .... aż do 1.
Utworzyć na dysku przy pomocy dowolnego edytora ASCII plik tekstowy zawierający dane kilku osób o organizacji Student(nazwisko, ocena). Napisać program wykorzystujący procedurę odczytu utworzonego pliku, drukującą jedynie studentów z oceną nie mniejszą niż 4.0.
Utworzyć na dysku przy pomocy dowolnego edytora ASCII plik tekstowy zawierający 10 par dowolnych liczb całkowitych, każda para w nowym wierszu.. Napisać program wykorzystujący procedurę odczytu liczb pliku, umieszczającą je w tablicy [10,2] i drukującą sumy par i sumy kolumn.
Napisać program tworzący na dysku sieciowym F elementowy plik trzech wektorów o rozmiarze 5 zawierających dowolne, wczytywane z klawiatury liczby naturalne. Następnie otworzyć plik do odczytu i wydrukować na ekranie drugą z trzech tablic.
Zdefiniować procedurę drukującą tabelę wartości wyrażenia y = x2 + x −5 z przekazywanymi do procedury przez wartość końcami przedziału i krokiem wydruku. Wykorzystać procedurę w programie głównym w dwóch wywołaniach.
Zdefiniować procedurę obliczającą objętość prostopadłościanu. Do procedury przekazywać przez wartość wymiary boków, a przez zmienną wyprowadzać objętość i drukować na ekranie. Zadawanie danych, wykorzystanie procedury i wydruk wyniku umieścić w pętli kończącej się w przypadku podania wartości ujemnej jako wymiaru pierwszego boku.
Grafika w TurboPascalu
Procedury organizacyjne
Turbo Pascal umożliwia tworzenie grafiki ekranowej. Jednostką obrazu jest pixel- punkt świetlny, sieć pixeli tworzy układ współrzędnych o początku w lewym, górnym rogu ekranu.
W celu inicjacji trybu graficznego należy:
- dokonać przyłączenia modułu procedur i funkcji graficznych graph w deklaracji:
uses graph;
- wykonać procedurę:
initgraph (sterownik, tryb, katalog_sterownika);
nie zapominając o zadeklarowaniu obu zmiennych całkowitych sterownik i tryb.
Zamknięcie trybu graficznego dokonuje się przy użyciu bezparametrowej procedury:
closegraph;
Funkcje i procedury ustalające parametry graficzne
Poniższe funkcje bezparametrowe podają rozmiar ekranu w pixelach:
Hercules |
VGA |
||
getmaxx |
719 |
639 |
799 |
getmaxy |
347 |
479 |
599 |
Poniższe procedury służą do ustalenia parametrów graficznych:
SetColor (kolor); wybór koloru rysowania,
SetBkColor (kolor); wybór koloru tła,
SetLineStyle (rodzaj , wzór , grubość); wybór stylu linii,
SetFillStyle (wzór_wypełnienia , kolor); wybór stylu wypełniania,
kolor 0 - czarny, 15 - biały
rodzaj 0 do 3 (4 - definicja własna)
grubość 1 (normalna) lub 3 (pogrubiona)
wzór istotny gdy rodzaj = 4
wzór wypełnienia 0 do 11
SetTextStyle (krój, kierunek, rozmiar); {wybór stylu pisma},
krój 0 do 10,
kierunek 0 - poziomo, 1 - pionowo,
rozmiar 1 do 10.
W niektórych trybach możliwe jest tworzenie grafiki na więcej niż jednej stronie. Wybór strony do rysowania dokonuje się przez procedurę:
Setactivepage(nr_strony);
zaś strony widocznej na ekranie przez:
Setvisualpage (nr_strony);
Odpowiednie stosowanie przełączania stron umożliwia uzyskanie efektów animacyjnych (ruch).
Procedury graficzne
Poniższe procedury służą do wyprowadzania na ekran elementów geometrycznych i tekstu. Główne parametry podajemy w nawiasach przy wykonaniu procedury, inne określone są przez wcześniej wymienione procedury ustalające parametry graficzne (ich brak powoduje przyjęcie wartości domyślnych).
Outtextxy(x, y, łańcuch_tekstu); |
wyprowadza tekst od punktu x, y |
Line (x1, y1, x2, y2) ; |
odcinek prostej - podajemy współrzędne końców |
PutPixel (x, y, kolor); |
punkt |
Rectangle (x1, y1, x2, y2); |
prostokąt - współrzędne lewego, górnego i prawego dolnego wierzchołka |
Circle (x, y, r); |
okrąg - współrzędne środka i promień |
Ellipse (x, y, , , rx, ry); |
łuk elipsy - współrzędne środka, kąt początkowy i końcowy zatoczenia łuku, promienie osi |
Bar (x1, y1, x2, y2); |
prostokąt wypełniony aktualnym wzorem wypełnienia, określonym przez procedurę Setfillstyle |
PieSlice (x, y, , , r); |
wycinek koła, wypełniany aktualnym wzorem |
FillEllipse (x, y, rx, ry); |
elipsa wypełniona, |
Floodfill (x, y, kolor_brzegu); |
wypełnienie konturu zamkniętego, |
Parametrami wywołania procedur mogą być:
- zmienne (zadeklarowane) o wcześniej ustalonych wartościach,
- stałe typu Word (całkowite, dodatnie),
- zdefiniowane stałe języka, np.Black - czarny, Dottedln - linia kropkowana, Thickwidth - linia pogrubiona itp.,
- wyrażenia.
Powtarzalne wzory rysunku uzyskać można umieszczając procedury rysujące wewnątrz iteracji oraz uzależnić współrzędne bądź parametry od odpowiednich wyrażeń arytmetycznych typu całkowitego.
Przykład 1.
program balwan;
uses crt , graph;
var s, t, x, y: integer;
begin
initgraph(s, t, 'c:\tp\bgi') ; {inicjacja trybu graficznego}
x := getmaxx div 2; {wyznaczenie współrzędnych środka ekranu}
y := getmaxy div 2;
pieslice(x, y+90, 0, 360, 90); {brzuch}
pieslice(x, y-50, 0, 360, 50); {głowa}
bar(x-70, y-95, x+70, y- 80); {kapelusz}
pieslice ( x, y-100, 0, 360, 45);
fillellipse(x-110 , y+90, 20, 40); {kończyny}
fillellipse(x +110, y+90, 20, 40);
fillellipse(x-70, y+180, 40, 20);
fillellipse(x+70, y+180, 40, 20);
while not keypressed do; {zatrzymanie obrazu}
closegraph;
end .
Przykład 2.
...
outtextxy (5, 5, 'Nacisnij litere a lub b');
z := readkey;
case z of
'a': for i:=0 to 4 do
for j:=0 to 4 do pieslice (50+i101, 20+j101, 0, 360, 30);
'b': ...{inna akcja}
end ;
...
5. Przykłady testu egzaminacyjnego z rozwiązaniem
[4 p] Podać "pascalową" postać następującego wyrażenia:
Rozwiązanie: (sin(x)*sin(x)+2.5)/(sqrt(ln(x)+2) -1)
lub : (sqr (sin(x))+2.5) / (sqrt(ln(x)+2) -1)
[4 p] Dla tablicy zadeklarowanej jako:
var w : array [ 0..5 ] of char ; k: 0..5;
wypisz jakie wartości przyjmują elementy tablicy po wykonaniu instrukcji:
for k := 0 to 5 do w [k] := chr(ord ( 'k' ) + k);
k |
l |
m |
n |
o |
p |
[10 p] Przy założeniu następujących deklaracji:
type wek = array [ 1 .. 10, 1 .. 10 ] of real ;
rek = record
nazwisko : string [15] ;
wiek : 0 .. 120 ;
end;
var i : integer ;
liczba : real ;
zn : char ;
czy : boolean ;
osoba : rek ;
tab : wek ;
zestaw : set of 1 .. 20 ;
x : file of rek;
procedure pisz (y:rek);
begin
writeln(y.nazwisko);
writeln(y.wiek)
end;
opisz błędy w następujących instrukcjach lub zaproponuj zbliżoną, poprawną ich postać:
a) wek [ 1, 1 ] := 5 ;
wek jest identyfikatorem typu a nie zmiennej
b) assign (x , c:\dane.bin) ;
brak apostrofów 'c:\dane.bin'
c) writeln ( ' Wiek =' , wiek ) ;
powinno być writeln ( ' Wiek =' , osoba.wiek);
d) for i := 1 to 10 do tab[ 1, i ] = i ;
brak dwukropka (instrukcja przypisania)
e) zestaw := 1;
zestaw jest typu zbiorowego, przypisanie zestaw:=[1]
f) writeln (x, osoba.nazwisko, osoba.wiek) ;
do pliku zapisujemy rekord w całości - writeln (x, osoba) ;
g) osoba.nazwisko := Kowalski;
brak apostrofów w stałej łańcuchowej 'Kowalski'
h) if osoba.wiek > 100 then pisz ;
brak parametru if osoba.wiek > 100 then pisz(osoba) ;
i) czy := nazwisko>'K' ;
powinno być np. czy := osoba.nazwisko>'K' ;
j) read (zn:1) ;
czytanie nie może być formatowane
[5 p] Zbadaj jak zmieniają się wartości zmiennych a, b, c w trakcie wykonywania poniższego programu, wypełnij tabelkę dla każdej zmiany i określ ostateczne wartości wydrukowane na ekranie:
program p11;
var a,b,c:integer;
begin
a:=1;
b:=1;
while not (a>=20) do
begin
for c:=2 downto 1 do b:=b+c;
a:=2*b;
end;
end.
krok |
a |
b |
c |
|
krok |
a |
b |
c |
1 |
1 |
1 |
2 |
|
11 |
|
|
2 |
2 |
|
3 |
|
|
12 |
|
9 |
|
3 |
|
|
1 |
|
13 |
|
|
1 |
4 |
|
4 |
|
|
14 |
|
10 |
|
5 |
8 |
|
|
|
15 |
20 |
|
|
6 |
|
|
2 |
|
16 |
|
|
|
7 |
|
6 |
|
|
17 |
|
|
|
8 |
|
|
1 |
|
18 |
|
|
|
9 |
|
7 |
|
|
19 |
|
|
|
10 |
14 |
|
|
|
20 |
|
|
|
[7 p] Napisać tekst programu którego zadaniem będzie znalezienie najwcześniejszego w alfabecie napisu w 3-elementowym wektorze z dowolnymi łańcuchami tekstowymi.
program egzamin;
uses crt;
var w:array [1..3] of string;
pierwszy: string;
begin
clrscr;
readln(w[ 1] );
readln(w[ 2] );
readln(w[ 3] );
pierwszy :=w[ 1];
for k:=2 to 3 do
if w[k]<pierwszy then pierwszy := w[k];
writeln(pierwszy);
repeat until keypressed
end.
Literatura
1. Marciniak A.: „Turbo Pascal 7.0” cz I., wyd. NAKOM Poznań, 1994
2. Struzińska-Walczak A., Walczak K.: „Programowanie w języku Turbo Pascal 7.0”, wyd. W&W.
3. Gliza D., Krasowska E.: „Turbo Pascal w zadaniach z komentarzem”, wyd. MIKOM.
4. Struzińska-Walczak A., Walczak K.: „Nauka programowania dla początkujących”, wyd. W&W.
5. Struzińska-Walczak A., Walczak K.: „Programowanie w języku Tubo Pascal”, wyd. W&W.
6. Arciszewski W.: „Ćwiczenia z Turbo Pascala”, wyd. MIKOM.
7. Porębski W.: „Pascal - wprowadzenie do programowania”, HELP - Warszawa 1996.
8. Jasiak Z.: „Elementy informatyki - Pascal”, wyd. Koncept, Warszawa 1995.
9. Wołek S.: „Podstawy programowania”, skrypt WSiZ, Rzeszów
46 10. Podprogramy - funkcje
10. Podprogramy - funkcje 47
48 11. Podprogramy - procedury
11. Podprogramy - procedury 49
54 12. Pliki
12. Pliki 53
56 13. Algorytmy rekurencyjne
13. Algorytmy rekurencyjne 55
58 14. Moduły
14. Moduły 59
60 15. Typ zbiorowy
15. Typ zbiorowy 61
76 DODATEK
DODATEK 77
Output:
0.0000000000
1.0000000000
Typ wskaźnikowy
WYJĄTEK:
Użycie typu, który jest definiowany później
Zmienne tego typu wskazują na adres w pamięci
Pom1^.kod to zawartość pola rekordu wskazywanego przez zmienną pom1
Przepisanie wskazania do pomocniczej zmiennej
Przewijanie listy do końca NIL
Dopisanie na końcu
procedure D
deklaracje D
begin
end
procedure C
deklaracje C
begin
end
procedure B
deklaracje B
begin
end
program A
deklaracje A
begin
end.