Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Параметри - значення




End

Begin

End

Begin

Begin

Begin

Begin

Begin

Begin

Begin

Begin

Begin

Begin

Begin

Begin

Begin

Тип запис

Begin

Repeat

Begin

clrscr;

nath:=[1..n];

kin:=[];

next:=2;

while not(next in nath) do

inc(next);

kin:=kin+[next];

j:=next;

while j<=n do

nath:=nath-[j];

j:=j+next;

end;

until nath=[];

for j:=2 to n do

if j in kin then write(j:5);

end.

 


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

type tPerson = record

Name,Surname,SecondName: string [30];

Male: boolean;

Address: string;

Phone: longint;

end;

 

Оголошення змінної типу запис виконується стандартно, за допомогою var. Частини запису (у нашому випадку: Name, Surname, SecondName, Male, Address, Phone) називаються полями. Звертання до поля запису в програмі виконується за допомогою знака ‘.’ (крапка). Приклад звертання до полів:

 

var p: tPerson;

...

...

p.Surname:='Іванов';

p.Name:='Іван';

p.SecondName:=’Іванович’;

...

if (p.Phone<0) or (p.Phone>999999)

then writeln(‘Помилка’);

...

end.

 

Помітимо, що в цьому прикладі постійно приходиться звертатися до полів однієї і тієї ж змінної типу запис, і, отже, постійно писати її ім'я. Існує можливість позбутися від цієї незручності. У Турбо Паскалі є оператор приєднання (with), що дозволяє один раз указати, яким записом ми користаємося і надалі писати лише імена полів. Цей оператор має такий вигляд:

with <ім'я_запису> do <оператор>;

Найчастіше як оператор використовується складений оператор.

Приклад:

with p do begin

Surname:=' Іванов';

Name:=’Іван’;

...

end;

 

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

 

const N = 30;

type tStaff = array [1..N] of tPerson;

 

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

 

type tPoint = record

x,y: integer;

end;

 

tRectangle = record

LeftTop: tPoint;

Width, Height: integer;

Color: integer;

end;

 

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

 

var rect: tRect;

with rect, LeftTop do begin

x:=100;

y:=150;

Color:=11;

...

end;

 

Без використання with з'явилися б вирази виду rect.Color, rect.LeftTop.x, rect.LeftTop.y і т.п.

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

 

const MaxVertex = 200;

 

type tPolygon = record

size: integer;

V: array [1..MaxVertex] of tPoint;

Color: tColor;

end;

 

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

type tFKind = (fCir,fRect,fSqr);

tFigure = record

Color: integer;

case kind: tFKind of

fCir: (Center: tPoint; r: integer);

fRect: (LeftTop,RightBottom: tPoint);

fSqr: (LT: tPoint; size: integer);

end;

У цьому записі мається одне звичайне поле (Color), а інші 6 і являють собою варіантну частину. Для кола в ній є поля Center і r, для прямокутника — LeftTop і RightBottom, для квадрата — LT і size. Фраза kind: tFKind не є обов'язкової, вона служить для розуміння того, які поля до яких фігур відносяться. Можна написати просто case integer of... і нумерувати варіанти цілими числами. Помітимо також, що в оголошенні нашого запису немає слова end, що відноситься до case.

 
 

Ми можемо звертатися до будь-якого поля варіантної частини, однак варто пам'ятати, що при записі даних у поле для однієї фігури поля для інших фігур можуть змінитися. Щоб зрозуміти, чому так відбувається, досить розглянути спосіб збереження змінної типу tFigure:

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

 


Процедури і функції

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

Ті дії, що входять у процедуру, записуються до початку основної програми в наступному виді:

 

program...

const...

type...

var...

procedure MyProc;

{дії}

end;

{основна програма}

end.

 

Розглянемо приклад знаходження максимуму з трьох чисел:

program Max1;

var a,b,c,m: integer;

write('Уведіть a: '); readln(a);

write('Уведіть b: '); readln(b);

write('Уведіть c: '); readln(c);

if a>b then m:=a else m:=b;

if c>m then m:=c;

writeln('Максимум = ',m);

readln;

end.

Перепишемо його з використанням процедури:

program Max2;

var a,b,c,m: integer;

procedure FindMax;

if a>b then m:=a else m:=b;

if c>m then m:=c;

end;

write('Уведіть a: '); readln(a);

write('Уведіть b: '); readln(b);

write('Уведіть c: '); readln(c);

FindMax;

writeln('Максимум = ',m);

readln;

end.

Цей варіант можна поліпшити. Поки наша процедура може шукати мінімум тільки серед значень конкретних змінних a, b і c. Змусимо її шукати мінімум серед будь-яких трьох цілих чисел і поміщати результат у потрібну нам змінну, а не завжди в m.

Щоб була видна користь від такої процедури, розглянемо приклад програми для пошуку максимуму серед чисел a+b, b+c и a+c:

program Max3;

var a,b,c,m: integer;

procedure FindMax(n1,n2,n3: integer; var max: integer);

if n1>n2 then max:=n1 else max:=n2;

if n3>max then max:=n3;

end;

write('Уведіть a: '); readln(a);

write('Уведіть b: '); readln(b);

write('Уведіть c: '); readln(c);

FindMax(a+b,b+c,a+c,m);

writeln('Максимум із сум = ',m);

readln;

end.

У дужках після імені процедури (у її описі) записані так називані параметри. Цей запис позначає, що усередині процедури можна використовувати цілі числа, позначені n1, n2 і n3, а також заносити значення в змінну типу integer, що усередині процедури називається max (а реально під час роботи програми всі дії виконуються над змінною m). Параметри, у яких зберігаються числа (n1,n2,n3) називаються параметрами-значеннями; а ті, котрі позначають змінні (max) – параметрами-змінними, перед ними в описі ставиться слово var. Параметри, на які маються посилання усередині процедури (n1, n2, n3, max), називаються формальними, а ті, котрі реально використовуються при виклику (a+b, b+c, a+c, m) — фактичними.

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

У нашому прикладі можна переписати програму і по-іншому. Максимум із трьох чисел визначається по них однозначно, чи, говорячи математичною мовою, є функцією цих трьох чисел. Поняття функції є також і в Паскале. Розглянемо таку програму:

program Max4;

var a,b,c,m: integer;

function Max(n1,n2,n3: integer): integer;

var m: integer;

if n1>n2 then m:=n1 else m:=n2;

if n3>m then m:=n3;

Max:=m;

end;

write('Уведіть a: '); readln(a);

write('Уведіть b: '); readln(b);

write('Уведіть c: '); readln(c);

writeln('Максимум = ',Max(a+b,b+c,a+c));

readln;

end.

Нам уже відомо як викликати функцію з програми (наприклад sqrt, sin і т.п.). Розглянемо опис функції. Воно дуже схоже на опис процедур, але є дві відмінності:

1. Після імені функції і списку параметрів (якщо є) через двокрапку записується тип значення функції (можливі не тільки числові типи, але і логічні, строкові, символьні);

2. Серед операторів у тілі функції найбільш важливими є оператори присвоювання значення функції (у нашому випадку це рядок Max:=m;).

У записаній вище функції використовується так називана локальна змінна m, тобто змінна, котра «видна» тільки нашій функції, а інші процедури і функції, а також головна програма її «не бачать». Крім локальних змінних у Турбо-Паскалі можна визначати локальні константи і типи.

 

Приведемо інші приклади процедур і функцій.

1. Напишемо на Паскале функцію .

function Cube(x: real): real;

Cube:=x*x*x;

end;

 

2. Обчислення площі трикутника через довжини сторін. Тут буде використана формула Герона: , де p – напівпериметр трикутника, a, b, c – довжини сторін.

function Square(a,b,c: real): real;

var p: real;

p:=(a+b+c)/2;

Square:=sqrt(p*(p-a)*(p-b)*(p-c));

end;

 

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

procedure SqEquation(a,b,c: real; var RootsExist: boolean; var x1,x2: real);

var d: real;

d:=sqr(b)-4*a*c;

if d>=0 then begin

RootsExist:=true;

x1:=(-b+sqrt(d))/(2*a);

x2:=(-b-sqrt(d))/(2*a);

else RootsExist:=false;

end;

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

function EqHasRoots(a,b,c: real; var x1,x2: real): boolean;

var d: real;

d:=sqr(b)-4*a*c;

if d>=0 then begin

EqHasRoots:=true;

x1:=(-b+sqrt(d))/(2*a);

x2:=(-b-sqrt(d))/(2*a);

else EqHasRoots:=false;

end;

Використовувати таку функцію навіть простіше чим останню процедуру:

if EqHasRoots(1,2,1,r1,r2) then writeln(r1,' ',r2) else writeln('Немає коренів');

Формальні і фактичні параметри. Механізм параметрів

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

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

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

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

Ідентифікатори формальних параметрів можна вважати умовними позначеннями в тілі підпрограми тих реальних чи ФАКТИЧНИХ параметрів, що будуть передані в підпрограму при її виклику.

Нижче приводяться синтаксичні діаграми для формальних параметрів.

Приклади заголовків процедур і функцій:

function MaxElem (A:Vector; n=byte): real;

Procedure Sum (А,В:Matrix; var С:Matrix);

Procedure Move (var Source, Dest; n: word);

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

Procedure Incorrect (var A: array[1..10] of byte)

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

type

MyArray = array[1..10] of byte;

procedure Correct (var A: MyArray);

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

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

1. Параметри, перед якими відсутнє службове слово var і за якими вказаний ідентифікатор типу.

2. Параметри, перед якими є службове слово var і за яким стоїть тип.

3. Параметри, перед якими є службове слово var після якого не вказаний тип.

Ці три способи завдання формальних параметрів відображають три різних способи передачі параметрів. перший спосіб називається передачею параметрів за значенням, другий спосіб - передачею параметрів по посиланню, третій спосіб передачею не типізованих параметрів по посиланню. Одна підпрограма може одержувати різні параметри всіма трьома способами одночасно.

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

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

Усередині підпрограми можливі довільні дії з даним формальним параметром, але будь-які зміни його значення НІЯК НЕ ВІДБИВАЮТЬСЯ на значеннях змінних поза підпрограмою.

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

procedure SumSquare (X, У: real);

begin

x:= x*x;

Y:= Y*Y;

Writeln('Сума квадратів = ',X+Y) end;

Виклик цієї процедури може відбуватись так:

var

А, у: real;

begin

write(‘a=>’); readln(a);

write(‘b=>’);writeln(b);

SumSquare(А,У);

При виклику цієї процедури з фактичними параметрами А і В значення цих параметрів (один раз) копіюються у відповідні формальні параметри X і У, і подальші маніпуляції з формальними параметрами усередині процедури "SumSquare ніяк не впливають на значення змінних А і В

Параметри-змінні

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

Пояснимо це на прикладі. Нехай необхідно обчислити суму і різницю квадратів двох чисел, причому реалізувати ці обчислення у виді процедури. Phj,bvj чорновий начерк такої процедури:

procedure Drafti (X, Y: real);

var

Sum, Sub: real;

begin

Sum:= X*X+Y*Y;

Sub:= X*X-Y*Y end;

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

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

procedure Draft2 (X, Y: real; Sum, Sub: real);

begin

Sum:= X*X+Y*Y;

Sub:= X*X-Y*Y end;

Що відбудеться при виклику даної процедури, наприклад, у наступному фрагменті?

var

А, У: real;

SumAB, SubAB: real;

begin

A:= 1.7;

В:= 8.09;

Draft2(А,У,SumAB,SubAB);

Відповідно до правил мови параметри Sum і Sub у процедурі Draft2, передані за значенням, є локальними в межах цієї процедури, тому присвоювання їм будь-яких значень НЕ ПРИВЕДЕ до одержання цих значень змінними SumAB і SubAB.

Для того, щоб зміна в тілі процедури значення формального параметра ПРИВОДИЛА ДО АНАЛОГІЧНОЇ ЗМІНИ відповідного фактичного параметра, необхідно використовувати передачу параметра по посиланню:

procedure SumSub (X, Y: real; var Sum, Sub: real);

begin

Sum:= X*X+Y*Y;

Sub:= X*X-Y*Y end;

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

Якщо виклик процедури SumSub матиме такий вигляд:

var

А, У: real;

SumAB, SubAB: real;

begin

А:= 1.7;

В:= 8.09;

SumSub(А,У,SumAB,SubAB);

то присвоювання параметрам Sum і Sub усередині тіла процедури будуть означати відповідні присвоювання змінним SumAB і SubAB, переданими процедурі як параметрі-змінні. Після завершення процедури ці змінні будуть містити шукані значення.

Приведемо ще один приклад передачі параметрів-змінних. Часто буває необхідно зробити обмін значень між двома змінними. Ці дії можна оформити у виді наступної процедури:

procedure Swap (var X, У: real);

var

Т: real;

begin

Т:= X;

X:= У;

У:= Т

end;

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

Swap(А,У)

присвоювання параметрам-змінним Х и У в тілі процедури означають присвоювання відповідним фактичним параметрам А і В.

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

Безтипові параметри

Крім двох звичайних способів передачі параметрів у підпрограми при їхньому виклику - за значенням і по посиланню - Turbo Pascal допускає третій спосіб передачі, при якому тип параметра не фіксується. У цьому випадку специфікація формального параметра в заголовку підпрограми має наступний загальний вид:

var Ident,

де Ident - ідентифікатор формального параметра.

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

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

Приведення типів.

Turbo Pascal, на відміну від авторської версії мови, містить спеціальну конструкцію явного перетворення типу. Ця конструкція зветься приведення типу змінної. Приведення не здійснює ніяких операцій з змінної (як, наприклад, операції Round чи Trunc), а просто дозволяє трактувати змінну одного типу, як змінну деякого іншого типу. Загальний вид приведення типу наступний:

ТИП (ЗМІННА)

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

var

SI: 0..255;

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

SI:= 'А'

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

char(SI):= 'A'

Конструкція приведення типу може знаходитися у всіх позиціях, у яких допускається входження змінної. При цьому слід пам'ятати, що тип даної конструкції завжди визначається ідентифікатором типу перед дужками. Крім того, варто враховувати, що розмір змінної (число байтів, які займає ця змінна) ПОВИНО БУТИ РІВНИМ розміру типу, ідентифікатор якого зазначений у приведенні.

Наведемо ще один приклад. Нехай мається такий опис:

type

Days = (Monday,Tuesay,Wednesday,Thursday,Friday, Satuday,Sunday);

var

aDay: Days;

Num: byte;

Тоді припустимі такі оператори:

Days(Num):= Monday; { Num = 0 }

aDay:= Days(Num); { aDay = MonDay }

Num:= byte(Friday); { Num = 4 }

Зверніть увагу, що еквівалентом останнього присвоювання буде наступне:

Num:= Ord(Friday);

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

aDay:= Days(Num+l);

Num:= byte(Pred(Friday));

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

var

li: longint;

I: integer;

begin

LI:= 1234567;

I:= integer (li+1);

Writeln(I)

end.

приведе до виведення на екран значення -10616 (значення типу longint у процесі приведення до типу integer буде "обрізане").

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

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

Досі ми розглядали процедури і функції просто як текстові фрагменти програми, що позначені іменами. Таке положення підпрограм істотно відрізняє їх, наприклад, від змінних, котрі в різні моменти виконання можуть приймати різні значення. Однак, Turbo Pascal дозволяє вводити змінні спеціального виду, значеннями яких можуть служити підпрограми. Іншими словами. Turbo Pascal дозволяє інтерпретувати процедури і функції як значення, які можна присвоювати змінним і передавати як параметри. Підкреслимо, що мова тут йде про підпрограми як цілісні об'єкти, а не про значення, що виникають у результаті їхнього виконання.

Найпростіший випадок опису змінної, значеннями якої можуть бути процедури, приведений нижче.

var

Р: procedure;

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

type

Func = function(х,у:integer):integer;

var

Fl, F2: Func;

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

function Add(a,b:integer): integer;

begin

Add:= a+b

end;

 

те припустиме присвоювання виду Fl:= Add;

Зверніть увагу, що в останньому операторі змінній Fl як значення присвоюється ФУНКЦІЯ Fl; у даному випадку виконання цієї функції не відбувається. Після такого присвоювання мається можливість викликати функцію Add як безпосередньо по її імені, так і за допомогою вказівки змінної F 1. Так, наприклад виконання операторів

WriteLn(Add(l,2))

WriteLn(Fl(l,2))

надрукують число 3.

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

Приклади:

type

Proc = procedure;

BinOperation = function (х, у: real):real;

OnOperation = function(х:real):real;

Reading = procedure(var F:text;var Elem:char);

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

Таким чином. Turbo Pascal дозволяє визначати змінні, значеннями яких можуть бути процедури і функції. Для таких змінних припустимі оператори присвоювання, у правих частинах яких знаходяться ідентифікатори інших процедурних змінних чи ідентифікатори підпрограм. Змінна процедурного типу в різні моменти виконання програми може мати як значення різні підпрограми. Такі змінні можна, зокрема, використовувати для виклику підпрограм, що присвоєні цим змінної. Наприклад, якщо в програмі маються наступні описи:

var

Operation: function (х, у: real):real;

function Add (a,b:real): real;

begin

Add:= a+b

end;

function Sub (a,b:real): real;

begin

Sub:= a-b

end;

то виконання послідовності операторів

if Condition then

Operation:= Add

elseOperation:= Sub;

Write(Operation(2.05,3+X))

приведе до присвоєння змінної Operation, у залежності від істинності умови, або функції Add, або функції Sub. Конструкція Operation (2.05, 3+X) викликає активізацію тієї функції, що була присвоєна змінній Operation. Таким чином, у викликах функцій і в операторах процедури, крім ідентифікаторів відповідних підпрограм, можуть стояти імена змінних процедурних типів. Виконання таких конструкцій буде полягати у виклику підпрограм, що були присвоєні цим змінним.

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

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

type

Proc = procedure (Т:real);

NoticePtr = ^Notice;

Notice = record

Next: NoticePtr;

Time: real;

Action: Proc

end;

var

NewNotices: array[1..10] of Proc;

Notices: NoticePtr;

з урахуванням цих описів припустимі наступні оператори процедур:

MewNotices[7]((Х+17.5) *2);

Notices^.Action(0.25);

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

1. Підпрограма, що присвоюється процедурній змінний, повинна бути відтрансльована в режимі "далекого типу викликів". Не поглиблюючи в подробиці системного характеру, досить пояснити, що необхідного ефекту можна досягти, розташувавши перед чи підпрограмою групою підпрограм директиву компілятора $F зі знаком '+' (плюс), а наприкінці цієї групи - таку ж директиву зі знаком '-' (мінус), наприклад:

{$?+} function Add (a,b:real): real;

begin

Add:= a+b

end;

function Sub (a,b:real): real;

begin

Sub:= a-b

end;

{$F-}

Якщо необхідно поширити дію цієї директиви на всю програму, досить помістити одну директиву {$F+} на самому початку тексту.

Еквівалентом директиви компілятора {$F+} є, службове слово far, що, будучи записаним перед блоком підпрограми, задає для неї далекий тип виклику. (Інший можливий тип виклику - "ближній" - задається службовим словом near у тім же місці опису підпрограми). Таким чином, перша процедура з попереднього приклада може бути записана в наступному виді:

function Add (a,b:real): real; far;

begin

Add:= a+b

end;

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

var

Func: function (S:string): byte;

function MyLength (S:string): byte; far;

begin.

MyLength:= Length(S)

end;

Func:= MyLength;

3. Важливим обмеженням є те, що обговорювані підпрограми НЕ МОЖУТЬ БУТИ вкладеними в інші підпрограми.

4. Нарешті, підпрограми, що присвоюються процедурним змінним, не можуть бути підпрограмами спеціального виду, що містять специфікацію interrupt і конструкцію inline.

Зазначені обмеження можуть стати трохи більш зрозумілими, якщо мати на увазі, що на фізичному рівні змінна процедурного типу містить повну адресу початку коду підпрограми в пам'яті (сегмент і зсув). Фактично, процедурна змінна дуже нагадує змінну посилкового типу, тільки замість покажчика на данні вона містить покажчик на код підпрограми. Процедурна змінна завжди займає в пам'яті 4 байти (два слова), причому в першому слові зберігається зсув, у другому - сегмент.

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

program Tables;

type

Func = function (x,y:integer): integer;

function Add (a,b:integer): integer; far;

begin

Add:= a+b

end;

function Mult (a,b:integer): integer; far;

begin

Mult:= a*b

end;

procedure MakeTable (W,H:integer; Operation:Func);

var

i, j: integer;

begin

{ Формуємо заголовок }

Write (' ':6);

for i:=1 to W do Write(i:5);

WriteLn;

Write(' ':6);

for i:=1 to W do Write('-----');

WriteLn;

{ Проводимо обчислення }

for i:=1 to H do

begin

Write(i:5,'|');

for j:=1 to W do Write (Operation(j,i):5);

WriteLn

end;

WriteLn

end;

begin { MakeTable }

MakeTable(10,10,Add);

MakeTable(10,10,Mult)

end.

 

Тіло програми Tables містить два виклики процедури MakeTable, що будує таблиці, використовуючи для обчислень у першому випадку функцію Add (одержуючи таблицю додавання 10х10), а в другому випадку - функцію Mult (одержуючи таблицю множення 10х10).

Необхідно нагадати, що хоча змінні процедурних типів можна передавати як параметри підпрограмам, функції, що повертають значення процедурних типів, НЕ ДОПУСКАЮТЬСЯ.

У загальному випадку використання процедурної змінної в операторі чи виразі означає виклик присвоєної даної змінної процедури чи функції. Однак, тут мається одне виключення: якщо в лівій частині оператора присвоювання стоїть процедурна змінна, права його частина повинна представляти ідентифікатор іншої процедурної змінної чи ідентифікатор підпрограми. Розглянемо, наприклад, таку програму:

type

IntFunc = function: integer;

var

F: IntFunc;

N: integer;

function Readint: integer; far;

var

i: integer;




Поделиться с друзьями:


Дата добавления: 2014-12-16; Просмотров: 1802; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.013 сек.