[ЗМІСТ]      [Далі]       [Назад]      [Початок розділу]

 

5.4. Символьний тип даних

 

      Серед задач, що вирішуються за допомогою ЕОМ, особливе місце належить задачам обробки текстів. До них, зокрема, відносяться багато задач системного програмування, пов'язані з підготовкою до виконання програм, представлених текстами в мовах програмування. Обробка текстів - істотна частина практично всіх автоматизованих систем.

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

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

декількох стандартів, найбільш відомими серед яких є стандарт Міжнародної організації по стандартизації ISO і його американська версія ASCII.

      Ми будемо вважати, що символьний тип, визначимо його Ch (від слова character - символ) складається з m різних значень ¦Ch¦ = m. Якщо m < 255, то для кодування символьних значень досить одного байта. Оскільки символи, що складають множину Ch, самі будуть зустрічатися в текстах, в тому числі в текстах алгоритмів, виникає необхідність їх явного виділення. Наприклад, один і той же текст а <- b міг би означати як присвоєння змінній а значення змінної b, так і присвоєння змінній а символа b (другої букви латинського алфавіта). Можна було б запропонувати безліч способів розрізнення символа, що позначає змінну b, від самого символа b, наприклад, використати різні засоби зображення - курсив b, напівгрубий b або прямий b шрифт. Загальноприйнятим є рішення, висхідне до традицій прямої мови: символи, що складають множину Ch, при написанні беруться в лапки - одинарні або подвійні.

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

      Нехай Ch - множина, що містить всі великі латинські літери

 

      'A', 'B', 'C', ..., 'Z' Î Ch,                   (5.18)

арабські цифри

 

      '0', '1', '2', ..., '9' Î Ch,             (5.19)

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

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

      chr: N -> Ch,

область визначення якої dom(chr) - це деяка обмежена множина натуральних чисел Nm. Згідно більшості діючих в цей час стандартів m = 255. Будемо говорити, що функція chr дає символ за його номером.

 

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

 

    алгоритм Liter це

       змін I: нат;

    поч

       для I<-0 до 255 повт

          показати (chr(I))

       кц

    ка.

 

      Зауваження 5.9. Згідно з поширеними стандартами символи з нульового по 31-й не друкуються. Вони мають спеціальне призначення. Тому при підготовці програми за алгоритмом з прикладу 5.5 для виконання на ЕОМ константу 0 в заголовку циклу треба замінити константою 32.¦

 

      Якщо ¦Ch¦ = m, то функція нумерації

            ord: Ch -> Nm,

- обернена до функції chr:

 

      chr(ord(S)) = S,  S Î Ch;                       (5.20)

      ord(chr(I)) = I,  I Î Nm.                       (5.21)

 

      Співвідношення  (5.20) та (5.21) дозволяють перенести на Ch відношення, задані на Nm, додавши тим самим точне значення відносшеням рівності та нерівностей

 

      S w Т = ord(S) w ord(Т) (S, Т Î Ch);            (5.22)

 

де w одна з операцій відношення (w Î {=,  <>,  <,  <=,  >,  >=}). Використовуючи функцію нумерації ord, визначимо на Ch дві функції

- слідування succ

 

       succ(S) = якщо ord(S) < m то chr(ord(S)+1)

                 інакше відмова;

- передування pred

 

       pred(S) = якщо ord(S) > 0 то chr(ord(S)-)(1)

                 інакше відмова.

 

Символ succ(S) будемо називати символом, наступним за S, а символ pred(S) - попереднім.

      Умови (5.18) і (5.19) посилимо таким чином

 

    succ('A') = 'B',  succ('B') = 'C', ..., succ('Y') = 'Z'; (5.18')

    succ('0') = '1',  succ('1') = '2', ..., succ('8') = '9'. (5.19')

 

      Приклад 5.6. З урахуванням (5.18') отримаємо алгоритм виведення літер в алфавітному порядку.

 

    алгоритм ABC це

       змін I: нат;

    поч

       для I<-ord('A') до ord('Z') повт

          показати (chr(I))

       кц

    ка.

 

      Символьним типом даних назвемо множину Ch, що складається з скінченнї кількості (звичайно - 256) символів, яка задовольняє умовам (5.18) та (5.19) і співпадає з областю значень функції chr. На множини Ch задані функції слідування succ і передування pred,

відношення {=, <>, <, <=, >, >=}, а також нумерація ord.¦

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

      Домовимося вважати цикл

 

       для s<-s0  до s1  повт

          Р(s)

       кц,

де s - змінна типу симв, скороченням для циклу

 

       для I<-ord(s0 ) до ord(s1 ) повт

          Р(chr(I))

       кц.

      Тоді цикл з прикладу 5.6 можна переписати у вигляді

 

          змін S: симв;

       поч

          для S<-' А' до 'Z' повт

             показати (S)

          кц.

 

      Приклад 5.7. Дано символи s1, s2,... Відомо, що символ s1 відмінний від символа '/' і що серед s2, s3,... є хоч би один символ '/'. Нехай s1,..., sn - символи даної послідовності, що передують першому символу '/' (n зазделегідь не відоме). Алгоритм підрахунку числа знаків оклику серед s1,..., sn.

 

    Алг Znak це

       змін C: симв; N: нат;

    поч

       N <- 0;

       цикл

          взяти (C);

          якщо C='/' то вихід;

          якщо C='!' то N <- N+1

       кц;

       показати (N)

    ка.

 

      Приклад 5.8. Алгоритм читання і обчислення значення виразу вигляду

 

      -17+3-5+1501;

 

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

      Враховуючи умову (5.19'), отримаємо

 

    Алгоритм Inout це

       конст Minus= '-'; Tz= ';';

             N0= '0'; N9= '9';

 

       змін Rez, Zn, M, K0: ціл;

            S: симв;

    поч

       взяти (S);

       Rez <- 0; K0 <- ord(N0);

       поки S<>Tz повт

          якщо S=Minus то Zn <- -1

          інакше Zn <- 1 кр;

          взяти (S); M <- 0;

          поки N0<=S & S<=N9 повт

             M <- 10*M+ord(S)-K0;

             взяти (S)

          кц; { S= '+' V '-' V ';' }

          Rez <- Rez+Zn*M

       кц;  { S= ';' }

       показати (' Сума= ', Rez)

    ка.

 

5.5. Програмування символьного типу

 

      Мова Паскаль. Множина значень Ch задана функцією chr

 

      Ch = { chr(k):  k Î N255}.

 

Символьні константи беруться в апострофи ', наприклад, 'A', '&'.

      Для опису даних символьного типу використовується ідентифікатор char:

 

       var Р, Q, R: char.

Кожна з величин Р, Q або R займає в пам'яті один байт.

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

      Над елементом з символьного типу визначені наступні функції:

 

       ord(c) - отримання номера символа c в кодовій таблиці даного

виконавця;

 

                 ¦ chr(ord(c)-1), ord(c) > 0,

       pred(c) = <

                 ¦ chr(255),      ord(c) = 0;

 

                 ¦ chr(ord(c)+1), ord(c) < 255,

       succ(c) = <

                 ¦ chr(0),        ord(c) = 255;

 

Примітка: Функції pred та succ дають відмову відповідно при значеннях аргумента 0 та 255, якщо встановлено опцію компілятора “Overflow checking”.

 

       upcase(c) - перетворення маленької літери у велику (визначена для латинських букв), наприклад, с:= 'a'; с:= upcase(с) => с = 'A'.

 

      Приклад 5P. Число знаків '!' (задача 5.7).

 

    program Znak;

       var С: char; N: byte;

    begin

       N:= 0; writeln(' Вводьте символи');

       while true do begin

          read(C);

          if C='/' then break;

          if C='!' then N:= N+1

       end;

       writeln;

       write(N, ' символів !')

    end.

 

      Приклад 6P. Обчислення значення числового виразу (задача 5.8).

 

    program Inout;

       const Minus= '-'; Tz= ';';

             N0= '0'; N9= '9';

       var Rez, Zn, M, K0: integer;

           S: char;

    begin

       writeln(' Вводьте вираз'); read(S);

       Rez:= 0; K0:= ord(N0);

       while S<>Tz do begin

          if S=Minus then Zn:= -1

          else Zn:= 1;

          read(S); M:= 0;

          while (N0<=S) and (S<=N9) do begin

             M:= 10*M+ord(S)-K0;

             read(S)

          end;

          Rez:= Rez+Zn*M

       end;

       writeln; write(' Сума= ', Rez)

    end.

 

      Мова Сі. Символьні константи беруться в апострофи ', наприклад, 'A', '&'. Символьні константи також можна задавати їх кодами у системі числення за основою 8 (або 16). Для цього в апострофах записують \ та вісімковий код символа, наприклад, ‘\0’ для символа з кодом 0 (або ‘\x0’, де x позначає, що код задано у системі числення за основою 16).

      Для опису даних символьного типу використовується ідентифікатор char:

 

       char p, q, r;

Кожна з величин p, q або r займає в пам'яті один байт.

Тип char у Сі відноситься до цілих типів, тобто для даних типу char визначено всі операції та відношення, які визначено для цілих чисел. Операції виконуються над кодами символів. Функції chr та ord не визначено. Натомість визначено наступні функції:

 

- бульові функції, що визначають, до якого класу належить символ

 

       isalnum(c) – с – літера (латинська) або цифра;

 

       isalpha(c) – с – літера (латинська);

 

       isdigit(c) – с – цифра;

 

       islower(c) – с – маленька літера (латинська);

 

       isupper(c) – с – велика літера (латинська);

 

- функції переведення літре до верхнього (нижнього) регістру

 

       tolower(c) – переводить с до нижнього регістру (для латинських літер);

 

       toupper(c) – переводить с до верхнього регістру (для латинських літер);

 

Примітка: Для використання цих функцій треба підключити бібліотеку ctype.h.

 

      Для введення та виведення даних символьного типу у scanf та printf використовується елемент формату %c. До того ж є спеціальні функції введення та виведення символів: getchar та putchar.

 

      c = getchar(); - взяти символ;

      putchar(c); - показати символ.

 

      Приклад 5C.

 

#include <stdio.h>

 

/* Znak */

main()

{

  char c;

  unsigned n;

 

  n = 0; printf(Вводьте символи);

  while (1)

    {

     c=getchar();

     if (c=='/') break;

     if (c=='!') n++;

    }

  printf(“\n%u символів !\n”, n);

}

 

      Приклад 6C.

 

#include <stdio.h>

 

#define Minus ‘-‘

#define Tz ‘;‘

#define N0 ‘0‘

#define N9 ‘9‘

 

/* Inout */

main()

{

  char s;

  int rez, zn, m;

 

  printf(Вводьте вираз);

  s = getchar();

  rez = 0;

  while (s!=Tz)

    {

     if (s==Minus) zn = -1;

     else zn = 1;

     s = getchar(); m = 0;

     while (N0<=s && s<=N9)

       {

        m = 10*m+(s-N0);

        s = getchar();

       }

     rez += zn*m;

    }

  printf(“\n Сума= %d”, rez);

}

 

 

5.6. Типи, що визначаються

 

      Структуру символьного типу можна узагальнити на випадок довільної скінченної множини значень. Будемо вважати, що визначення

 

      тип Т = (c1, c2,..., cn )                              (5.23)

 

задає множину значень, на якій визначені операції:

- нумерація ord

      ord(ck) = k-1, k=1,2,..., n,                    (5.24)

 

- слідування succ

      succ(ck ) = ck+1, k=1,2,..., n-1                  (5.25)

 

і передування pred

      pred(ck ) = ck-1, k=2,3,..., n;                   (5.26)

 

а також стандартний набір відношень " r, s Î Т:

      r w s = ord(r) w ord(s),                        (5.27)

 

де w Î {=, <>, >, >=, <, <=}.

      Множину Т що визначається виразом (5.23) разом із заданими на ній функціями (5.24)-(5.26) і відношеннями (5.27) назвемо типом перерахування, а сам спосіб її визначення - перерахуванням.

      Об'єкти ck могли б бути будь-чим, однак, наслідуючи сталу традицію, будемо вважати, що ck - це ідентифікатори.

      Будемо вважати, що перерахування (5.23) розширює виконавець, приєднуючи до нього новий операційний пристрій Т, здатний зберігати константи c1, c2,..., cn, обчислювати  функції та відношення (5.24)-(5.27), а також виконувати введення - виведення та присвоєння значень типу Т змінним відповідного типу.

      Нехай Т – тип перерахування вигляду (5.23); a, b Î Т і а<b; х - змінна, що приймає значення типу Т; Р - інструкція. Тоді запис

 

       для х<-a до b повт

          Р

       кц

 

будемо вважати скороченим записом наступного циклу

 

       х <- a;

       цикл

          Р;

          якщо х=b то вихід;

          х <- succ(х)

       кц.

 

      Приклад 5.9. Алгоритм виведення тижня.

 

    алгоритм D_W це

       тип Тиждень= (пн, вв, ср, чт, пт, сб, нд);

       змін Day: Тиждень;

    поч

       для Day<-пн до нд повт

          показати (Day)

       кц

    ка.

 

      Розглянемо одне узагальнення інструкції розгалуження (IF), яке особливо корисно при роботі з даними типу перерахування. Нехай Т - деякий простий тип за винятком дійсного; х - вираз, що приймає значення типу Т; а1, а2,..., аk - деякі різні константи типу Т; Р1, Р2,..., Рk, Q - інструкції. Тоді команда

 

                     -----------------

                     ¦ вибір х із    ¦

                     ¦   ¦а:  Р;     ¦

                     ¦     1   1     ¦

                     ¦   ¦а:  Р;     ¦

                     ¦     2   2     ¦                     (Cs)

                     ¦     ....      ¦

                     ¦   ¦а:  Р;     ¦

                     ¦     k   k     ¦

                     ¦   інакше Q    ¦

                     ¦ кінець_вибору ¦

                     L----------------

 

називається інструкцією вибору. Конструкція інакше Q є необов’язковою. Службові слова кінець_вибору будемо скорочено писати кв.

 

Правило виконання вибору:

    ¦     Команда вибору виконується в два етапи.

    ¦     1) Обчислюється значення виразу х, який називають

    ¦ селектором.

    ¦     2) Значення селектора порівнюється з константами аi, i<=k.

    ¦     Якщо деяка константа вибору аi рівна поточному значенню

    ¦ селектора х, то виконується інструкція Рi. Потім управління

    ¦ передається команді, що безпосередньо слідує за командою (Cs).

    ¦     У тому випадку, коли жодна з констант аi не рівна значенню

    ¦ х, виконується інструкція Q і команда (Cs) завершує роботу.

    ¦ Якщо конструкція інакше Q відсутня, то виконується команда,

    ¦ наступна за (Cs).

    L---------------------------------------------------------------

 

Якщо одна і та ж інструкція Рi повинна виконуватися для різних констант аi, аj,..., аn, то допускається скорочення

 

       вибір х із

....            

         ¦а , а ,..., а :  Р ;

           i   j       n    i

           ....

       кв.

 

      Наприклад, нехай є тип

 

       тип Operator= (min, plu, mul, dvd)

і змінна r має тип Operator. Тоді можна записати команду

 

       вибір r із

         ¦plu: z <- х+у;

         ¦min: z <- x-y;

         ¦mul: z <- х*у;

         ¦dvd: z <- х/у

       кв.

 

      З правила виконання інструкції вибору випливає справедливість такої властивості:

      Властивість 5.5. Інструкція (Cs) рівносильна каскадному розгалуженню

 

       якщо х  то Р

               1     1

       інякщо х  то Р

                 2     2

       ................

       інякщо х  то Р

                 k     k

       інакше Q

       кр

 

      Очевидно, розгалуження

 

       якщо F то Р інакше Q кр

 

рівносильно такій команді вибору

 

       вибір F із

         ¦Іст: Р;

         ¦Хиб: Q

       кв

 

або наступній

 

       вибір F із

         ¦Іст: Р;

         інакше Q

       кв,

 

а захищену інструкцію

 

       якщо F то Р кр

 

можна записати як

 

       вибір F із

         ¦Іст: Р;

         ¦Хиб:

       кв

або

       вибір F із

         ¦Іст: Р

       кв.

 

      Інший спосіб визначення нових типів полягає у виділенні підмножини значень і обмеженні на виділеній підмножині функцій та відношень.

      Нехай Т – тип перерахування з функцією слідування succ; a, b Î Т і а<b. Обмеженням Т0 базового типу Т назвемо тип, що задано перерахуванням

 

      тип Т0 = (a, succ(a), ..., b),

 

який ми будемо записувати як

 

       тип Т0 = a..b;

      Наприклад, описом

 

      тип Роб_Тиждень = пн..пт;

задане очевидне обмеження типу Тиждень, розглянутого вище.

      Обмеження Т0 успадковує всі функції та відношення, задані на базовому типі T. Нехай f - функція на Т, тоді її обмеження на Т0 - це функція f0, задана визначенням

 

      f0 (х)= якщо f(х) Î Т0 то f(х) інакше відмова

" х Î Т0.

 

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

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

      Користуючись базовим типом Zm, натуральний тип Nm природно визначається як обмеження

 

      тип нат= 0..m;

 

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

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

 

                                  ----------------

                                  ¦              ¦

          ABRZNChT ...T           ¦ С + IF + Lw  ¦

                  1    k          ¦              ¦

                                  L---------------

                                   ^ ^ ^ ^

        ---------------------------- ¦ ¦ ¦

        ¦             ---------------- ¦ ¦

        ¦             ¦             ---- L---------

    ----¦------------ ¦-------------¦-------------¦-----------

    ¦   ¦   ----------v----------   ¦    ---------v--------- ¦

    ¦ --v-- ¦-----  -----  -----¦ --v--- ¦------     ------¦ ¦

    ¦ ¦   ¦ ¦¦   ¦  ¦   ¦  ¦   ¦¦ ¦    ¦ ¦¦    ¦     ¦    ¦¦ ¦

    ¦ ¦ B ¦ ¦¦ N ¦  ¦ Z ¦  ¦ R ¦¦ ¦ Ch ¦ ¦¦ Т  ¦ ... ¦ Т  ¦¦ ¦

    ¦ L---- ¦L----  L----  L----¦ L----- ¦L--1--     L--k--¦ ¦

    ¦       L--------------------        L------------------ ¦

    L-------------------------------ОП------------------------

 

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

 

      Приклад 5.10. Визначення дня тижня в деякому році з інтервалу [1920,2099] за його датою, знаючи, що 1 січня 1920 року - четвер.

      При складанні алгоритму будемо виходити з того факту, що, якщо 1 січня якогось року - понеділок, то для визначення дня тижня в цьому році за довільною датою необхідно проаналізувати остачу від ділення дня в році мінус одиниця на 7. Якщо остача рівна 0, то задана дата потрапляє на понеділок; якщо остача рівна одиниці, то - на вівторок і т.д. Нарешті, якщо остача рівна 6, то дата потрапляє на неділю. Таким чином, за датою необхідно обчислити день року з інтервалу [1,356], відняти 1 і знайти остачу відділення на 7.

      Якщо ж 1 січня - не понеділок, то остача від ділення вказує нам, на скільки днів необхідно циклічно зсунутись відносно дня Нового року. Наприклад, нехай 1 січня - п'ятниця. Тоді 8 січня (залишок 0) - це п’ятниця, 9 січня (залишок 1) потрапляє на суботу, 10 січня (залишок 2) - на неділю і т.д.

      Кожний невисокосний рік має 365 днів та зсуває день тижня 1 січня на одиницю (остача від ділення 365 на 7 дорівнює 1). Високосний рік має 366 днів та зсуває день тижня першого січня на 2. Отже отримати зсув дня тижня деякого року Y у інтервалі [1920,2099] відносно 1 січня 1920 року можна, порахувавши різницю між Y та 1920 роком та додавши кількість високосних років KV, що обчислюється за формулою:

 

      KV = якщо Y=1920 то 0 інакше ((Y - 1) - 1920) div 4 + 1;

 

      Враховуючи дані міркування можемо скласти наступний

 

    Алгоритм Day_Week це

       тип Тиждень= (пн, вв, ср, чт, пт, сб, нд);

           Місяць= (січ, лют, бер, кві, тра, чер, лип, сер, вер, жов, лис, гру);

           Роки= 1920..2099;

       змін  Day: 1..366; Disp, i: 0..6;

             Num, N_D: 1..31;

             Mon, М: Місяць;

             Year: Роки;

             D_W: Тиждень;

             KV: нат;

    поч

    { Введення дати }

       показати (' Рік= '); взяти (Year);

       показати (' Місяць= '); взятиon);

       вибір Мon із        { Число днів в Мon }

         ¦кві, чер, вер, лис: N_D <- 30;

         ¦лют:                якщо Year mod 4=0 то N_D <- 29

                              інакше N_D <- 28 кр

         інакше N_D <- 31

       кв;

       повт

          показати (' Число= '); взяти (Num);

          якщо Num>N_D то показати (' Помилка! Невірне число') кр

       до Num <= N_D;

    { День року }

       Day <- Num;

       якщо Мon>січ то

          для М<-січ до pred(Мon) повт

             вибір М із

               ¦кві, чер, вер, лис: Day <- Day + 30;

               ¦лют: якщо Year mod 4=0 то Day <- Day + 29

                     інакше Day <- Day + 28 кр

               інакше Day <- Day + 31

             кв;

          кц

       кр;

    { Зсув дня тижня відносно 1 січня 1920 року}

       якщо Year = 1920 то KV <- 0

       інакше KV <- ((Year - 1) - 1920) div 4 + 1 кр;

       Disp <- ((Year-1920)+KV+(Day-1)) mod 7;

    { День тижня }

       D_W <- чт;

       для i<-1 до Disp повт

          якщо D_W=нд то D_W <- пн

          інакше D_W <- succ(D_W) кр

       кц;

       показати (D_W)

    ка.

 

5.7. Програмування типів, що визначаються

 

5.7.1. Тип перерахування

 

      Мова Паскаль. Визначення типу перерахування Т має вигляд

 

      type Т= (c1, c2,..., cn );

 

де Т - ім'я типу, ci  - ідентифікатор константи типу Т, i= 1,...,n.

Наприклад,

 

       type Operator= (plus, minus, multi, divide);

            Week= (mon, tue, wed, thu, fri, sat, sun);

       var  Day: Week; Op1, Op2, Op3: Operator;

 

      Дані типу перерахування займають в пам'яті один байт, тому будь-який тип містить не більше за 256 елементів.

      Величини типу перерахування Т не можуть фігурувати в командах введення і виведення. Над ними визначені операції відношення {<, <=, =, <>, >, >=}, функції нумерації ord, слідування succ та передування pred. Окрім цих функцій у Паскалі для кожного типу перерахування T визначена також функція, обернена до функції нумерації ord. Ім’я цієї функції співпадає з імям типу T. Нприклад,

 

      week(1)=tue,

      operator(2)=multi.

 

      Інструкція вибору (Cs) кодується таким чином:

 

       case х of

          а:  begin Pas(Р ) end;

           1             1

          а:  begin Pas(Р ) end;

           2             2

....            

          а:  begin Pas(Р ) end;

           k             k

          else begin Pas(Q) end

       end.

 

      Приклад 7P. Програма друку даних типу перерахування (задача 5.9).

 

    program Pech;

       type Week=(Mon, Tue, Wed, Thu, Fri, Sat, Sun);

       var D: Week;

    begin

       for D:=Mon to Sun do begin

          write(ord(D),'-');

          case D of

             Mon: writeln('понеділок');

             Tue: writeln('вівторок');

             Wed: writeln('середа');

             Thu: writeln('четвер');

             Fri: writeln('п''ятниця');

             Sat: writeln('субота');

             Sun: writeln('неділя')

          end

       end

    end.

 

      У мові Сі тип перерахування визначається так:

 

      typedef enum {c1, c2,..., cn} T;

 

Визначення типу, як правило, вказують перед початком програми (main).

Наприклад,

 

       typedef enum {plus, minus, multi, divide} operator;

       typedef enum {mon, tue, wed, thu, fri, sat, sun} week;

            ...

       week day;

       operator op1, op2, op3;

 

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

Наприклад,

 

       typedef enum {plus=1, minus=2, multi=4, divide=8} operator;

       typedef enum {jan=1, feb, mar, apr, may, jun, jul, aug, sep,

                     oct, nov, dec} month;

 

Для типу month значення jan=1, feb=2 тощо.

Реалізація інструкції вибору (Cs) має наступний вигляд:

 

  switch (х)

    {

     case а :  C(Р ) break;

           1      1

     case а :  C(Р ) break;

           2      2

       ...

     case а :  C(Р ) break;

           k      k

    case else C(Q )

    }.

 

Оператор break призначено для завершення виконання інструкції вибору після виконання команд, що відповідають певній альтернативі. Якщо ж після виконання C(Pi) не ставити break, то далі буде виконуватись C(Pi+1).

 

      Приклад 7C. Програма друку даних типу перерахування (задача 5.9).

 

#include <stdio.h>

 

/* Pech */

 

typedef enum {mon, tue, wed, thu, fri, sat, sun} week;

 

main()

{

  week d;

 

  for (d=mon; d<=sun; d++)

    {

     printf("%d-",d);

     switch (d)

       {

        case mon: printf("понеділок\n"); break;

        case tue: printf("вівторок\n"); break;

       case wed: printf("середа\n"); break;

       case thu: printf("четвер\n"); break;

       case fri: printf("п'ятниця\n"); break;

       case sat: printf("субота\n"); break;

       case sun: printf("неділя\n"); break;

       }

    }

}

 

5.7.2. Обмеження

 

      У мові Паскаль будь-який з простих типів, в тому числі, заданий перерахуванням, за винятком дійсного типу, можна обмежити, виділивши діапазон значень. Нехай Т - базовий тип, a, b - константи типу Т, а<b. Визначення обмеження Т0 має вигляд

 

      type Т0 = a..b;

 

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

      Приклади типів:

 

       type NAT = 0..maxint;

            LET = 'a'..'z';

            NUM = '0'..'9';

            WORKDAY = Mon..Fri;

            WEEKEND = Sat..Sun;

 

      Перший тип служить прикладом завдання натурального типу даних.

      Зауваження 5.10. Якщо значення деякого  обмеженого типу вийде за межі діапазону, то за угодою відмова не фіксується, повідомлення не виводиться, результат непередбачуваний. Для фіксації у цьому випадку відмови необхідно встановити опцію компілятора „Range checking” або включити у текст програми директиву компілятора $R+. Це відноситься також до типу перерахування.¦

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

·         програма стає більш зрозумілою;

·         з'являється можливість більш економного розподілу пам'яті під змінні;

·         вводиться додатковий контроль значень що отримуються змінними.

      Приклад 8P. Визначення дня тижня (задача 5.10).

    {$R+}

    program Day_Week;

      type Week = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);

           Month = 1..12;

           Years = 1920..2099;

      var Day: 1..366; Disp,i: 0..6;

          Num, N_D: 1..31;

          Mn, M: Month;

          Year: Years;

          KV: word;

          D_W: Week;

    begin

    { Введення дати }

      write(' Рік [1920..2099]=? '); readln(Year);

      write(' Місяць [1..12]=? '); readln(Mn);

      case Mn of

        4, 6, 9, 11: N_D:= 30;

        2: if Year mod 4=0 then N_D:= 29 else N_D:= 28;

        else N_D:= 31

      end;

      repeat

        write('Число [1..',N_D,']=?'); readln(Num);

        if Num>N_D then writeln('Невірне число')

      until Num<=N_D;

    { День року }

      Day:= Num;

      for M:=1 to Mn-1 do

        case M of

          4, 6, 9, 11: Day:=Day+30;

          2: if Year mod 4=0 then Day:=Day+29 else Day:=Day+28;

          else Day:=Day+31

        end;

    { День тижня }

      if Year = 1920 then KV := 0

      else KV := ((Year - 1) - 1920) div 4 + 1;

      Disp := ((Year-1920)+KV+(Day-1)) mod 7; { обчислення зcуву }

      D_W:= Thu; { 1 січня 1920 року - четвер }

      for i:=1 to Disp do

        if D_W=Sun then D_W:= Mon  { обчислення дня тижня }

        else D_W:= succ(D_W);

      case D_W of

        Mon: writeln('понеділок');

        Tue: writeln('вівторок');

        Wed: writeln('середа');

        Thu: writeln('четвер');

        Fri: writeln('п''ятниця');

        Sat: writeln('субота');

        Sun: writeln('неділя')

      end

    end.

 

У мові Сі робота з даними типу обмеження не передбачена.

      Приклад 8C. Визначення дня тижня (задача 5.10).

#include <stdio.h>

 

typedef enum {mon,tue,wed,thu,fri,sat,sun} weekday;

 

/* Day_Week */

main()

{

      weekday wd;

      int y,m,d,disp,kv,day,nd,i;

 

      do

            {

             printf("Рік [1920..2099]=? ");

             scanf("%d",&y);

            }

      while (y < 1920 || y > 2099);

      do

            {

             printf("Місяць [1..12]=? ");

             scanf("%d",&m);

            }

      while (m < 1 || m > 12);

      switch (m)

            {

             case 2:    nd = (y % 4 == 0 ? 29 : 28); break;

             case 4:

             case 6:

             case 9:

             case 11:   nd = 30; break;

             default:   nd = 31;

            }

      do

 

            {

             printf("Число [1..%d]=? ",nd);

             scanf("%d",&d);

            }

      while (d < 1 || d > nd);

      kv = (y == 1920 ? 0 : ((y - 1) - 1920) / 4 + 1);

      day = d;

      for (i = 1; i < m; i++)

            switch(i)

                  {

                   case 2:    day += (y % 4 == 0 ? 29 : 28); break;

                   case 4:

                   case 6:

                   case 9:

                   case 11:   day += 30; break;

                   default:   day += 31;

                  }

      disp = ((y - 1920) + kv + (day - 1)) % 7;

      wd = thu;

      for (i = 1; i <= disp; i++)

            if (wd == sun)

                  wd = mon;

            else

                  wd++;

      switch (wd)

            {

             case mon: printf("monday\n"); break;

             case tue: printf("tuesday\n"); break;

             case wed: printf("wednesday\n"); break;

             case thu: printf("thursday\n"); break;

             case fri: printf("friday\n"); break;

             case sat: printf("saturday\n"); break;

             case sun: printf("sunday\n"); break;

            }

}

 

[ЗМІСТ]      [Далі]       [Назад]      [Початок розділу]