Процедури та функції. Процедурні типи

Досить часто процес розв’язку складної задачі можна розбити на послідовність розв’язків більш простих задач. Якщо вважати, що програма призначена для виконання якоїсь складної задачі, то процедури та функції призначені для розв’язку окремих підзадач. 
Структура процедур та функцій подібна структурі програми. Процедури та функції мають спільну назву – підпрограми. Кожна підпрограма визначається лише один раз, а використовуватись може багаторазово. Використання підпрограм дозволяє зменшити число повторів однакових послідовностей операторів, зменшити розмір програми, а також прискорити процес програмування і спростити структуру програми. 
В програмі опис підпрограм, як правило, здійснюють у розділі опису процедур та функцій. Цей розділ повинен знаходитись до першого використання підпрограм, описаних у ньому. В програмі може бути кілька таких розділів. Зазвичай цей розділ розміщують між розділом опису змінних та розділом операторів. Функція відрізняється від процедури тим, що функція в результаті своєї роботи повертає значення, яке може бути привласнене змінною, а процедура тільки виконує задану послідовність дій і не повертає ніякого значення. 
Так наприклад стандартна процедура clrscr очищує екран монітора, а стандартна функція sin обчислює значення синусу заданого кута і повертає обчислене значення у вигляді числа, яке може привласнюватись змінною відповідного типу ( x:= sin(3.14)); 
Опис процедури має вигляд: 
procedure <ім’я процедури>(<список формальних параметрів>); 
Опис функції має вигляд: 
function <ім’я функції>(<список формальних параметрів>): <тип результату>; 
Наприклад: 
procedure out_put(x,y:integer); function factorial(a:integer):integer; 
Виклик процедури здійснюється за допомогою речення, що має вигляд: 
<ім’я процедури>(<список фактичних параметрів>); 
Виклик функції може здійснюватись одним із двох способів: 
<ім’я функції>(<список фактичних параметрів>); 
<ім’я змінної>:=<ім’я функції>(<список фактичних параметрів>); 
Перший спосіб використовується тоді, коли обчислене значення не потрібне для подальшого використання, а другий тоді, коли обчислене значення планується використовувати. 
Наприклад: 
var a,b,c:integer; 
a:=3; 
b:=5; 
… 
out_put(5,3); 
out_put(a,b) 
writeln(factorial(5)); 
a:=factorial(5); 
c:=factorial(b); 
Між формальними та фактичними параметрами повинна бути точна відповідність, тобто кількість, типи і порядок слідування фактичних параметрів повинні співпадати з кількістю типами і порядком слідування формальних параметрів у описі підпрограми. 
Як уже відзначалося, при виклиці підпрограм їм передаються параметри. Параметри можуть передаватися по значенню чи по посиланню. Основна відмінність між ними в тому, що при передачі параметрів по посиланню при закінченні підпрограми у відповідній змінній зберігається значення обчислене при виконанні процедури, а при передачі по значенню, всі зміни, що відбулися із даною змінною у підпрограмі, пропадають після її закінчення і змінна набуває значення, яке вона мала до виконання підпрограми. 
Для позначення змінної, що передається по посиланню, в описі функції перед цією змінною ставиться ключове слово var. Наприклад: 
procedure primer(x,y:integer; var b:real); 
Якщо підпрограма змінює значення формального параметра, переданого за посиланням або глобальної змінної, то кажуть, що підпрограма має побічний ефект. 
Серед операторів, що входять до складу функції обов’язково повинен бути хоча б один оператор присвоєння, в лівій частині якого стоїть ім’я даної функції. Цей оператор визначає значення, яке повертається функцією. Всі типи, що використовуються при передачі параметрів у підпрограму повинні бути попередньо описані. 
Наприклад: 
program demo; 
uses crt; 
var n, fact:integer; 
procedure print_results( n,fact:integer); 
begin 
writeln(‘factorial(n):=’, fact); 
end; 
function factorial(n:integer):integer; 
var i,f:integer; 
begin 
f:=1; 
for i:=2 to n do 
f:=f*i; 
factorial:=f; 
end; 
begin 
clrscr; 
write(‘input n=’); 
readln(n); 
fact:=factorial(n); 
print_results(n,fact); 
readkey; 
end. 
Для зручності роботи Турбо-Паскаль містить велику кількість власних (“стандартних”) процедур та функцій. 
Наприклад для виконання арифметичних обчислень з дійсними і цілими числами в Паскалі є ряд арифметичних функцій: 
 abs - обчислює модуль аргументу; 
 arctan - обчислює арктангенс аргументу; 
 cos - обчислює косинус аргументу; 
 txp - обчислює експоненту аргументу; 
 frac - обчислює дробову частину аргументу; 
 int - обчислює цілу частину аргументу; 
 ln - обчислює натуральний логарифм аргументу; 
 pi - повертає значення числа π {3.1415926535897932385}; 
 sin - обчислює синус аргументу; 
 sqr - обчислює квадрат аргументу; 
 sqrt - обчислює квадратний корінь аргументу. 
Основні стандартні процедури та функції містяться у файлах стандартних бібліотечних модулів, про які буде сказано пізніше. 
Повний перелік функцій можна знайти у файлах допомоги системи Турбо-Паскаль. 
Мова Паскаль не містить функцій для обчислення показникової функції і функції піднесення до степені. Проте для їх обчислення можуть бути використані експоненціальна і логарифмічна функції -  х в степені у =е в степені y ln( x)
Турбо-Паскаль розглядає процедури і функції як об’єкти, які можна використовувати в якості значень змінних, параметрів інших функцій і т.д. Це можливо завдяки існуванню у Турбо-Паскалі процедурних типів. 
Синтаксис визначення процедурного типу дещо схожий на синтаксис визначення процедури чи функції. 
Наприклад: 
type proc=procedure;{тип процедура без аргументів}  
arg_proc=procedure(x,y:integer);{тип процедура з двома аргументами}
math_fun=function(x:real):real;{тип функція} 
var a,b:proc;{а,в можуть приймати значення процедури без аргументів} 
c:arg_proc;{с може приймати значення процедури з двома аргументами} 
d:math_fun;{d визначається, як функція з одним параметром типу real, що повертає значення типу real } 
Після визначення процедурної змінної вона може бути зв’язана з конкретною процедурою чи функцією, яка має ідентичне визначення, тобто такі ж параметри та тип значення, яке повертається (для функції). Фактично, після присвоєння значення процедурна змінна стає начебто псевдонімом функції і її ім’я може бути використано для виклику заданої процедури.
Для роботи з процедурними змінними у програму необхідно включити директиву {$F+}.
Розглянемо приклад: 
uses crt; 
type func=function(x:real):real; {визначаємо процедурний тип func, як функцію, що приймає один аргумент типу real і повертає значення типу real } 
var fun:func; {описуємо змінну типу func } 
{$F+} {дозволяємо використання процедурних змінних} 
function sin1(x:real):real; 
begin 
sin1:=sin(x)+1; 
end; 
function cos1(x:real):real; 
begin 
 cos1:=cos(x)+1; 
end; 
begin 
 ClrScr; 
fun:=sin1; {пов’язуємо функцію sin1 з процедурною змінною fun } 
writeln(fun(pi/2)); {обчислюємо значення функції sin1 і виводимо обчислене значення на екран, використовуючи процедурну змінну fun } 
fun:=cos1; { пов’язуємо функцію cos1 з процедурною змінною fun } 
writeln(fun(pi/2)); { обчислюємо значення функції cos1 і виводимо обчислене значення на екран, використовуючи процедурну змінну fun } 
readkey; 
end. 
Як видно із наведеного прикладу, наявність механізму процедурних змінних дозволяє маніпулювати процедурами і функціями як звичайними змінними. На перший погляд це нічого не дає програмістові і лише ускладнює програму. Однак це не так. Завдяки існуванню цього механізму можна робити програми значно ефективнішими. 
Наприклад, якщо нам треба побудувати просте користувацьке меню програми, то без використання процедурних типів нам буде необхідно написати досить складний код з великою кількістю операторів if, або досить складним оператором case, які б дозволили проаналізувати вибір користувача і виконати дії відповідно до того, яку клавішу натиснув користувач. Такий код дуже громіздкий і при великій кількості можливих варіантів досить повільно працює. Використання ж процедурних типів дозволяє зробити цей код більш простим та ефективнішим: 
uses crt; 
type proc=procedure;{визначаємо процедурний тип proc як процедуру без аргументів} 
var cons_proc:array[ord('a')..ord('d')] of proc;{визначаємо масив із чотирьох елементів з номерами, що відповідають кодам символів ‘a’,’b’,’c’,’d’(див. п. 2.1) } 
c,i:integer; 
{$F+}{дозволяємо використання процедурних змінних} 
{визначаємо процедури, що будуть виконуватись при натисканні клавіш, що відповідають символам ‘a’,’b’,’c’,’d’ (відповідно a – a_set, b – b_set …)} 
procedure a_set; 
begin 
writeln('a_set'); 
writeln('press any key'); 
readkey; 
end;
procedure b_set; 
begin 
writeln('b_set'); 
writeln('press any key'); 
readkey; 
end; 
procedure c_set; 
begin 
writeln('c_set'); 
writeln('press any key'); 
readkey; 
end; 
procedure d_set; 
begin 
writeln('d_set'); 
halt; 
end; 
{основна програма} 
begin 
clrscr; 
{зв’язуємо відповідні процедурні змінні з потрібними процедурами} 
cons_proc[ord(‘a’)]:=a_set; 
cons_proc[ord(‘b’)]:=b_set; 
cons_proc[ord(‘c’)]:=c_set; 
cons_proc[ord(‘d’)]:=d_set; 
{виводимо меню на екран і виконуємо процедури меню у відповідності до натиснутої клавіші}
repeat 
clrscr; 
writeln('a-a_set'); 
writeln('b-b_set'); 
writeln('c-c_set'); 
writeln('d-quit'); 
writeln; c:=ord(readkey); if((c>=97)and(c<=100)) then cons_proc[c]; until false; 
end. 

Немає коментарів:

Дописати коментар