Недостатком применения перечислимых типов является то, что значения из перечислимого типа (атомы) не могут быть выведены на экран или принтер и не могут быть явно введены с клавиатуры. Бороться с этим недостатком можно, но посредством не очень красивых приемов. Обычно, чтобы все-таки иметь возможность вывода на экран, вводят массивы, проиндексированные атомами. Каждый их элемент есть строковое написание соответствующего атома (например, для атома NoInfo_ - строка 'Nolnfo_').
- 67 -
Для работы с перечислимыми типами в Турбо Паскале используются общепринятые функции Ord, Pred и Succ. Рассмотрим их действие.
Любой перечислимый тип имеет внутреннюю нумерацию. Первый элемент всегда имеет номер 0; второй - номер 1 и т.д. Порядок нумерации соответствует порядку перечисления. Номер каждого элемента можно получить функцией Ord(X) : LongInt, возвращающей целое число в формате длинного целого, где X - значение перечислимого типа или содержащая его переменная. Так, для введенного выше типа Test:
Ord(Level0) даст 0,
Ord(Level1) даст 1,
...
Ord(Level5) даст 5.
Применительно к целым типам функция Ord не имеет особого смысла и возвращает значение аргумента:
Ord(0) = 0
Ord(-100) =-100
но для значений Char она вернет их код:
Ord('0') = 48,
Ord(' ') = 32,
Ord('Б') = 129.
Для логических значений
Ord(False) = 0 и Ord( True ) = 1.
Обратной функции для извлечения значения по его порядковому номеру в языке нет, хотя выражение вида
X : = ИмяПеречислимогоТипа(ПорядковыйНомер)
запишет в X значение, соответствующее заданному порядковому номеру элемента перечисления. Кроме этого, имеются две функции последовательного перебора значений перечислимого типа:
Succ(X) - возвращает следующее за X значение в перечислимом типе;
Pred(X) - возвращает предыдущее значение в перечислимом типе.
Так, для нашего типа Boolеаn3
Succ(false_) = noinfo_ = Pred( true_).
Функции Succ и Pred применимы и к значениям целочисленных типов:
- 68 -
Succ(15) = 16, Succ(-15) = -14,
Pred(15) = 14, Pred(-15) = -16.
и очень эффективно работают в выражениях вида (N-1)*N*(N+1), которые могут быть переписаны как Pred(N)*N*Succ(N).
Не определены (запрещены) значения:
Succ(последний элемент перечисления)
и
Pred(первый элемент перечисления).
Поскольку перечислимые значения упорядочены, их можно сравнивать. Из двух значений большим является то, у которого больше порядковый номер (но это сравнение должно быть в пределах одного и того же типа!), т.е. выполняется:
True > False
в типе Boolean,
NoInfo_ < true_
в типе Boolean3,
'z' > 'a'
в типе Char.
Знаки сравнения могут быть и нестрогими.
4.1.8. Ограниченные типы (диапазоны)
Еще одним вводимым типом языка является диапазон. Используя его, мы можем определить тип, который будет содержать значения только из ограниченного поддиапазона некоего базового типа. Базовым типом, из которого вычленяются диапазоны, может быть любой целочисленный тип, тип Char и любой из введенных программистом перечислимых типов.
Для введения нового типа - диапазона - надо в блоке описания типов TYPE указать имя этого типа и границы диапазона через две точки подряд:
| TYPE
| Century = 1..20; { диапазон целочисленного типа }
| CapsLetters = 'А'..'Я'; { заглавные буквы из типа Char }
| TestOK = Level3..Level5; { часть перечислимого типа Test }
Переменные этих объявленных типов смогут иметь значения только в пределах заданных типом диапазонов, включая их границы.
- 69 -
Это еще больше усиливает контроль данных при выполнении программы. Значения переменных типа "диапазон" могут выводиться на экран и вводиться с клавиатуры, только если этот диапазон взят из базового типа, выводимого на экран или вводимого с клавиатуры. В приведенном примере сугубо "внутренними" значениями будут только значения, принадлежащие к типу TestOK, как диапазону невыводимого перечислимого типа Test. Будет ошибкой задать нижнее значение диапазона большим, чем верхнее.
При конструировании диапазона в описании типа можно использовать несложные арифметические выражения для вычисления границ. Но при этом надо следить, чтобы запись выражения не начиналась со скобки (скобка - это признак начала перечисления):
| TYPE
| IntervalNo = (2*3+2)*2 .. (5+123); {неверно! }
| IntervalYes = 2*(2*3+2) .. (5+123); { правильно }
4.2. Сложные типы языка
Среди сложных типов первым традиционно рассматривается массив - упорядоченная структура однотипных данных, хранящая их последовательно. Массив обязательно имеет размеры, определяющие, сколько элементов хранится в структуре. До любого элемента в массиве можно добраться по его индексу.
Тип "массив" определяется конструкцией
Array [диапазон] of ТипЭлементов;
Диапазон в квадратных скобках указывает значения индексов первого и последнего элемента в структуре. Примеры объявления типов:
| TYPE
| Array01to10 = Array[ 1..10] of Real;
| Array11to20 = Array [11..20] of Real;
Здесь мы вводим два типа. Они совершенно одинаковы по структуре, но по-разному нумеруют свои элементы. Оба типа содержат наборы из десяти значений типа Real.
Пусть определены переменные именно таких типов (скажем, a01to10 и a11to20). Доступ к i-му элементу массивов осуществляется через запись индекса в квадратных скобках сразу за именем массива: a01to10[i] или a11to20[i].
Возможно объявление таких конструкций, как массив массивов (это будет, по сути, матрица) или других структур. Подробнее массивы будут рассмотрены в разд. 7.1 "Массивы (Array) и работа с ними".
- 70 -
Другим сложным типом является множество, конструируемое специальной фразой
Set of БазовыйПеречислимыйТип.
Данные типа "множество" - это наборы значений некоего базового перечислимого типа, перечисленные через запятую в квадратных скобках. Так, если базовым считать введенный ранее перечислимый тип Test, то значение типа
Set of Test
может содержать произвольные выборки значений типа Test:
[Level0]
[Level3, Leve4]
[Level1..Level5],
и т.п., а также
[] - пустое множество.
Множество отличается от массива тем, что не надо заранее указывать количество элементов в нем, используя индексацию. Множество может расширяться или сокращаться по ходу программы. В Паскале определены операции над множествами, аналогичные математическим: объединение множеств, поиск пересечения их (взятие разности множеств), выявление подмножеств и др. Излишне говорить, что такой тип данных существенно расширяет гибкость языка. Подробно множества описываются в разд. 7.3 "Тип "множество" (Set). Операции с множествами".
Файловый тип - это "окно в мир" для программы на Турбо Паскале. Именно с помощью файловой системы осуществляется весь ввод и вывод информации программой. Этим вопросам в книге посвящена гл. 12, здесь же ограничимся общим определением файла. Файл (любого типа) - это последовательность данных, расположенных вне рабочей памяти программы (на магнитном диске, на экране, на принтере, в другом компьютере в сети ПЭВМ или где-нибудь еще). Некоторые файлы могут только выдавать информацию (например, клавиатура). Другие файлы могут только принимать ее, например, устройство печати. Напечатанный принтером лист - это зримый образ выведенного программой файла. Третьи файлы позволяют и считывать из себя информацию, и записывать ее. Примером является обычный файл на диске. Определяя файлы в программе, мы можем через них общаться с периферией ПЭВМ, и в том числе накапливать данные и впоследствии обращаться к ним.
- 71 -
Файловые типы Турбо Паскаля (Text, File of... и File) различаются только типами данных, содержащихся в конкретных файлах программы. Задавая в программе структуры данных типа "файл", мы определяем тип этих данных, но не оговариваем их количество в файле. Теоретически файл может быть бесконечной последовательностью данных; практически же на все случаи жизни в ПЭВМ найдутся ограничения, в том числе и на длину файла.
Следующий сложный тип языка - записи. Для тех, кто не знаком с языком Паскаль, это слово поначалу ассоциируется не с тем, что оно на самом деле обозначает. Да, запись может быть записью на диске. Но в исходном значении - это структура данных, аналогичная таблице. У обычной таблицы есть имя, и у каждого ее поля тоже есть имя. Похожим образом в типе данных "запись" указываются названия полей и тем самым вводятся в работу табличные структуры данных. Мы можем, например, ввести тип данных, аналогичный анкете (т.е. той же таблице):
| TYPE
| PERSON=RECORD
| F_I_O_ : String; { фамилия, имя, отчество}
| Ves.Rost : Real; { вес и рост}
| Telephone : Longint { номер телефона}
| END;