Зміст вступ 5

Вид материалаДокументы
§ 7.1 Чи потрібні процедури і функції
§ 7.2 Процедури
Подобный материал:
1   ...   14   15   16   17   18   19   20   21   ...   32

§ 7.1 Чи потрібні процедури і функції




Питання, винесене до заголовку даного параграфу, може здатись вам дещо незрозумілим, як воно незрозуміле і нам. Адже ми до цього весь час використовували стандартні процедури і функції мови програмування, правда особливо на цьому не наголошували. А оскільки ми їх використовували, то вони потрібні! Але мова йтиме далі не про процедури і функції, вкладені в систему програмування розробниками мови, а про написання власних процедур і функцій. І ось тут питання стає доречним: чи потрібні власні процедури і функції? Відповідь однозначна: потрібні і навіть дуже, особливо при написанні великих програм. Уявимо собі таку картину, взяту з футбольного матчу. Перед початком гри тренер дає вказівку правому захиснику: “Ти повинен на 7, 12, 15, 21, 29, 43, 47, 54, 76 і 86 хвилинах зустрічі здійснювати рейди по правому краю.” Мабуть всім вам така інструкція здасться абсурдною. Нам також! Мабуть набагато простішою і ціннішою для гравця і всієї команди в цілому була б інструкція такого типу: “При можливості частіше здійснюй проходи по правому краю”!

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

Задача 146 Знайти найменше спільне кратне (НСК) 5-ти чисел.

Розв’язання: Для розв’язання цієї задачі нам потрібно вміти знаходити НСК двох чисел. Тому спочатку розв’яжемо саме цю задачу, а потім повернемось до розв’язання сформульованої вище.

Задача 147 Знайти НСК 2-х чисел.

Розв’язання: Нам відомо, як знаходити НСД (nsd) за алгоритмом Евкліда (див задачу ). Скористаємось ним для знаходження НСК (nsk) на підставі такого твердження: якщо НСД (a,b)=nsd, то nsk=(a div nsd)·b. Спробуйте самостійно вивести дану формулу. А ми приведемо програмну реалізацію розв’язку задачі, не використовуючи ніяких коментарів, ми вже й так все роз’яснили.

program n_s_k1;

var a, b, a1, b1, nsk : longint;

begin

write(’ Введіть перше число: ’); readln(a);

write(’ Введіть друге число: ’); readln(b);

a1:=a; b1:=b;

while a1 <> b1 do

if a1 > b1 then a1 := a1–b1

else b1 := b1–a1;

nsk := (a div a1)*b;

writeln(’НСК чисел ’,a,’ та ’,b,’ = ’,nsk);

readln

end.

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

program n_s_k1;

var a, b, b1, b2, b3, b4, b5, nsk : longint;

begin

write('Введiть перше число: '); readln(b1);

write('Введiть друге число: '); readln(b2);

write('Введiть трете число: '); readln(b3);

write('Введiть четверте число: '); readln(b4);

write('Введiть п''яте число: '); readln(b5);

a:=b1; b:=b2;

while a <> b do

if a > b then a := a - b

else b := b - a;

nsk := (b1 div a)*b2;

a:=nsk; b:=b3;

while a <> b do

if a > b then a := a - b

else b := b - a;

nsk := (nsk div a)*b3;

a:=nsk; b:=b4;

while a <> b do

if a > b then a := a - b

else b := b - a;

nsk := (nsk div a)*b4;

a:=b1; b:=b2;

a:=nsk; b:=b5;

while a <> b do

if a > b then a := a - b

else b := b - a;

nsk := (nsk div a)*b5;

writeln('НСК п''яти чисел = ',nsk);

readln

end.

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


§ 7.2 Процедури



Розв'яжемо попередню задачу у дещо модифікованому, або як прийнято говорити – узагальненому вигляді.

Задача 148 Знайти НСК n чисел.

Розв’язання: Спосіб розв'язання нам вже відомий. Але ж ми мали вивчати процедури. Тому давайте домовимось, що ми не знаємо, для скількох чисел ми будемо шукати НСК, але нам відомо, що в любому випадку результат не буде перевищувати тип longint. Дані будемо вводити з клавіатури і ознакою закінчення завдання буде число 0.

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

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

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

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

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

program nsk_n_v2; { Програма знаходження НСК для n чисел }

var a, nsk : longint; { нам будуть потрібні всього дві змінні: число і НСК}

procedure nsk_2; { процедура для знаходження НСК двох чисел }

var x, y : longint; { додаткові змінні, значення яких програма не бачить }

begin { початок процедури }

x := nsk; y := a; { зберігаємо вхідні дані }

while x <> y do { і працюємо з внутрішніми змінними }

if x > y then x := x - y { процедури, використовуючи вже відомий }

else y := y - x; { нам алгоритм Евкліда для НСД }

nsk := (nsk div x) * a; { і знаходимо НСК }

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

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

nsk := 1; { для першого входження в процедуру nsk_2 }

a := 1; { для входження в цикл }

while a <> 0 do { цикл завершується коли з клавіатури ввели 0 }

begin

write('Введіть число: '); readln(a); { зчитали число }

if a<>0 then { і якщо воно не нуль }

begin

nsk_2; { то знаходимо НСК двох чисел }

writeln('НСК = ',nsk); { і виводимо значення на екран }

end;

end; { кінець циклу, коли ввели 0 }

writeln('Для закінчення нажміть '); { повідомлення }

readln; { чекаємо натиснення }

end. { кінець програми }

Зверніть увагу на внутрішні змінні процедури nsk_2. Якщо ви захочете побачити їх значення після чергового виклику процедури, то виклик write(x, ' ', y), здійснений в тілі програми, не дасть вам нічого. Більше того, програма навіть не захоче працювати і компілятор видасть повідомлення про невідомий ідентифікатор6, тобто про невідому змінну. Такий самий виклик в тілі процедури виведе на екран відповідні значення змінних. Спробуйте самостійно вставити у наведену вище програму у відповідних місцях вказаний виклик і ви самі у цьому наочно переконаєтесь. Це пояснюється тим, що кожна змінна має свою область дії, а це в свою чергу означає, що нам потрібно внести доповнення до правил нашої гри, яку ми почали на перших сторінках книги:
  1. Кожен ідентифікатор (назва змінної, процедури, функції і т.д.) повинен бути описаний перед тим, як він буде використаний.
  2. Областю дії ідентифікатора є блок в якому його описано.
  3. Всі ідентифікатори в блоці повинні бути унікальними, тобто не повторюватись.
  4. Один і той самий ідентифікатор може бути по-різному визначено в кожному окремому блоці.
  5. Якщо ідентифікатор програми користувача співпадає з ім’ям стандартної процедури або функції, то останні недоступні в межах області дії підпрограми, описаної користувачем, тобто стандартна функція або процедура ігнорується, а виконується підпрограма користувача.

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

program demo_proc;

uses dos, crt;

var x, y : longint; { ввели глобальні змінні для всієї програми }

procedure hello;

var x : byte; { локальна змінна видима тільки в даній процедурі}

begin

x := 5; { присвоїли значення локальній змінній }

writeln('А у мене Х = ',x); { вивели локальну змінну на екран }

end;

procedure inc(x:longint); { переписали стандартну процедуру }

begin

x:=2*x; { працюємо з глобальної змінною }

writeln('X = ',x); { вивели глобальну змінну на екран }

end;

begin

clrscr;

write('Введiть число: ');readln(x); { ввели значення глобальної змінної}

writeln('X = ',x); { вивели його на екран }

hello; { викликали процедуру з локальною змінною }

inc(x); { викликали перевизначену процедуру з глобальною змінною }

writeln('X = ',x); { знову вивели значення х на екран }

writeln('Для закiнчення нажмiть ');

readln;

end.




Рис. 1.
Результатом виконання цієї демонстраційної програми буде те, що приведено на рис. 1. Зверніть увагу, що ми ввели значення змінної х рівне 300. Після виклику процедури hello значення змінної стало 5 – локальне значення цієї процедури, після цього викликали “стандартну” для Паскаля процедуру inc(x) і очікували збільшення значення змінної на 1, але оскільки ми задали стандартне ім’я власній процедурі, то програма перестала бачити стандартну процедуру inc, а виконує нашу, в якій значення змінної подвоюється, але надрукувавши знову значення змінної х з подивом бачимо, що значення змінної х не змінилося. В чому ж справа? А вся справа в тому, що ми не розглянули ще одну особливість процедур.

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

Параметр, або група параметрів, перед якими відсутнє зарезервоване слово var і за якими йде тип, називаються параметрами-значеннями. Так у наведеній вище демонстраційній програмі в описі procedure inc(x:longint); х – параметр-значення. Формальний параметр-значення обробляється як локальна по відношенню до процедури змінна і зміни формальних параметрів не впливають на фактичне значення відповідних параметрів.

Якщо ми у приведеній демонстраційній програмі змінимо опис процедури на procedure inc(var x : longint);, то ми отримаємо процедуру з параметрами-змінними. Параметри-змінні використовуються у тому випадку, коли нам потрібно передати значення змінної з процедури у той блок програми, що її викликав. Спробуйте вставити слово var у опис вищезгаданої процедури у демонстраційній програмі і ви помітите, що значення змінної х у програмі зміниться на очікуване.

Нетипізовані змінні ми розглянемо у наступному параграфі. А зараз розв’яжемо ще одну просту задачу, де наведемо ще один приклад використання процедур.

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