Програмування на мові Delphi. Глава 2. Основи мови Delphi. Частина 2, Різне, Програмування, статті

попередня стаття серії


Зміст



Оператори


Загальні положення


Основна частина програми мовою Delphi являє собою послідовність операторів, що виконують деяку дію над даними, оголошеними в секції опису даних. Оператори виконуються строго послідовно в тому порядку, в якому вони записані в тексті програми і відділяються один від одного крапкою з комою.

Всі оператори прийнято в залежності від їх призначення розділяти на дві групи: прості та структурні. Прості оператори не містять в собі ніяких інших операторів. До них відносяться оператори присвоювання, виклику процедури і безумовного переходу. Структурні оператори містять в собі прості або інші структурні оператори і поділяються на складовою оператор, умовні оператори та оператори повтору.

При вивченні операторів ми рекомендуємо вам звернути особливу увагу на наші рекомендації з приводу того, де який оператор треба застосовувати. Це позбавить вас від безлічі помилок в практичній роботі.

Оператор присвоювання


Оператор присвоювання (: =) Обчислює вираз, визначене в його правій частині, і присвоює результат змінної, ідентифікатор якої розташований у лівій частині. Наприклад:

X := 4;
Y := 6;
Z := (X + Y) / 2;

Для уникнення помилок присвоювання необхідно стежити, щоб тип вираження був сумісний з типом змінної. Під сумісністю типів даних розуміється можливість автоматичного перетворення значень одного типу даних у значення іншого типу даних. Наприклад, всі цілочисельні типи даних сумісні з речовими (але не навпаки!).

У загальному випадку для числових типів даних діє наступне правило: вираз з більш вузьким діапазоном можливих значень можна присвоїти змінній з більш широким діапазоном значень. Наприклад, вираз з типом даних Byte можна привласнити змінної з типом даних Integer, а вираз з типом даних Integer можна привласнити змінної з типом даних Real. У таких випадках перетворення даних з одного подання в інше виконується автоматично:

var
B: Byte;
I: Integer;
R: Real;
begin
B := 255;
I := B + 1; // I = 256
R := I + 0.1; // R = 256.1
I: = R; / / Помилка! Типи даних несумісні з присвоєння
end.

Виняток становить випадок, коли вираз належить 32-розрядному целочисленному типу даних (наприклад, Integer), а змінна – 64-розрядному целочисленному типу даних Int64. Для того, щоб на 32-розрядних процесорах сімейства x86 обчислення виразу відбувалося правильно, необхідно виконати явне перетворення одного з операндів виразу до типу даних Int64. Наступний приклад пояснює сказане:

var
I: Integer;
J: Int64;
begin
I: = MaxInt; / / I = 2147483647 (максимальне ціле)
J: = I + 1; / / J = -2147483648 (неправильно: помилка переповнення!)
J: = Int64 (I) + 1; / / J = 2147483648 (правильно: обчислення в форматі Int64)
end.


Оператор виклику процедури


Оператор виклику процедури являє собою не що інше, як ім'я стандартної або користувацької процедури. Про те, що це таке, ви дізнаєтеся трохи пізніше, а поки досить просто візуалізація. Приклади виклику процедур:

 Writeln (Hello!); / / Виклик стандартної процедури виведення даних
MyProc; / / Виклик процедури, визначеної програмістом

Складовою оператор


Складовою оператор являє собою групу з довільного числа операторів, відділених один від одного крапкою з комою і укладену в так звані операторні дужки – begin і end:

begin
<Оператор 1>;
<Оператор 2>;

<Оператор N>
end

Окремим випадком складеного оператора є тіло наступної програми:

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

var
X, Y: Integer;

begin
X := 4;
Y := 6;
Writeln(X + Y);
Writeln(Press Enter to exit…);
Readln; / / Точка з комою після цього оператора не обов'язкова
end.


Хоча символ крапки з комою служить роздільником між операторами і перед словом end може опускатися, ми рекомендуємо ставити його наприкінці кожного оператора (як у прикладі), щоб надати програмі понад гарний вигляд і уникнути потенційних помилок при наборі тексту.

Складовою оператор може знаходитися в будь-якому місці програми, де дозволений простий оператор. Він широко використовується з умовними операторами і операторами повтору.

Оператор розгалуження if


Оператор розгалуження if – Одне з найпопулярніших засобів, які змінюють природний порядок виконання операторів програми. Ось його загальний вигляд:

 if <умова> then
<Оператор 1>
else
<Оператор 2>;

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

Логіка роботи оператора if очевидна: виконати оператор 1, якщо умова істинна, і оператор 2, якщо умова помилкова. Пояснимо сказане на прикладі:

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

var
A, B, C: Integer;

begin
A := 2;
B := 8;
if A > B then
C := A
else
C := B;
Writeln(C=, C);
Writeln(Press Enter to exit…);
Readln;
end.


В даному випадку значення виразу А> В помилково, отже на екрані з'явиться повідомлення C = 8.

В оператора if існує й інша форма, в якій else відсутній:

 if <умова> then <оператор>;

Логіка роботи цього оператора if ще простіше: виконати оператор, якщо умова істинна, і пропустити оператор, якщо воно помилкове. Пояснимо сказане на прикладі:

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

var
A, B, C: Integer;

begin
A := 2;
B := 8;
C := 0;
if A > B then C := A + B;
Writeln(C=, C);
Writeln(Press Enter to exit…);
Readln;
end.


В результаті на екрані з'явиться повідомлення С = 0, оскільки вираз А> В помилково і присвоювання С: = А + В пропускається.

Один оператор if може входити до складу іншого оператора if. У такому випадку говорять про вкладеності операторів. При вкладеності операторів кожне else відповідає тому then, Яке безпосередньо йому передує. Наприклад

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

var
A: Integer;

begin
Readln(A);
if A >= 0 then
if A <= 100 then
Writeln (A потрапляє в діапазон 0 – 100.)
else
Writeln (A більше 100.)
else
Writeln (A менше 0.);
Writeln(Press Enter to exit…);
Readln;
end.


Конструкцій зі ступенем вкладеності більше 2-3 краще уникати через складність їх аналізу при налагодженні програм.

Оператор розгалуження case


Оператор розгалуження case є зручною альтернативою оператору if, Якщо необхідно зробити вибір з кінцевого числа наявних варіантів. Він складається з виразу, званого перемикачем, І альтернативних операторів, кожному з яких передує свій список допустимих значень перемикача:

 case <перемикач> of
<Список № 1 значень перемикача>: <оператор 1>;
<Список № 2 значень перемикача>: <оператор 2>;

<Список № N значень перемикача>: <оператор N>;
else <оператор N +1>
end;

Оператор case обчислює значення перемикача (який може бути заданий виразом), потім послідовно переглядає списки його допустимих значень в пошуках обчисленого значення і, якщо це значення знайдено, виконує відповідний йому оператор. Якщо перемикач не потрапляє ні в один із списків, виконується оператор, що стоїть за словом else. Якщо частина else відсутня, управління передається наступному за словом end оператору.

Перемикач повинен належати порядковому типу даних. Використовувати речові і рядкові типи як перемикача не допускається.

Список значень перемикача може складатися з довільної кількості констант і діапазонів, відділених один від одного комами. Межі діапазонів записуються двома константами через розмежувачами у вигляді двох крапок (..). Всі значення перемикача повинні бути унікальними, а діапазони не повинні перетинатися, інакше компілятор повідомить про помилку. Тип значень повинен бути сумісний з типом перемикача. Наприклад:

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

var
Day: 1..31;

begin
Readln(Day);
case Day of
20 .. 31: Writeln (День потрапляє в діапазон 20 – 31.);
1, 5 .. 10: Writeln (День потрапляє в діапазон 1, 5 – 10.);
else Writeln (День не потрапляє в задані діапазони.);
end;
Writeln(Press Enter to exit…);
Readln;
end.


Якщо значення перемикача записані в порядку зростання, то пошук необхідного оператора виконується значно швидше, тому що в цьому випадку компілятор будує оптимізований код. Враховуючи сказане, перепишемо попередній приклад:

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

var
Day: 1..31;

begin
Readln(Day);
case Day of
1, 5 .. 10: Writeln (День потрапляє в діапазон 1, 5 – 10.);
20 .. 31: Writeln (День потрапляє в діапазон 20 – 31.);
else Writeln (День не потрапляє в задані діапазони.);
end;
Writeln(Press Enter to exit…);
Readln;
end.


Оператори повтору – цикли


Алгоритм вирішення багатьох завдань вимагає багаторазового повторення одних і тих же дій. При цьому суть дій залишається колишньою, але змінюються дані. За допомогою розглянутих вище операторів важко уявити в компактному вигляді подібні дії в програмі. Для багаторазового (циклічного) виконання одних і тих же дій призначені оператори повтору (цикли). До них відносяться оператори for, while і repeat. Всі вони використовуються для організації циклів різного виду.

Будь-який оператор повтору складається з умови повтору і повторюваного оператора (тіла циклу). Тіло циклу є простим або структурний оператор. Воно виконується стільки разів, скільки наказує умова повтору. Різниця серед операторів повтору пов'язано з різними способами записи умови повтору.

Оператор повтору for


Оператор повтору for використовується в тому випадку, якщо заздалегідь відомо кількість повторень циклу. Наведемо найбільш поширену його форму:

 for <параметр циклу>: = <значення 1> to <значення 2> do
<Оператор>;

де <параметр циклу> – це змінна будь-якого порядкового типу даних (змінні речових типів даних неприпустимі); <значення 1> і <значення 2> – вирази, що визначають відповідно початкове і кінцеве значення параметра циклу (вони обчислюються тільки один раз перед початком роботи циклу); <оператор> – тіло циклу.

Оператор for забезпечує виконання тіла циклу до тих пір, поки не будуть перебрані всі значення параметра циклу від початкового до кінцевого. Після кожного повтору значення параметра циклу збільшується на одиницю. Наприклад, в результаті виконання наступної програми на екран будуть виведені всі значення параметра циклу (від 1 до 10), причому кожне значення – в окремому рядку:

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

var
I: Integer;

begin
for I := 1 to 10 do Writeln(I);
Writeln(Press Enter to exit…);
Readln;
end.


Зауважимо, що якщо початкове значення параметра циклу більше кінцевого значення, цикл не виконається жодного разу.

В якості початкового і кінцевого значень параметра циклу можуть використовуватися вираження. Вони обчислюються тільки один раз перед початком виконання оператора for. В цьому полягає важлива особливість циклу for в мові Delphi, Яку слід враховувати тим, хто має досвід програмування на мовах C / C + +.

Після виконання циклу значення параметра циклу вважається невизначеним, тому в попередньому прикладі не можна покладатися на те, що значення змінної I дорівнює 10 при виході з циклу.

Друга форма запису оператора for забезпечує перебір значень параметра циклу не за зростанням, а за спаданням:

 for <параметр циклу>: = <значення 1> downto <значення 2> do
<Оператор>;

Наприклад, в результаті виконання наступної програми на екран будуть виведені значення параметра циклу в порядку убування (від 10 до 1):

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

var
I: Integer;

begin
for I := 10 downto 1 do Writeln(I);
Writeln(Press Enter to exit…);
Readln;
end.


Якщо в такого запису оператора for початкове значення параметра циклу менше кінцевого значення, цикл не виконається жодного разу.

Оператор повтору repeat


Оператор повтору repeat використовують в тих випадках, коли тіло циклу повинно бути виконано перед тим, як відбудеться перевірка умови завершення циклу. Він має наступний формат

repeat
<Оператор 1>;

<Оператор N>;
until <умова завершення циклу>;

Тіло циклу виконується до тих пір, поки умова завершення циклу (вираз Булевського типу) не стане істинним. Оператор repeat має дві характерні особливості, про які потрібно завжди пам'ятати:


У наступному прикладі показано, як оператор repeat застосовується для підсумовування вводяться з клавіатури чисел. Підсумовування припиняється, коли користувач вводить число 0:

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

var
S, X: Integer;

begin
S := 0;
repeat
Readln(X);
S := S + X;
until X = 0;
Writeln(S=, S);
Writeln(Press Enter to exit…);
Readln;
end.


Часто буває, що умова виконання циклу потрібно перевіряти перед кожним повторенням тіла циклу. У цьому випадку застосовується оператор while, Який, на відміну від оператора repeat, Містить умову виконання циклу, а не умова завершення.

Оператор повтору while


Оператор повтору while має наступний формат:

 while <умова> do
<Оператор>;

Перед кожним виконанням тіла циклу відбувається перевірка умови. Якщо воно істинне, цикл виконується і умова обчислюється заново, якщо воно помилкове, відбувається вихід з циклу, тобто перехід до наступного за циклом оператору. Якщо спочатку умова помилкова, то тіло циклу не виконується жодного разу. Наступний приклад показує використання оператора while для обчислення суми S = ​​1 + 2 + .. + N, де число N задається користувачем з клавіатури:

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

var
S, N: Integer;

begin
Readln(N);
S := 0;
while N > 0 do
begin
S := S + N;
N := N – 1;
end;
Writeln(S=, S);
Writeln(Press Enter to exit…);
Readln;
end.


Пряма передача управління в операторах повтору


Для управління роботою операторів повтору використовуються спеціальні процедури-оператори Continue і Break, які можна викликати тільки в тілі циклу.

Процедура-оператор Continue негайно передає управління оператору перевірки умови, пропускаючи частину циклу (малюнок 4):

Малюнок 4. Схема роботи процедури-оператора Continue

Процедура-оператор Break перериває виконання циклу і передає управління першим оператором, розташованому за блоком циклу (рисунок 5):

Малюнок 5. Схема роботи процедури-оператора Break

Оператор безумовного переходу


Серед операторів мови Delphi існує один рідкісний оператор, про який автори спершу хотіли промовчати, але так і не зважилися. Це оператор безумовного переходу goto ("Перейти до"). Він задумувався для того випадку, коли після виконання деякого оператора треба виконати не наступний один по одному, а який-небудь інший, зазначений міткою, оператор.

Мітка – Це іменована точка в програмі, в яку можна передати управління. Перед вживанням мітка повинна бути описана. Розділ опису міток починається зарезервованим словом label, За яким слідують імена міток, розділені комами. За останніми ім'ям ставиться крапка з комою. Типовий приклад опису міток:

label
Label1, Label2;

У розділі операторів мітка записується з двокрапкою. Перехід на мітку виконується за допомогою зарезервованого слова goto, За яким слідує ім'я мітки:

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

label
M1, M2;

begin
M1:
Write (Бажаємо успіху);
goto M2;
Write (А цього повідомлення ви ніколи не побачите!);
M2:
goto M1;
Writeln (в освоєнні середовища Delphi!);
Writeln(Press Enter to exit…);
Readln;
end.


Ця програма буде виконуватися нескінченно, причому другий оператор Write не виконається жодного разу!

Увага! Відповідно до правил структурного програмування слід уникати застосування оператора goto, Оскільки він ускладнює розуміння логіки програми. Оператор goto використовувався на зорі програмування, коли виразні можливості мов були мізерними. У мові Delphi без нього можна успішно обійтися, застосовуючи умовні оператори, оператори повтору, процедури Break і Continue, оператори обробки винятків (останні описані в розділі 4).

Підпрограми


Загальні положення


У практиці програмування часто зустрічається ситуація, коли одну й ту ж групу операторів потрібно виконати без змін в кількох місцях програми. Щоб позбавити програміста від багаторазового дублювання однакових фрагментів, була запропонована концепція підпрограм. У цьому розділі ми розповімо про те, як ця концепція реалізована в мові Delphi.

Підпрограмою називається іменована логічно закінчена група операторів, яку можна викликати по імені (тобто виконати) будь-яку кількість разів з різних місць програми. У мові Delphi підпрограми оформляються у вигляді процедур і функцій.

Процедура – Це підпрограма, ім'я якої не може використовуватися у виразах як операнд. Процедура складається з заголовка і тіла. За структурою її можна розглядати як програму в мініатюрі. Коли процедура описана, її можна викликати по імені з будь-якої точки програми (у тому числі з неї самої!). Коли процедура виконає своє завдання, програма продовжиться з оператора, наступного безпосередньо за оператором виклику процедури. Використання імені процедури в програмі називається оператором виклику процедури.

Функція також є підпрограмою, але на відміну від процедури її ім'я може використовуватися у виразах як операнд, на місце якого підставляється результат роботи цієї функції.

Всі процедури і функції мови Delphi поділяються на дві групи: вбудовані і певні програмістом.

Вбудовані процедури і функції є частиною мови і можуть викликатися на ім'я без попереднього опису. У даній главі розглядаються лише базові групи вбудованих процедур і функцій, інші будуть розглянуті в інших розділах по ходу викладу матеріалу.

Процедури і функції програміста пишуться програмістом, тобто вами, відповідно до синтаксисом мови і являють собою локальні блоки. Попередній опис процедур і функцій програміста обов'язково.

Стандартні підпрограми


Арифметичні функції





















Abs(X)  Повертає абсолютне значення аргументу X.
Exp(X)  Повертає значення ex.
Ln(X)  Повертає натуральний логарифм аргументу X.
Pi  Повертає значення числа?.
Sqr(X)  Повертає квадрат аргументу X.
Sqrt(X)  Повертає квадратний корінь аргументу X.

Приклади:

























Вираз


Результат

Abs(-4) 4
Exp(1) 2.17828182845905
Ln(Exp(1)) 1
Pi 3.14159265358979
Sqr(5) 25
Sqrt(25) 5

Тригонометричні функції












ArcTan(X)  Повертає кут, тангенс якого дорівнює X.
Cos(X)  Повертає косинус аргументу X (X задається в радіанах).
Sin(X)  Повертає синус аргументу X (X задається в радіанах).

Приклади:
















Вираз


Результат

ArcTan(Sqrt(3)) 1.04719755119660
Cos(Pi/3) 0.5
Sin(Pi/6) 0.5

Зауважимо, що до складу середовища Delphi входить стандартний модуль Math, який містить високопродуктивні підпрограми для тригонометричних, логоріфміческіх, статистичних та фінансових розрахунків.

Опції виділення цілої або дробової частини















Frac(X)  Повертає дробову частину аргументу X.
Int(X)  Повертає цілу частину дійсного числа X. Результат належить речовинним типу.
Round(X)  Округлює дійсне число X до цілого.
Trunc(X)  Повертає цілу частину дійсного числа X. Результат належить цілого типу.

Приклади:



















Вираз


Результат

Frac(2.5) 0.5
Int(2.5) 2.0
Round(2.5) 3
Trunc(2.5) 2

Функції генерації випадкових чисел












Random  Повертає випадкове дійсне число в діапазоні 0? X <1.
Random(I)  Повертає випадкове ціле число в діапазоні 0? X <I.
Randomize  Заново ініціалізує вбудований генератор випадкових чисел новим значенням, отриманим від системного таймера.

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
























Chr(X)  Повертає символ, порядковий номер якого дорівнює X.
Dec(X, [N])  Зменшує цілу змінну X на 1 або на задане число N.
Inc(X, [N])  Збільшує цілу змінну X на 1 або на задане число N.
Odd(X)  Повертає True, якщо аргумент X є непарним числом.
Ord(X)  Повертає порядковий номер аргументу X в своєму діапазоні значень.
Pred(X)  Повертає значення, попереднє значення аргументу X в своєму діапазоні.
Succ(X)  Повертає значення, що випливає за значенням аргументу X в своєму діапазоні.

Приклади:






















Вираз


Результат

Chr(65) A
Odd(3) True
Ord(A) 65
Pred(B) A
Succ(A) B

Підпрограми для роботи з датою і часом



























Date  Повертає поточну дату у форматі TDateTime.
Time  Повертає поточний час в форматі TDateTime.
Now  Повертає поточні дату і час у форматі TDateTime.
DayOfWeek(D)  Повертає день тижня за датами в форматі TDateTime.
DecodeDate(…)  Розбиває значення дати на рік, місяць і день.
DecodeTime(…)  Розбиває значення часу на годину, хвилини, секунди і мілісекунди.
EncodeDate(…)  Формує значення дати по року, місяця і дня.
EncodeTime(…)  Формує значення часу по годині, хвилинах, секундах і мілісекунди.

Процедури передачі управління


















Break  Перериває виконання циклу.
Continue  Починає нове повторення циклу.
Exit  Перериває виконання поточного блоку.
Halt  Зупиняє виконання програми і повертає керування операційній системі.
RunError  Зупиняє виконання програми, генеруючи помилку часу виконання.

Різні процедури і функції




































FillChar(…)  Заповнює безперервну область символьним або байтовим значенням.
Hi(X)  Повертає старший байт аргументу X.
High(X)  Повертає саме старше значення в діапазоні аргументу X.
Lo(X)  Повертає молодший байт аргументу X.
Low(X)  Повертає наймолодше значення в діапазоні аргументу X.
Move(…)  Копіює задану кількість байт з однієї змінної в іншу.
ParamCount  Повертає кількість параметрів, переданих програмі в командному рядку.
ParamStr(X)  Повертає параметр командного рядка за його номером.
SizeOf(X)  Повертає кількість байт, займане аргументом X в пам'яті. Функція SizeOf особливо потрібна для визначення розмірів змінних Узагальнення типів даних, оскільки подання Узагальнення типів даних у пам'яті може змінюватися від однієї версії середовища Delphi до іншої. Рекомендуємо завжди використовувати цю функцію для визначення розміру змінних будь-яких типів даних; це вважається хорошим стилем програмування.
Swap(X)  Міняє місцями значення старшого і молодшого байтів аргументу.
UpCase(C)  Повертає символ C, перетворений до верхнього регістру.

Приклади:




























Вираз


Результат

Hi($F00F) $F0
Lo($F00F) $0F
High(Integer) 32767
Low(Integer) -32768
SizeOf(Integer) 2
Swap($F00F) $0FF0
UpCase(a) A

Процедури програміста


Очевидно, що вбудованих процедур і функцій для вирішення більшості прикладних задач недостатньо, тому доводиться придумувати власні процедури і функції. За своєю структурою вони дуже нагадують програму і складаються з заголовка і блоку. Заголовок процедури складається з зарезервованого слова procedure, Імені процедури і необов'язкового укладеного в круглі дужки списку формальних параметрів. Ім'я процедури – Це ідентифікатор, унікальний у межах програми. Формальні параметри – Це дані, які ви передаєте в процедуру для обробки, і дані, які процедура повертає (детально параметри описані нижче). Якщо процедура не отримує даних ззовні і нічого не повертає, формальні параметри (в тому числі круглі дужки) не записуються. Тіло процедури являє собою локальний блок, за структурою аналогічний програмі:

 procedure <ім'я процедури> (<список формальних параметрів>);
const …;
type …;
var …;
begin
<Оператори>
end;

Опис констант, типів даних і змінних дійсні тільки в межах даної процедури. У тілі процедури можна використовувати будь-які глобальні константи і змінні, а також викликати будь-які підпрограми (Процедури і функції).

Виклик процедури для виконання здійснюється за її імені, за яким в круглих дужках слід список фактичних параметрів, Тобто переданих у процедуру даних:

 <Ім'я процедури> (<список фактичних параметрів>);

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

Поняття процедури є надзвичайно важливим, оскільки саме воно лежить в основі однієї з найпопулярніших технологій вирішення задач на мові Delphi. Технологія ця зовні проста: завдання розбивається на кілька логічно відокремлених підзадач і рішення кожної з них оформляється у вигляді окремої процедури. Будь-яка процедура може містити в собі інші процедури, їх кількість обмежена тільки об'ємом пам'яті вашого комп'ютера.

Наведемо приклад невеликої програми, що використовує процедуру Power для обчислення числа X в ступені Y. Результат обчислення процедура Power заносить в глобальну змінну Z.

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

var
Z: Double;

procedure Power (X, Y: Double); / / X і Y – формальні параметри
begin
Z := Exp(Y * Ln(X));
end;

begin
Power (2, 3); / / 2 та 3 – фактичні параметри
Writeln (2 в ступені 3 =, Z);
Writeln(Press Enter to exit…);
Readln;
end.


Опції програміста


Опції програміста застосовуються в тих випадках, коли треба створити підпрограму, що бере участь у вираженні як операнд. Як і процедура, функція складається з заголовка і блоку. Заголовок функції складається з зарезервованого слова function, Імені функції, необов'язкового укладеного в круглі дужки списку формальних параметрів і типу повертається функцією значення. Функції повертають значення будь-яких типів даних крім Text і file of (див. файли). Тіло функції являє собою локальний блок, за структурою аналогічний програмі.

 function <ім'я функції> (<список формальних параметрів>): <тип результату>;
const …;
type …;
var …;
begin
<Оператори>
end;

У тілі функції повинен перебувати принаймні один оператор, привласнює значення імені функції або неявній локальної змінної Result. Якщо таких привласнення декілька, то результатом функції буде значення останнього з цих операторів. Перевага від використання змінної Result полягає в тому, що вона може брати участь у виразах як операнд.

Як приклад замінимо явно незграбну процедуру Power (див. вище) на функцію з таким же ім'ям:

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

function Power (X, Y: Double): Double; / / X і Y – формальні параметри
begin
Result := Exp(Y * Ln(X));
end;

begin
Writeln (2 в ступені 3 =, Power (2, 3)); / / 2 та 3 – фактичні параметри
Writeln(Press Enter to exit…);
Readln;
end.


Параметри процедур і функцій


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

Вихідні дані передаються в підпрограму за допомогою вхідних параметрів, а результати роботи підпрограми повертаються через вихідні параметри. Параметри можуть бути вхідними і вихідними одночасно.

Вхідні параметри оголошуються за допомогою ключового слова const; Їх значення не можуть бути змінені всередині підпрограми:

function Min(const A, B: Integer): Integer;
begin
if A < B then Result := A
else Result := B;
end;

Для оголошення вихідних параметрів служить ключове слово out:

procedure GetScreenResolution(out Width, Height: Integer);
begin
Width := GetScreenWidth;
Height := GetScreenHeight;
end;

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

var
W, H: Integer;
begin
GetScreenResolution(W, H);

end;

Після виклику процедури GetScreenResolution змінні W і H міститимуть значення, які були присвоєні формальним параметрам Width і Height відповідно.

Якщо параметр є одночасно і вхідним, і вихідним, То він описується за ключовим словом var:

procedure Exchange(var A, B: Integer);
var
C: Integer;
begin
C := A;
A := B;
B := C;
end;

Зміна значень var-Параметрів усередині підпрограми приводить до зміни значень змінних, переданих як аргументів:

var
X, Y: Integer;
begin
X := 5;
Y := 10;

Exchange(X, Y);
/ / Тепер X = 10, Y = 5

end;

При виклику підпрограм на місце out– І var-Параметрів можна підставляти тільки змінні, але не константи і не вираження.

Якщо при описі параметра не вказано ні одне з ключових слів const, out, Або var, То параметр вважається вхідним, його можна змінювати, але всі зміни не впливають на фактичний аргумент, оскільки вони виконуються з копією аргументу, що створюється на час роботи підпрограми. При виклику підпрограми на місці такого параметра можна використовувати константи та вирази. Приклад підпрограми:

function NumberOfSetBits(A: Cardinal): Byte;
begin
Result := 0;
while A <> 0 do
begin
Result := Result + (A mod 2);
A := A div 2;
end;
end;

Параметр A у наведеній функції є вхідним, але при цьому він використовується як локальної змінної для зберігання проміжних даних.

Різні способи передачі параметрів (const, out, var і без них) можна поєднувати в одній підпрограмі. У наступному закінченому прикладі процедура Average приймає чотири параметри. Перші два (X і Y) є вхідними і служать для передачі вихідних даних. Другі два параметри є вихідними і служать для прийому в зухвалій програмі результати обчислення середнього арифметичного (M) і середнього геометричного (P) від значень X і Y:

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

procedure Average(const X, Y: Double; out M, P: Double);
begin
M := (X + Y) / 2;
P := Sqrt(X * Y);
end;

var
M, P: Double;

begin
Average(10, 20, M, P);
Writeln (Середнє арифметичне =, M);
Writeln (Середнє геометричне =, P);
Writeln(Press Enter to exit…);
Readln;
end.


Існує різновид параметрів без типу. Вони називаються нетипізований і призначені для передачі і прийому даних будь-якого типу. Нетипізований параметри описуються за допомогою ключових слів const і var, При цьому тип даних опускається:

procedure JustProc(const X; var Y; out Z);

Усередині підпрограми тип таких параметрів не відомий, тому програміст повинен сам подбати про правильної інтерпретації переданих даних. Зауважимо, що при виклику підпрограм на місце нетипізований параметрів (у тому числі і на місце нетипізований const-параметрів) можна підставляти тільки змінні.

Передача фактичних аргументів на підпрограму здійснюється через спеціальну область пам'яті – стек. У стек міститься або значення переданого аргументу (передача значення), Або адреса аргументу (передача посилання на значення). Конкретний спосіб передачі вибирається компілятором в залежності від того, як оголошено параметр в заголовку підпрограми. Зв'язок між оголошенням параметра і способом його передачі пояснює таблиця 10:
























Ключове слово


Призначення


Спосіб доступу

<Відсутній> Вхідний Передається копія значення
const Вхідний Передається копія значення або посилання на значення в залежності від типу даних
out Вихідний Передається посилання на значення
var Вхідний і вихідний Передається посилання на значення

Таблиця 10. Способи передачі параметрів

Якщо передається значення, то підпрограма маніпулює копією аргументу. Якщо передається посилання на значення, то підпрограма маніпулює безпосередньо аргументом, звертаючись до нього через переданий адресу.

Опущені параметри процедур і функцій


У мові Delphi існує можливість задати параметрам процедур і функцій стандартні значення. Вони вказуються через знак рівності після типу параметра. Наприклад, опишемо процедуру, яка заповнює деяку область пам'яті заданим значенням:

 procedure Initialize (var X; MemSize: Integer; InitValue: Byte = 0);

Для параметра InitValue задано стандарт, тому його можна опустити при виклику процедури Initialize:

 Initialize (MyVar, 10); / / Еквівалентно Initialize (MyVar, 10, 0);

Підпрограма може містити будь-яку кількість параметрів зі стандартними значеннями, однак такі параметри повинні бути останніми у списку. Іншими словами, після параметра зі стандартним значенням не може слідувати звичайний параметр, тому наступний опис буде сприйнято компілятором як помилкове:

 procedure Initialize (var X; InitValue: Byte = 0; MemSize: Integer); / / Помилка!

Перевантаження процедур і функцій


У деяких випадках виникає необхідність у написанні підпрограм, які виконують однакові логічні дії, але над змінними різних типів даних. Наприклад:

procedure IncrementInteger(var Value: Integer);
procedure IncrementReal(var Value: Real);

У мові Delphi існує можливість дати двом і більше процедур (функцій) однакові ідентифікатори за умови, що всі такі процедури (функції) відрізняються списком параметрів. Така можливість називається перевантаженням. Для вказівки того, що процедура (функція) перевантажена, служить стандартна директива overload. З її допомогою вищенаведений приклад можна переписати таким чином:

 procedure Increment (var Value: Integer); overload; / / процедура 1
procedure Increment (var Value: Real); overload; / / Процедура 2

Яку саме процедуру використовувати в тому чи іншому випадку компілятор буде визначати на етапі компіляції програми за типами фактичних аргументів, переданих при виклику.

var
X: Integer;
Y: Real;
begin
X:=1;
Y:=2.0;
Increment (X); / / Викликається процедура 1
Increment (Y); / / Викликається Процедура 2
end.

При перевантаженні процедур і функцій існує особливість, пов'язана з цілочисельними типами даних. Припустимо, є дві процедури:

 procedure Print (X: Shortint); overload; / / процедура 1
procedure Print (X: Longint); overload; / / Процедура 2

Якщо ми спробуємо викликати процедуру Print, вказавши в якості фактичного аргументу целочисленную константу, то побачимо, що вибір компілятором варіанти процедури залежить від значення константи.

 Print (5); / / Викликається процедура 1
Print (150); / / Викликається Процедура 2
Print (-500); / / Викликається Процедура 2
Print (-1); / / Викликається процедура 1

Очевидно, що одне і те ж число може інтерпретуватися і як Longint, і як Shortint (наприклад, числа 5 і -1). Логіка компілятора в таких випадках така: якщо значення фактичного параметра потрапляє в діапазон значень декількох типів, за якими відбувається перевантаження, то компілятор вибирає процеудуру (функцію), у якої тип параметра має менший діапазон значень. Наприклад, виклик Print (5) буде означати виклик того варіанта процедури, який має тип параметра Shortint. А ось виклик Print (150) означатиме виклик того варіанта процедури, який має тип параметра Longint, т.к. число 150 не вміщається в діапазон значень типу даних Shortint.

Оскільки в нинішній версії середовища Delphi Узагальнення тип даних Integer збігається з фундаментальним типом даних Longint, наступний варіант перевантаження є помилковим:

procedure Print(X: Integer); overload;
procedure Print (X: Longint); overload; / / Помилка!

Така ж помилка виникає при використанні для користувача типів даних, визначених через загальний базовий тип.

type
TMyInteger = Integer;

procedure Print(X: Integer); overload;
procedure Print (X: TMyInteger); overload; / / Помилка!


Що робити в тих випадках, коли таке перевантаження просто необхідна? Для цього користувача тип даних необхідно створювати з використанням ключового слова type:

type
TMyInteger = type Integer;

procedure Print(X: Integer); overload;
procedure Print (X: TMyInteger); overload; / / Правильно


Необхідно зауважити, що при використанні перевантажених процедур (функцій), у яких є параметри, що мають стандартні значення, потрібно бути дуже уважним, тому що можуть виникнути ситуації, коли компілятор просто не буде знати, яку саме процедуру (функцію) ви хочете викликати. Наприклад:

 procedure Increment (var Value: Real; Delta: Real = 1.0); overload; / / процедура 1
procedure Increment (var Value: Real); overload; / / Процедура 2


Виклик процедури Increment з одним параметром викличе неоднозначність:

var
X: Real;
begin
Increment (X, 10); / / Викликається процедура 1
Increment (X); / / Помилка! Неоднозначність
end.

Забороняється також перевантажувати функції, які відрізняються лише типом, що повертається.

function SquareRoot(X: Integer): Single; overload;
function SquareRoot (X: Integer): Double; overload; / / Помилка!

Угоди про виклик підпрограм


У різних мовах програмування використовуються різні правила виклику підпрограм. Для того щоб з програм, написаних мовою Delphi, можливо було викликати підпрограми, написані на інших мовах (І навпаки), у мові Delphi існують директиви, відповідні чотирьом відомим угодами про виклик підпрограм: register, stdcall, pascal, cdecl.

Директива, що визначає правила виклику, поміщається в заголовок підпрограми, наприклад:

procedure Proc; register;
function Func(X: Integer): Boolean; stdcall;

Директива register задіє регістри процесора для передачі параметрів і тому забезпечує найбільш ефективний спосіб виклику підпрограм. Ця директива застосовується за умовчанням. Директива stdcall використовується для виклику стандартних підпрограм операційної системи. Директиви pascal і cdecl використовуються для виклику підпрограм, написаних на мовах Delphi і C / C + + відповідно.

Рекурсивні підпрограми


У ряді програм алгоритм вирішення задачі вимагає виклику підпрограми з розділу операторів тієї самої підпрограми, тобто підпрограма викликає сама себе. Такий спосіб виклику називається рекурсією. Рекурсія корисна перш за все в тих випадках, коли основне завдання можна розділити на підзадачі, які мають ту ж структуру, що і первинне завдання. Підпрограми, що реалізують рекурсію, називаються рекурсивними. Для розуміння суті рекурсії краще розуміти рекурсивний виклик як виклик іншої підпрограми. Практика показує, що в такому трактуванні рекурсія сприймається значно простіше і швидше.

Наведена нижче програма містить функцію Factorial для обчислення факторіала. Нагадаємо, що факторіал числа визначається через добуток всіх натуральних чисел, менших або рівних даному (факторіал числа 0 приймається рівним 1):

X! = 1 * 2 * … * (X – 2) * (X – 1) * X

З визначення випливає, що факторіал числа X дорівнює факторіалів числа (X – 1), помноженому на X. Математична запис цього твердження виглядає так:

X! = (X – 1)! * X

, Де 0! = 1

Остання формула використовується у функції Factorial для обчислення факторіала:

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

function Factorial(X: Integer): Longint;
begin
if X = 0 then / / Умова завершення рекурсії
Factorial := 1
else
Factorial := Factorial(X – 1) * X;
end;

begin
Writeln(4! = , Factorial(4)); // 4! = 1 * 2 * 3 * 4 = 24
Writeln(Press Enter to exit…);
Readln;
end.


При написанні рекурсивних підпрограм необхідно звертати особливу увагу на умову завершення рекурсії, інакше рекурсія виявиться нескінченною і додаток буде перервано через помилки переповнення стека.

Буває зустрічається така рекурсія, коли перша підпрограма викликає другу, а друга – першу. Така рекурсія називається непрямої. Очевидно, що записана першою підпрограма буде містити ще невідомий ідентифікатор другого підпрограми (компілятор не вміє заглядати вперед). В результаті компілятор повідомить про помилку використання невідомого ідентифікатора. Ця проблема вирішується за допомогою попереджувального (попереднього) опису процедур і функцій.

Попереджуюче оголошення процедур і функцій


Для реалізації алгоритмів з непрямою рекурсією у мові Delphi передбачена спеціальна директива попереднього опису підпрограм forward. Попередній опис складається з заголовка підпрограми і наступного за ним зарезервованого слова forward, наприклад:

procedure Proc; forward;
function Func(X: Integer): Boolean; forward;

Зауважимо, що після такого первинного опису в повному описі процедури або функції можна не вказувати список формальних параметрів і тип значення (для функції). Наприклад:

 procedure Proc2 (<формальні параметри>); forward;

procedure Proc1;
begin

Proc2 (<фактичні параметри>);

end;

procedure Proc2; / / Список формальних параметрів опущений
begin

Proc1;

end;

begin

Proc1;

end.


Процедурні типи даних


Поряд з уже відомими типами даних в мові Delphi введено так званий процедурний тип, За допомогою якого звичайні процедури і функції можна інтерпретувати як деяку різновид змінних. Визначення процедурного типу складається з зарезервованого слова procedure або function, За яким слідує повний опис параметрів. Для функції додатково вказується тип результату. Символічні імена параметрів ніякої ролі не грають, оскільки ніде не використовуються.

type
TProc = procedure (X, Y: Integer);
TFunc = function (X, Y: Integer): Boolean;

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

var
P: TProc;
F: TFunc;

При роботі з процедурної змінної важливо розуміти, що вона не дублює код підпрограми, а містить лише її адресу. Якщо звернутися до такої змінної як до підпрограми, відбудеться виконання підпрограми, адреса якої записаний в змінної.

program Console;

{$APPTYPE CONSOLE}

uses
SysUtils;

function Power(X, Y: Double): Double;
begin
Result := Exp(Y * Ln(X));
end;

type
TFunc = function (X, Y: Double): Double;

var
F: TFunc;

begin
F: = Power; / / В змінну F заноситься адреса функції Power
Writeln (2 power 4 =, F (2, 4)); / / Виклик Power допомогою F
Writeln(Press Enter to exit…);
Readln;
end.

наступна стаття серії


Посилання по темі



Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*