Сторінка
4
4. Процедури, підпрограми та параметри Розглянемо задачу: довільні значення трьох змінних a, b, c переставити за необхідності так, щоб вони були упорядковані за неспаданням, тобто щоб мали місце нерівності a£ b£ c. Алгоритм розв'язання цієї задачі простий: якщо a>b, то обміняти значення змінних a і b; {гарантовано, що a£ b} якщо b>c, то обміняти значення змінних b і c; {гарантовано, що b£ c і a£ c; але нерівність a£ b не гарантована, тому:} якщо a>b, то обміняти значення змінних a і b. Обмін значень двох змінних, наприклад, a і b, задається трьома операторами з допоміжною змінною: t:=a; a:=b; b:=t. Мовою Паскаль алгоритм записується так: program sort3(input, output); var a, b, c, t : integer; begin writeln('задайте три цілих'); readln(a, b, c); if a>b then begin t:=a; a:=b; b:=t end; if b>c then begin t:=b; b:=c; c:=t end; if a>b then begin t:=a; a:=b; b:=t end; writeln('упорядкування: ', a, ' ', b, ' ', c) end. Проте три майже однакові складені оператори, що задають ті самі дії, тільки з різними змінними – це нудно. Аналогічно функціям, можна один раз описати обмін значень двох змінних, представлених параметрами, дати ім'я цьому опису, а потім тільки позначати його, тобто вказувати ім'я опису й змінні, чиї значення повинні обмінятися. На відміну від функцій, при обміні відбувається не обчислення якогось одного значення, а змінюється стан пам'яті програми (недарма обмін заданий складеним оператором). Тому такий опис оформляється й використовується інакше. Опис обміну задається процедурою, а її виклик являє собою окремий оператор. Процедура має загальний вигляд procedure ім'я(означення параметрів); означення імен begin послідовність операторів end; Процедура, як і функція, є означенням імені і записується серед означень програми. На відміну від функції, в її заголовку немає імені типу для значень, породжуваних у результаті виклику, тому що ніякі значення не породжуються. За цією ж причиною в тілі процедури не може бути операторів присвоювання з її ім'ям у лівій частині. Виклик процедури складається з імені й аргументів у дужках і записується як окремий оператор, наприклад, readln(x, y). Отже, напишемо інший варіант програми упорядкування трьох значень: program sort31(input, output); var a, b, c : integer; procedure swap(xx, yy : integer); var t : integer; begin t:=xx; xx:=yy; yy:=t end; begin writeln('задайте три цілих:'); readln(a, b, c); if a>b then swap(a, b); if b>c then swap(b, c); if a>b then swap(a, b); writeln('упорядкування: ', a, ' ', b, ' ', c) end. КРАСИВО, АЛЕ НЕПРАВИЛЬНО! Справа в тім, що при виконанні виклику, наприклад, swap(a, b), змінні xx і yy одержать значення змінних a і b, потім ці значення поміняються місцями, виконання виклику закінчиться, а в змінних a і b залишаться ті ж самі значення, що були перед викликом. Наприклад, якщо змінним a, b, c присвоїти "з зовнішнього світу" значення відповідно 3, 1, 2, то буде надруковано упорядкування: 3 1 2. Слушність цього напису дуже сумнівна. Отже, при виконанні виклику процедури (чи функції) спочатку параметри одержують значення аргументів, а потім їх зміни ніяк не відбиваються на аргументах (рис.3.3). Тому параметри, що дотепер розглядалися, називаються параметрами-значеннями. Мова Паскаль допускає в заголовках процедур і функцій означати параметри іншого виду. Вони називаються параметрами-змінними і означаються зі словом var попереду. Так, процедура swap набуває вигляду: procedure swap(var xx, yy : integer); var t : integer; begin
t:=xx;
xx:=yy;
yy:=t
end; Таке означення параметрів забезпечує, що при виконанні виклику процедури або функції іменам параметрів ставляться у відповідність змінні, тобто ділянки пам'яті, уже зіставлені аргументам. При виконанні виклику зміна значення параметра-змінної насправді є зміною значення аргументу (рис.3.4).
Момент виконання програми |
a |
b |
c |
перед першим викликом |
3 |
2 |
1 |
після першого виклику |
2 |
3 |
1 |
після другого виклику |
2 |
1 |
3 |
після третього виклику |
1 |
2 |
3 |
var xx, yy : integer. Секції розділяються ";". За необхідності ми могли б написати, наприклад,