Основні принципи модульного програмування та стеки

Якщо в деякій програмі використовуються власні процедури досить великого розміру, то ці процедури найкраще оформити в вигляді окремого модуля. Існує декілька причин для створення такого модуля. По-перше, модуль може зберігатися як в вихідному виді в PAS-файлі, так і в відкомпільованому файлі з розширенням TPU (Turbo Pascal Unit - модуль ТУРБО ПАСКАЛЬ). Усі процедури не компілюються щораз при перекомпіляції основної програми, а просто їхній код, що міститься в tpu-файлі, компонується з кодом основної програми. Це значно заощаджує час загальної компіляції задачі. По-друге, винесення множини процедур в окремий модуль розвантажує текст основної програми від зайвої захаращеності, робить його більш компактним і зрозумілим для сприйняття. По-третє, модулі з процедурами, що часто зустрічаються в різноманітних програмах, заощаджують час при написанні нових програм. Для цього достатньо помістити потрібний tpu-файл в каталог із новим проектом, в основній програмі підключити цей модуль в рядку uses і далі просто використовувати процедури з цього модуля в тексті основної програми. Будова модуля нагадує створення нової програми. в окремому pas-файлі записується заголовок модуля, константи, змінні, процедури і функції, які використовуються в цьому модулеві:

Unit ім'я_модуля; {це ж ім'я повинно бути іменем файла} Interface {розділ описів}

Const глобальні_константи;

Var глобальні_змінні;

Procedure Ім’я1 (параметри);

Procedure Ім’я2 (параметри); ..

.Procedure Ім’яM (параметри);

Function Ім’я11 (параметри): тип; ...

Function Имя1N (параметри): тип;

Implementation {розділ реалізації};

Const локальні_константи;

Var локальні_змінні;

Procedure Ім’я1; ...

Begin ...End;

Procedure Ім’я2; ...

Begin ...End; ...

Function Ім’я11; ...

Begin ...End; ...

End.

Модуль не виконується як звичайна програма, а компілюється в tpu-файл. Інтерфейсна частина модуля, яка фактично відображає його зміст, компілюється особливо, що дозволяє компілятору надалі дуже швидко переглядати tpu-файли і знаходити потрібні процедури.

Завдання. Створити модуль з набором процедур, що реалізують різноманітні методи сортування числових масивів. Налагодити модуль для використання в наступній лабораторній роботі.

3.1. Приклад програми

unit mmm;

interface

procedure str(var a:array of integer);

procedure sts(var a:array of integer);

procedure bub(var a:array of integer);

procedure qui(var a:array of integer);

implementation

procedure str;

var

x,i,j:integer;

n1,n:integer;

begin

n:=high(a);

n1:=low(a);

for i:=n1 to n do

begin

x:=a[i];

j:=i-1;

while (j>=n1) and (a[j]>x) do

begin

a[j+1]:=a[j];

j:=j-1;

end;

a[j+1]:=x;

end;

end;

procedure sts;

var

x,k,i,j:integer;

n1,n:integer;

begin

n:=high(a);

n1:=low(a);

for i:=n1 to n-1 do

begin

k:=i;

for j:=i+1 to n do

if a[k]>a[j] then k:=j;

if k<>i then

begin

x:=a[i];a[i]:=a[k];a[k]:=x;

end;

end;

end;

procedure bub;

var

x,i,j:integer;

n1,n:integer;

begin

n:=high(a);

n1:=low(a);

for i:=n1 to n-1 do

for j:=n downto i+1 do

if a[j-1]>a[j] then

begin

x:=a[j];a[j]:=a[j-1];

a[j-1]:=x;

end;

end;

procedure qui;

var n1,n:integer;

procedure sort(l,r:integer);

var

x,w,i,j:integer;

begin

i:=l;j:=r;

x:=a[(i+j) div 2];

repeat

while a[i]<x do i:=i+1;

while a[i]>x do j:=j-1;

if i<=j then

begin

w:=a[i];

a[i]:=a[j];

a[j]:=w;

i:=i+1;

j:=j-1;

end;

until i>j;

if i<r then sort(i,r);

if j>l then sort(l,j)

end;

begin

n:=high(a);

n1:=low(a);

sort(n1,n);

end;

end.

program test;

uses mmm;

const n=100;

var a:array[1..n] of integer;

i:integer;

begin randomize;

for i:=1 to n do

a[i]:=random(100);

writeln('неупорядкований масив:');

for i:=1 to n do

write(a[i]:4);

writeln;

sts(a);

writeln('упорядкований масив :');

for i:=1 to n do

write(a[i]:4);

writeln;

end.

Реалізація роботи з графічними елементами на принципах модульного програмування

Для реалізації графічних зображень використовується графічний режим. Для ініціалізації графічного режиму необхідно підключити модуль graph.tpu за допомогою оператора USES GRAPH; та викликати процедуру ініціалізації графічного режиму:

initgraph(ім'я драйвера, режим, шлях до драйвера);

Можливе автоматичне визначення відеоадаптера. Для цього на місці першого параметра треба вказати ім'я Detect:

INITGRAPH(Detect,Regim,”);

Після роботи в графічному режимі його закривають процедурою closegraph.

Перелік процедур та функцій роботи з графічними елементами надана в додатку.

Приклад програми

Умова задачі: Дослідити область визначення і побудувати графік функції Y=3+2/X+3/X2

Алгоритм

1. Ініціалізація графічного режиму

2. Відображення координатних осей, асимптот, надписів.

3. Відображення графіка.

3.1. Перебір точок з абсцисами від лівого до правого кінця екрана

3.2. Визначення масштабу відображення точки на екрані

3.3. Визначення ординати точки

4. Кінець

uses crt,graph; { Підключення модуля GRAPH }

var grDriver,grMode:integer; {Змінні застосовуються процедурою

InitGraph}

x,y:real; { Змінні умови }

i:integer; { Змінна циклу }

function f(x:real):real; {функція , що досліджується}

begin

f:=3+2/x+3/sqr(x);

end;

procedure koordinate; {зображення координатних осей}

begin

setcolor(5); {колір осей червоний}

setbkcolor(15); {колір фону білий}

Line(320,0,320,350); {координатна вісь Х }

Line(0,300,640.300); {координатна вісь Y}

Line(318,10,320.0); {стрілка на осі Y }

Line(322,10,320.0): {стрілка на осі Y }

Line(630,298,640,300); {стрілка на осі Х }

Line(630,302,б40,300); {стрілка на осі Х }

SetLineStyle(DashedLn,0,1); { штрихова лінія }

Line(0.240,640,240); {асимптота графіка}

OutTextXY(310,305,'0'); {надписи на осях}

OutTextXY(310,5,'Y');OutTextXY(630.305.'X');

end;

begin {---------------------головна програма---------------------}

grDriver:=Detect; {визначення номера драйвера }

lnrtGraph(grDriver.grMode,'c:\tp7\bgi');{ініціалізація графічного

режиму}

koordinate; {зображення координатних осей}

for i:=-320 to 320 do begin {зображення графіка}

x:=0.05 *i; { визначити значення абсциси }

{масштаб представлення графіка в системі координат хОу дорівнює

20}

if x<>0 then { область визначення }

begin

y:=f(x); {визначити значення ординати}

PutPixel(round(320+20*x),round(300-20*y),1);

{зобразити піксель в заданих }

end; {координатах синім кольором}

end;

readin; closegraph; { закриття режиму графіки }

end.

ЧЕРГИ ТА СТЕКИ

Змінні можуть бути статичними і динамічними. Для статичних змінних пам'ять виділяється компілятором на весь термін дії програми і звільнюється після закінчення програми. Для динамічних змінних пам'ять надається на момент звернення до динамічної змінної під час виконання програми. Доступ до динамічних змінних здійснюється за адресою, де зберігається змінна. Адреса динамічної змінної визначається за допомогою покажчиків на конкретний тип даних. Опис покажчика на конкретний тип має вид:

var ім'я_змінної : ^ідентифікатор типу;

Наприклад:

type point=^data;

data=record

r: integer;

next: point;

end;

var a:point;

Змінна А є покажчиком на структуру типу DATA. При виконанні програми змінна А набуває значення адреси даних типу DATA. Визначення змінної типу покажчик відбувається при виконанні стандартної процедури NЕW(покажчик). Система знаходить вільну область пам'яті, в якій може розміститися вказаний тип даних. Початкове значення покажчика задається константою NIL (A^.NEXT:=NIL;).

За допомогою процедури DISPOSE (ідентифікатор_типу_покажчик) звільнюється пам'ять, зайнята даними, на які вказує покажчик.

Значення, що знаходиться за адресою, на яку вказує покажчик, визначається за форматом ім'я_змінної^, де змінна описана типом покажчик.

У мові Паскаль також можна використовувати нетипізовані покажчики, які, на відміну від типізованих, не зв'язані з конкретним типом. Такі покажчики мають тип POINTER, наприклад:

VAR P1,P2:POINTER;

Ці покажчики сумісні із будь-якими типізованими покажчиками. Процедура GETMEM(P1,Size) виділить пам'ять розміром Size байтів, покажчик Р1 матиме значення адреси початку такої пам'яті (обмежена числом 64К). Значення Size визначається функцією sіzеоf(тип_даних). Процедура FREEMEM(P1,Size) звільняє пам'ять. Покажчику можна присвоїти значення NIL, що визначає значення "пусто".

За допомогою динамічних змінних створюються динамічні структури даних: черги, стеки, списки. Черга - це структура, в якій елемент, що першим прийшов, першим обробляється (first in, first out). Стек - це структура, в якій елемент, що першим прийшов, обробляється останнім (last in , first out).

Алгоритм побудови стека:

1. Визначити покажчик на перший елемент firsr:=nil;

2. Виділити пам'ять під новий елемент new(curr).

3. Занести значення даних за адресою покажчика, наприклад, readln(curr^.data).

4. Зв'язати перший елемент із поточним curr^.next:=first;

5. Покажчик на перший елемент перепризначити на поточний first:=curr;. Повторити дії від пункту 2 до тих пір, поки не закінчаться всі елементи.

Алгоритм побудови черги

1. Виділити пам'ять під поточний елемент черги new(curr).

2. Запам'ятати покажчик на перший елемент first:=curr;

3. Запам'ятати покажчик на попередній елемент prev:=curr;

4. Ввести дані в пам'ять: readln(curr^.data)

5. Встановити покажчик на наступний елемент сигг^.nехt:=nіІ;

6. Зв'язати поточний елемент із попереднім, перепризначивши покажчик prev^.next :=curr;

7. Виділити пам'ять під новий елемент та повторити дії пп.3-7.

7.1.2. Приклад програми. Умова задачі.

Побудувати список з одночасним впорядкування елементів списку в порядку зростання. Виконати операції додавання, вилучення, друкування елементів списку.

Алгоритм

1. Список порожній.

2. Повторювати дії, поки не натиснута клавіша 'Q':

2.1. Виведення списку на екран.

2.2. Додавання елементів до списку і їх сортування:

2.2.1. Виділити пам'ять для нового елемента;

2.2.2. Ввести елемент в пам'ять;

2.2.3. Якщо список порожній, то перепризначити покажчик на новий елемент, покажчику на наступний елемент присвоїти значення nil;

2.2.4. Якщо список не порожній, то, якщо нове значення елемента менше першого, розмістити елемент на початку списку;

2.2.5. Інакше пошук місця вставки нового елемента в списку елементів.

2.3. Вилучення заданого елемента:

2.3.1. Якщо список не порожній, то - ведення елемента, що вилучається;

2.3.2. Якщо потрібний елемент знайдений, то перепризначити покажчики; в противному різі виконувати пошук потрібного елемента, вилучення його і звільнення пам'яті.

3. Кінець.

Приклад програми

Умова задачі: Організувати список із операціями впорядкування елементів, вилучення елементів із списку, друкування списку.

uses crt;

type

ptr=^zap; {тип покажчик на запис}

zap=record

number:string; {елемент даних}

next:ptr; {покажчик на наступний елемент}

end;

var first: ptr; {покажчик на початок списку }

ch: char; { вибiр режиму обробки списку}

newptr, {покажчик на введений елемент}

curr, {покажчик на наступний елемент}

prev: ptr; {покажчик на попереднiй елемент}

reg: string; {значення елемента даних }

done: boolean; {прапорець для пошуку мiсця вставки елемента}

{додавання елементiв до списку }

procedure insert;

begin

write('input elenent:');

readln(reg);

new(newptr); { видiлення пам'ятi пiд новий елемент}

newptr^.number:=reg; { ввiд даних в пам'ять}

if first=nil then { формування першого елемента списку}

begin

first:=newptr; {початковий покажчик на першому елементi}

first^.next:=nil; { покажчик на наступний елемент }

end

else {якщо список не порожнiй

перевiрка впорядкованостi списку}

if reg<=first^. number then {якщо нове значення менше iснуючого}

{вставка нового елемента на початку списку}

begin newptr^.next:=first;

first:=newptr;

end

else {якщо нове значення бiльше iснуючих}

{пошук потрiбного мiсця вставки }

begin

curr:=first; {покажчик поточного елемента вказує на перший}

repeat

prev:=curr; {покажчик попереднього елемента вказує на

поточний елемент}

curr:=curr^.next; {покажчик поточного елемента вказує на

наступний елемент}

if curr=nil

then done:=true

else done:=curr^.number>=reg;

until done; {переадресацiя пари покажчикiв до тих пiр, поки значення

даних, що введене, бiльше за тих, що є в списку: until curr^.number>=reg;}

{вставка нового елемента }

prev^.next:=newptr;

newptr^.next:=curr;

end

end; { кiнець процедури insert}

{вилучення елемента iз списку, перший елемент вилучається останнiм}

procedure delete;

begin

if first=nil then {якщо список порожнiй}

begin

writeln('List is empty.Press ENTER...');

readln;

end

else {якщо список не порожнiй}

begin

write('input element:');

readln(reg); {ввiд значення, що вилучається }

curr:= first;

if curr^.number=reg then {якщо потрiбний елемент знайдений}

first:=curr^.next {вилучення елемента }

else

repeat {пошук потрiбного елемента }

prev:=curr; {перепризначення покажчикiв }

curr:=curr^.next; {покажчик на попереднiй елемент стає

поточним покажчиком, покажчик на

поточний елемент вказує на наступний}

until (curr^.number=reg) or (curr^.next=nil);

{вилучення знайденого елемента та звiльнення пам'ятi}

if curr^.number=reg then {якщо елемент списку спiвпадає з заданим}

begin

prev^.next:=curr^.next; {переадресацiя покажчикiв}

dispose(curr); end {звiльнення пам'ятi}

else

begin {якщо елемент не знайдено}

writeln(reg,' not found in list. Press ENTER...'); {вивiд повiдомлення}

readln;

end;

end;

end; { кiнець процедури delete }

{ вивiд списку на екран }

procedure outlist;

begin

curr:=first; {поточний покажчик вказує на перший}

if curr=nil then writeln('List is empty')

else begin

writeln(' output list:');

repeat

write(curr^.number,' '); {вивiд значення елемента }

curr:=curr^.next; {поточним стає покажчик на наступний елемент} until curr=nil;

{вивiд значень, поки є елементи в списку}

end;

writeln;

end;

begin {**** головна програма ******}

first:=nil; {початок списку- список порожнiй }

repeat

clrscr; outlist; {очищення екрана, виведення списку}

writeln('I- input D- delete Q- quit');

write('input command:');

readln(ch);

ch:=upcase(ch);

case ch of

'I': Insert;

'D': delete;

end;

until ch='Q';

end.