7. Програмування у системі Delphi з використанням підпрограм
Метою даного розділу є вивчення структур процедур та функцій, механізму взаємодії програми з підпрограмами, рекурсивного методу організації обчислень, отримання навичок програмування алгоритмів середньої складності з використанням процедур та функцій.
Дві форми підпрограм
Якщо в програмі зустрічаються фрагменти, що повторюються, то доцільно ці фрагменти оформити як підпрограму. Підпрограми є відносно самостійні фрагменти програми, оформлені особливим чином і забезпечені ім'ям. Згадування цього імені у тексті програми називається викликом підпрограми. Підпрограма зазвичай містить описи ідентифікаторів: констант, типів, змінних та вкладених у неї підпрограм. Усі ідентифікатори, описані всередині підпрограми, локалізуються у ній. У розділі опису підпрограм можуть зустрітися описи підпрограм нижчого рівня, у яких – описи інших підпрограм тощо. буд. Усі імена у межах підпрограми, де вони оголошені, мають бути унікальними. При вході в підпрограму нижчого рівня стають доступними не тільки оголошені в ній імена, а й зберігається доступ до всіх імен верхнього рівня. При взаємодії підпрограм одного рівня ієрархії набирає чинності основне правилоObject Pascal: будь-яка підпрограма перед її використанням має бути описана.
Існують дві форми підпрограм – процедури та функції. Основна функціональна відмінність процедур від функцій полягає в тому, що процедура дозволяє отримати один або більше результатів, а функція завжди лише один результат.
Використання підпрограм дозволяє зменшити обсяг пам'яті, який займає код програми в цілому. Однак час виконання програми при цьому дещо зростає, тому що з'являються тимчасовіВитрати виклик підпрограм і повернення з підпрограм.
Кожну процедуру програмісту необхідно описати у розділі описів, тобто вказати її заголовок і тіло.
Узаголовкуоголошуютьсяім'я процедуриіформальні параметри, якщо вони є.
Procedure (формальні параметри);
Procedure A (K: integer; B: real);
Тіло процедуриє локальним блоком, за структурою аналогічним до програми:
Procedure (формальні параметри);
//Розділ опису типів
Type tx = array [0..4] of real;
//Розділ опису змінних
//Розділ опису вбудованих процедур та функцій
Виклик процедуриздійснюється простою згадкою імені процедури в операторі виклику процедури. Оператор виклику процедури складається з імені (ідентифікатора) процедури та списку фактичних параметрів, відокремлених один від одного комами та укладених у круглі дужки. Список фактичних параметрів може бути відсутнім, якщо процедурі не передається жодних значень.
Формат: (параметр 1, …, параметр N);
Параметри забезпечують механізми, які дозволяють виконувати процедуру початкових даних.
Між фактичними параметрами в операторі виклику процедури та формальними параметрами в заголовку опису процедури встановлюються взаємнооднозначні відповідності в результаті їх перебору зліва направо, тобто формальних та фактичних параметрів має бути однакова кількість; порядок проходження формальних і фактичних параметрів повинен бути той самий; тип кожного фактичного параметра має збігатися з типом відповідного формального параметра.
Опис функції переважно аналогічно опису процедури. Для функції, крім того, вказується тип повертається неюрезультату. Функція складається із заголовка та тіла функції. Заголовок містить зарезервоване словоFunction, ідентифікатор (ім'я) функції, необов'язковий список формальних параметрів, укладений у круглі дужки, та тип значення, що повертається функцією:
Function Pr (x, y: integer): real;
Відмінність виклику функції від виклику процедури полягає в тому, що звернення до функції можна використовувати у відповідних виразах поряд зі змінними та константами.
Тіло функції є локальний блок, за структурою аналогічний програмі:
Function (формальні параметри): ;
Для кожної функції створюється зміннаResult, яка є синонімом імені функції. У розділі операторів повинен використовуватися хоча б один раз оператор присвоювання, у лівій частині якого записується ім'я функції або зміннаResult.
Списокформальнихпараметрів необов'язковий і може бути відсутнім. Якщо ж він є, то в ньому мають бути перераховані іменаформальнихпараметрів та їх типи, наприклад:
Procedure SB(a: real; b: integer; var c: char);
Будь-який зформальнихпараметрів підпрограми може бути абопараметром-значенням, абопараметром-змінною, абопараметром-константою.
Якщо параметри визначаються якпараметри-змінні, перед ними необхідно ставити зарезервоване словоvar, а якщо цепараметри-константи– словоconst, наприклад:
Procedure SA(var a:real; b:real; const c:string);
Тут: а -параметр-змінна;
Визначення формального параметра тим чи іншим способом істотно в основному тільки для програми, що викликає: якщо формальний параметр оголошений якпараметр-змінна, то привиклик підпрограми йому повинен відповідати фактичний параметр у вигляді змінної потрібного типу.
Якщо параметр визначено якпараметр-значение, перед викликом підпрограми це значення обчислюється, отриманий результат копіюється у тимчасову пам'ять і передається підпрограмі. Важливо врахувати, що навіть якщо як фактичнийпараметр-значеннявказано найпростіше вираз у вигляді змінної або константи, все одно підпрограмі буде передана лише копія значення змінної (константи). Будь-які можливі зміни в підпрограміпараметра-значенияне сприймаються викликаючою програмою, оскільки у разі змінюється копія фактичного параметра.
Отже,параметри-зміннівикористовуються як засіб зв'язку алгоритму, реалізованого в підпрограмі, із зовнішнім світом: за допомогою цих параметрів підпрограма може передавати результати своєї роботи програмі, що викликає. Зрозуміло, у розпорядженні програміста завжди є й інший спосіб передачі результатів - через глобальні змінні. Однак зловживання глобальними зв'язками робить програму, як правило, заплутаною, важкою в розумінні і складною у налагодженні. Відповідно до вимог хорошого стилю програмування рекомендується там, де це можливо, використовувати передачу результатів черезпараметри-змінні.
Існує ще один різновид параметрів уObject Pascal-нетипізованіпараметри. Параметр вважаєтьсянетипізованим, якщо типформального параметра-змінноїу заголовку підпрограми не вказаний, при цьому відповідний йомуфактичнийпараметр може бути змінною будь-якого типу. Зауважимо, щонетипізованимиможуть бути тількипараметри-змінні:
Нетипізованіпараметри зазвичайвикористовуються у разі, коли тип даних несуттєвий.
Типом будь-якого параметра у спискуформальнихпараметрів може бути лише стандартний або раніше оголошений тип. Якщо ми хочемо передати якийсь елемент масиву, то проблем не виникає, але якщо підпрограму передається весь масив, то слід спочатку описати його тип.
aType = array [1..10] of real;
Procedure S(var a: aType);
Оскільки короткий рядок є фактично символьним масивом, його передача у підпрограму здійснюється аналогічним чином:
InType = String [15];
Function St (S: InType): OnType;
Object Pascalпідтримує відкриті масиви, що легко вирішують проблему передачі підпрограмі одновимірних масивів змінної довжини. Відкритий масив являє собоюформальнийпараметр підпрограми, що описує базовий тип елементів масиву, але не визначає його розмірності та межі:
Procedure My (OpenArray: array of Integer);
Рекурсія- це такий спосіб організації обчислювального процесу, при якому підпрограма в ході виконання складових її операторів звертається сама до себе. Слово "рекурсія" означає "повернення".
При виконанні правильно організованої рекурсивної підпрограми здійснюється багаторазовий перехід від деякого поточного рівня організації алгоритму до нижнього рівня послідовно, доки не буде отримано тривіальне рішення поставленого завдання.
Головна вимога до рекурсивних підпрограм полягає в тому, що виклик рекурсивної підпрограми має виконуватися за умовою, яка на якомусь рівні рекурсії стане хибною.
Структура рекурсивної процедури може набувати трьох різних форм:
Форма із виконанням дій до рекурсивного виклику (звиконанням дій на рекурсивному узвозі):
if умова then Rec;
Форма із виконанням дій після рекурсивного виклику (з виконанням дій на рекурсивному поверненні).
if умова then Rec;
Форма з виконанням дій як до, так і після рекурсивного виклику (з виконанням дій як на рекурсивному спуску, так і на рекурсивному поверненні):
а) Procedure Rec;
if умова then Rec;
б) Procedure Rec;
if умова then
Структура рекурсивної функції аналогічна структурі рекурсивної процедури.
Рекурсивний виклик може бути непрямим. У цьому випадку програма звертається до себе безпосередньо, а шляхом виклику іншої підпрограми, яка містить звернення до першої. Якщо суворо дотримуватися правила, згідно з яким кожен ідентифікатор перед вживанням має бути описаний, вводиться випереджальний опис:
Procedure B (var k: Byte); Forward;
Procedure A (var c: Byte);
Усі форми рекурсивних процедур та функцій знаходять застосування практично. Глибоке розуміння рекурсивного механізму та вміння керувати ним є необхідною якістю кваліфікованого програміста.
Приклад програмування з підпрограмами

Структура програми буде включати два модулі: програму з ім'ям Progect1 і модуль з ім'ям Unit1, пов'язаний з формою Form1.
Щоб продемонструвати на навчальному прикладі особливості та відмінності у використанні процедури та функції, реалізуємо підпрограму обчислення факторіалу трьома способами: у вигляді процедури, у вигляді функції та рекурсивним методом.
1. Розробка алгоритму (рис. 7.1):
а) X – речова змінна, що є аргументом функції cos(NX);
б) N – цілочисленназмінна, що означає кількість членів ряду.
• Вихідні дані: S – речова змінна, значення якої є сума членів ряду.
а) K – цілочисленна змінна, що використовується як лічильник циклу;
б) R – речова змінна.

Мал. 7.1. Схема алгоритму основної програми
Процедура FAKTproc (рис. 7.2). Ця процедура повинна обчислити R=K! .
• Вхідні дані: K – ціла чисельна змінна.
• Вихідні дані: R – речова змінна, що є значенням K! .
• Проміжні дані: I – цілочислова змінна, що використовується як лічильник циклу.

Мал. 7.2. Схема алгоритму процедури FAKTproc
Функція FAKTfunk (рис. 7.3). Функція FAKTfunk обчислює K! .
• Вхідні дані: K – ціла чисельна змінна.
• Вихідні дані: FAKTfunk – ім'я функції речового типу.
• Проміжні дані: I – цілочислова змінна, що використовується як лічильник циклу.

Мал. 7.3. Схема алгоритму функції FAKTfunc
Функція FAKTrec (рис. 7.4). Функція FAKTrec обчислює K! рекурсивним способом.
• Вхідні дані: K – ціла чисельна змінна.
• Вихідні дані: FAKTrec – ім'я функції речового типу.