Структуровані типи даних
Будь-який із структурованих типів даних характеризується множинністю елементів, які складають цей тип. Змінна або константа структурованого типу завжди має декілька компонент. Кожна з цих компонент, у свою чергу, може належати структурованому типу, що дозволяє говорити про можливу вкладеність типів.
Турбо Паскаль має п’ять структурованих типів:
- масиви;
- рядки;
- множини;
- записи;
- файли;
Однак, перш ніж приступити до їх вивчення, нам треба розглянути ще два типи даних - перелічуваний та інтервальний, що відносяться до порядкових типів і не розглядались нами раніше, проте будуть потрібні при вивченні нового матеріалу.
Перелічуваний тип даних
Перелічуваний тип являє собою обмежену упорядковану послідовність скалярних констант, що становлять даний тип. Значення кожної константи задається її ім'ям. Імена окремих констант відокремлюються один від одного комами, а вся сукупність констант, що становлять даний перелічуваний тип, обмежується круглими дужками.
Програміст об'єднує в одну групу у відповідності з якою-небудь ознакою всю сукупність значень, що складають перелічуваний тип. Наприклад, перелічуваний тип Rainbow (ВЕСЕЛКА) об'єднує скалярні значення RED, ORANGE, YELLOW, GREEN, LIGHT_BLUE, BLUE, VIOLET (червоний, оранжевий, жовтий, зелений, блакитний, синій, фіолетовий). Перелічуваний тип Traffic_Light (СВІТЛОФОР) об'єднує скалярні значення RED, YELLOW, GREEN (ЧЕРВОНИЙ, ЖОВТИЙ, ЗЕЛЕНИЙ).
Перелічуваний тип описується в розділі опису типів, наприклад:
type Rainbow = (RED, ORANGE, YELLOW, GREEN, LIGHT_BLUE, BLUE, VIOLET);
Кожне значення є константою свого типу і може належати тільки одному з перелічуваних типів, заданих в програмі. Наприклад, перелічуваний тип Traffic_Light не може бути визначений в одній програмі з типом Rainbow, так як обидва типи містять однакові константи.
Опис змінних, що належать до скалярних типам, які оголошені в розділі опису типів, проводиться за допомогою імен типів. Наприклад:
type Traffic_Light= (RED, YELLOW, GREEN);
var Section: Traffic_Light;
Це означає, що змінна Section може приймати значення RED, YELLOW або GREEN.
Змінні перелічуваного типу можуть бути описані в розділі опису змінних, наприклад:
var Section: (RED, YELLOW, GREEN);
При цьому імена типів відсутні, а змінні визначаються сукупністю значень, складових даний перелічуваний тип.
До змінних перелічуваного типу може бути застосовний оператор присвоєння:
Section: = YELLOW;
Упорядкована послідовність значень, які складають перелічуваний тип, автоматично нумерується, починаючи з нуля і далі через одиницю. Звідси випливає, що до змінних, що перераховуються і констант можуть бути застосовані операції відношення і стандартні функції Pred, Succ, Ord.
Інтервальний тип даних
Відрізок (діапазон значень) будь-якого порядкового типу може бути визначений як інтервальний (обмежений) тип. Відрізок задається діапазоном від мінімального до максимального значення констант, розділених двома крапками. В якості констант можуть бути використані константи, що належать до цілого, символьного, логічного або перелічуваного типу. Скалярний тип, на якому будується відрізок, називається базовим типом. Приклади відрізків:
1 .. 10
-15 .. 25
'A' .. 'z'
Мінімальне і максимальне значення констант називаються нижньою і верхньою межею відрізка, що визначає інтервальний тип. Нижня межа повинна бути менше верхньої.
Над змінними, що відносяться до інтервального типу, можуть виконуватися всі операції і застосовуватися всі стандартні функції, які допускаються до використання для відповідного базового типу.
Масиви
Масиви - це сукупності однотипних елементів. Вони мають наступні характеристики:
- кожен компонент масиву може бути явно позначений і до нього є прямий доступ;
- число компонентів масиву визначається при його описі і надалі не міняється.
Для позначення компоненту масиву використовується ім'я змінної-масиву і так звані індекси, які вказують на певний елемент. Тип індексу може бути тільки порядковим (крім longint). Найчастіше використовується інтервальний тип (діапазон).
Опис типу масиву задається наступним чином:
type ім'я типу = array[ список індексів ] of тип
Тут ім'я типу - правильний ідентифікатор; ,список індексів - список одного або декількох індексних типів, розділених комами; тип - будь-який тип даних.
Вводити і виводити масиви можна тільки по одному елементу за раз.
Приклад 1. Введення і виведення одновимірного масиву.
const n = 5; type mas = array[1..n] of integer; var a: mas; i: byte; begin writeln('введіть елементи масиву'); for i:=1 to n do readln(a[i]); writeln('вивід елементів масиву:'); for i:=1 to n do write(a[i]:5); end.
Визначити змінну як масив можна і безпосередньо при її описі, без попереднього опису типу масиву, наприклад:
var a,b,c: array[1..10] of integer;
Якщо масиви a і b описані як:
var a = array[1 .. 5] of integer; b = array[1 .. 5] of integer;
то змінні a і b будуть розрізнятись як змінні різних типів. Для забезпечення сумісності описуйте змінні через попередній опис типу.
Якщо типи масивів ідентичні, то в програмі один масив може бути привласнений іншим. У цьому випадку значення всіх змінних одного масиву буде надано відповідним елементам другого масиву.
Разом з тим, над масивами не визначені операції відношення. Порівнювати можна лише окремі елементи масивів.
Так як тип, що йде за ключовим словом of в описі масиву, - будь-який тип Турбо Паскаль, то він може бути і іншим масивом. Наприклад:
type mas = array[1 .. 5] of array[1 .. 10] of integer;
Такий запис можна замінити більш компактним:
type mas = array [1 .. 5, 1 .. 10] of integer;
Таким чином виникає поняття багатовимірного масиву. Глибина вкладеності масивів довільна, тому кількість елементів у списку індексних типів (розмірність масиву) не обмежена, однак не може бути більше 65520 байт.
Робота з багатовимірними масивами майже завжди пов'язана з організацією вкладених циклів. Таким чином, щоб заповнити двовимірний масив (матрицю) випадковими числами, використовують конструкцію виду:
for i:=1 to m do
for j:=1 to n do a[i,j]:=random(10);
Для "гарного" виводу матриці на екран використовуйте такий цикл:
for i:=1 to m do begin for j:=1 to n do write(a[i,j]:5); writeln; end;
Рядки
Рядковий тип даних
Для обробки інформації, поданої в рядковому вигляді, в Турбо Паскаль введений рядковий тип даних. Рядком у Паскалі називається певна послідовність символів. Кількість символів послідовності називається довжиною рядка. Синтаксис:
var s: string[n];,
var s: string;
n - максимально можлива довжина рядка - ціле число в діапазоні від 1 до 255. Якщо цей параметр опущений, то за умовчанням він приймається рівним 255.
Рядкові константи записуються як послідовності символів, обмежені апострофами. Допускається формування рядків з використанням десяткових кодів для запису символів (у вигляді комбінації # і коду символу) і керуючих символів (комбінації ^ і деяких великих латинських літер).
Приклад:
'Текстовий рядок'
#54#32#61
'Abcde'^A^M
Для позначення пробілу використовуються два апострофи, що стоять поспіль. Якщо апостроф входить в рядок як літера, то при записі він подвоюється.
Змінні, що описані як рядкові і мають різну довжину можна привласнювати одна одній. Варто зазначити, що при спробі привласнити більш короткій змінній довшу зайві символи будуть відкинуті.
Вирази типу char можна привласнювати будь-яким рядковим змінним.
У Турбо Паскаль є простий доступ до окремих символів рядковий змінної: запис st[i] варто розуміти як елемент масиву st з індексом i. Наприклад, якщо st - це 'Рядок', то st [1] - це 'Р', st [2] - це 'я', st [3] - 'д' і так далі.
Над рядковими даними визначена операція злиття (конкатенації), що позначається знаком +. Наприклад:
a: = 'Turbo';
b: = 'Pascal';
c: = a + b;
У цьому прикладі змінна c набуде значення 'TurboPascal'.
Крім злиття над рядками визначені операції порівняння <,>, =, <>, <=, >=. Два рядки порівнюються по символу за раз, зліва направо, по кодам символів. Якщо один рядок менше іншого по довжині, то бракуючі символи коротшого рядка будуть замінені символом з кодом 0.
Процедури і функції для роботи з рядками
В системі Turbo Pascal є декілька корисних стандартних процедур і функцій, орієнтованих на роботу з рядками. Нижче наводиться список цих процедур і функцій з короткими поясненнями.
Length(s:string):integer
Функція повертає в якості результату значення поточної довжини рядка-параметра
Приклад.
n := length('Pascal'); {n буде дорівнювати 6} Concat(s1,[s2,...,sn]:string):string
Функція виконує злиття рядків-параметрів, яких може бути довільна кількість. Кожен параметр є виразом рядкового типу. Якщо довжина рядка-результату перевищує 255 символів, то вона утинається до 255 символів. Ця функція еквівалентна операції конкатенації "+" і працює трохи менш ефективно, на відміну від неї.
Copy(s:string; index:integer; count:integer):string
Функція повертає підрядок, виділений з вихідного рядку s, довжиною count символів, починаючи з символу під номером index.
Приклад.
s := 'Система Turbo Pascal';
s2 := copy(s, 1, 7); {s2 буде дорівнювати 'Система'}
s3 := copy(s, 9, 5); {s3 буде дорівнювати 'Turbo'}
s4 := copy(s, 15, 6); {s4 буде дорівнювати 'Pascal'}
Delete(var s:string; index,count:integer)
Процедура видаляє з рядка-параметра s підрядок довжиною count символів, починаючи з символу під номером index.
Приклад.
s := 'Система Turbo Pascal';
delete(s,8,6); {s буде дорівнювати 'Система Pascal'}
Insert(source:string; var s:string;index:integer)
Процедура призначена для вставки рядка source в рядок s, починаючи з символу index цього рядка.
Приклад.
s := 'Система Pascal';
insert('Turbo ',s,9); {s буде дорівнювати 'Система Turbo Pascal'}
Pos(substr,s:string):byte
Функція здійснює пошук в рядку s підрядка substr. Результатом функції є номер першої позиціїпідрядка в заданому рядку. Якщо підрядок не знайдено, то функція повертає 0.
Приклад.
s := 'Система Turbo Pascal';
x1 := pos('Pascal', s); {x1 буде дорівнювати 15}
x2 := pos('Basic', s); {x2 буде дорівнювати 0}
Str(X: арифметичний вираз; var st: string)
Процедура перетворює чисельне вираження X в його рядкове представлення і поміщає результат в st.
Val(st: string; x: числова змінна; var code: integer)
Процедура перетворює рядковий запис числа, що міститься в st, в числове подання, поміщаючи результат в x. x - може бути як цілою, так і дійсною змінною. Якщо в st зустрічається неприпустимий (з точки зору правил запису чисел) символ, то перетворення не відбувається, а в code записується позиція першого неприпустимого символу. Виконання програми при цьому не переривається, діагностика не видається. Якщо після виконання процедури code дорівнює 0, то це свідчить про вдало завершену операцію.
На додаток наведемо деякі функції, пов'язані з типом char, але які тим не менш часто використовуються при роботі з рядками.
Chr (n: byte): char
Функція повертає символ, код якого дорівнює виразу n. Якщо n можна представити як числову константу, то можна також використовувати запис # n.
Ord (ch: char): byte;
В даному випадку функція повертає код символу ch.
UpCase (c: char): char;
Якщо c - мала латинська буква рядкового типу, то функція повертає відповідну малу латинську літеру, в іншому випадку символ c повертається без зміни.
Множини
Поняття множини в мові Паскаль ґрунтується на математичному баченні поняття про кінцеві множини: це обмежена сукупність різних елементів. Для конкретного множинного типу використовується перелічуваний або інтервальний тип даних. Тип елементів, складових безлічі, називається базовим типом.
Множинний тип описується за допомогою службових слів Set of, наприклад:
type M = Set of B;
Тут M – множинний тип, B – базовий тип.
Приклад опису змінної множинного типу.
type M = Set of 'A'..'D'; var MS: M;
Приналежність змінних до множинного типу може бути визначена прямо в розділі опису змінних:
var C: Set of 0..7;
Константи множинного типу записуються у вигляді обмеженої квадратними дужками послідовності елементів чи інтервалів базового типу, розділених комами, наприклад:
['A', 'C'] [0, 2, 7] [3, 7, 11..14]
Константа виду [] означає порожня підмножину. Кількість базових елементів не повинна перевищувати 256. Ініціалізація величин множинного типу може проводитись за допомогою типізованих констант:
const seLit: Set of 'A'..'D'= [];
Порядок перерахування елементів базового типу в константах не грає ролі.
Значення змінної множинного типу може бути задане конструкцією виду [T], де T - змінна базового типу. Наприклад, цілком допустима конструкція:
type T = set of char;
Множина містить у собі набір елементів базового типу, усі підмножини даної множини, а також порожню підмножину. Так, змінна Т множинного типу
var T: Set of 1 .. 3;
може приймати вісім різних значень:
[] [1] [2] [3] [1,2] [1,3] [2,3] [1,2,3]
До змінних і констант множинного типу можна застосувати операції присвоювання (: =), об'єднання (+), перетину (*) і віднімання (-) множин:
['A', 'B'] + ['A', 'D'] дасть ['A', 'B', 'D']
['A', 'D'] * ['A', 'B', 'C'] дасть ['A']
['A', 'B', 'C'] - ['A', 'B'] дасть ['C'].
Результатом виконання цих операцій є величина множинного типу.
До множинних величин також можна застосовувати наступні операції: тотожність (=), нетотожність (<>), міститься в (<=), містить (> =). Результат виконання цих операцій має логічний тип, наприклад:
['A', 'B'] = ['A', 'C'] дасть FALSE
['A', 'B'] <> ['A', 'C'] дасть TRUE
['B'] <= ['B', 'C'] дасть TRUE
['C', 'D'] >= ['A'] дасть FALSE.
Крім цих операцій для роботи з величинами множинного типу в мові ПАСКАЛЬ використовується операція in, вона перевіряє належність елемента базового типу, який стоїть ліворуч від знаку операції, до множини, що стоїть праворуч від знака операції. Результат виконання цієї операції – булевий. Операція перевірки належності елемента до множини часто використовується замість операцій відношення, наприклад:
'A' in ['A', 'B'] дасть TRUE,
2 in [1, 3, 6] дасть FALSE.
Записи
Запис являє собою сукупність обмеженого числа логічно зв'язаних компонент, що належать до різних типів. Компоненти запису називаються полями, кожне з яких визначається ім'ям. Поле запису містить ім'я поля, після якого, через двокрапку, вказується тип цього поля. Поля запису можуть відноситися до будь-якого типу, допустимого в мові Паскаль, за винятком файлового.
Опис запису в мові Паскаль здійснюється за допомогою службового слова record, за яким слідує опис компоненти запису. Завершується опис запису службовим словом end.
Наприклад, телефонний довідник містить прізвища і номери телефонів, тому окремий рядок в такому довіднику зручно представити у вигляді наступного запису:
type TRec = Record FIO: String[20]; TEL: String[7] end; var rec: TRec;
Описати записи можливо і без використання імені типу, наприклад:
var rec: Record FIO: String[20]; TEL: String[7] end;
Звернення до запису в цілому допускається тільки в операторах присвоєння, де ліворуч і праворуч від знака присвоювання використовуються імена записів однакового типу. У всіх інших випадках оперують окремими полями записів. Щоб звернутися до окремої компоненти запису, необхідно задати ім'я запису і через крапку вказати ім'я потрібного поля, наприклад:
rec.FIO, rec.TEL
Таке ім'я називається складеним. Компонентою запису може бути іще один запис, у такому випадку складене ім'я буде містити не два, а більшу кількість імен.
Звернення до компонентів записів можна спростити, якщо скористатися оператором приєднання with.
Він дозволяє замінити складені імена, що характеризують кожне поле на імена полів, а ім'я запису визначити в операторі приєднання:
with rec do оператор;
Тут rec - ім'я запису, оператор - оператор, простий або складений. Оператор являє собою область дії оператора приєднання, у межах якої можна не використовувати складені імена. Наприклад для нашого випадку:
with rec do begin FIO:='Козак С.В.'; TEL:='2324231'; end;
Така алгоритмічна конструкція повністю ідентична наступній:
rec.FIO:='Козак С.В.';
rec.TEL:='2324231';
Ініціалізація записів може проводитися за допомогою типізованих констант:
type RecType = Record x,y: Word; ch: Char; dim: Array[1..3] of Byte end; const Rec: RecType = ( x: 127; y: 255; ch: 'A'; dim: (2, 4, 8) );
Особливим різновидом записів є записи з варіантами, які оголошуються з використанням зарезервованого слова case. За допомогою записів з варіантами ви можете одночасно зберігати різні структури даних, які мають загальну спільну частину, що однакова у всіх структурах, але відрізняються деякими невеличкими частинами
Наприклад, побудуємо запис, в якому ми будемо зберігати дані про деяку геометричну фігуру (відрізок, трикутник, коло).
type TFigure = record type_of_figure: string[10]; color_of_figure: byte; ... case integer of 1: (x1,y1,x2,y2: integer); 2: (a1,a2,b1,b2,c1,c2: integer); 3: (x,y: integer; radius: word); end; var figure: TFigure;
Таким чином, у змінній figure ми можемо зберігати дані як про відрізок, так і про трикутник або коло. Треба лише в залежності від типу фігури звертатися до відповідних полів запису.
Зауважимо, що індивідуальні поля для кожного з типів фігур займають один адресний простір пам'яті, а це означає, що одночасне їх використання неможливе.
У будь-якому записі може бути тільки одна варіантна частина. Після закінчення варіантної частини в записі не можуть з'являтися ніякі інші поля. Імена полів повинні бути унікальними в межах того запису, де вони оголошені.