Andrew Fionik дата публикации 20-07-2005 09:06 урок из цикла:
Перечислимые типы данных, определяемые пользователем
В данной лекции мы познакомимся с перечислимыми типами данных. В сущности, любой целочисленный тип данных уже является перечислимым, однако целочисленные типы данных являются встроенными, и не требуют объявления их пользователем. Здесь же мы узнаем, как создаются собственные перечислимые типы данных определяемые пользователем.
Перечислимый тип данных определяет упорядоченный набор значений, просто перечисляя идентификаторы, обозначающие эти значения. Эти идентификаторы не имеют какого-то самостоятельного смысла.
Перечислимые типы очень похожи на целочисленные типы данных. Кое-кто может задаться вопросом: "А зачем это все нужно, если и так можно пользоваться целочисленными типами данных?" Предположим, что нам нужно определить состояние лифта. Возможными состояниями лифта являются: "стоит", "едет", "отключен". Можно обозначить каждое из состояний значениями 1, 2, 3 и оперировать ими. Какие у нас тут появляются проблемы?
Во-первых - не наглядно. Посмотрит человек со стороны, и ему еще придется вникать, что 1 это "стоит", а 3 это "отключен", и почему не наоборот. Да и самому можно запутаться.
Во-вторых - есть потенциальное место для ошибок. Что если целочисленной переменной, которая обозначает состояние лифта, где-нибудь присвоят значение 4? Что это будет обозначать для кода, обрабатывающего это значение? Что с ним произойдет? Он отработает, но выдаст неверный результат, или программа просто "вывалится" с ошибкой? Придется в каждом месте, где используется эта переменная писать код проверки на правильность значения. А если у нас добавиться еще одно состояние "авария", то придется исправлять код проверяющий правильность значения.
В общем, существуют ситуации (и их достаточно много) когда использование перечислимых типов гораздо более предпочтительно, чем целочисленных, как с точки зрения наглядности, так и с точки зрения надежности.
EnumeratedTypeName=(ListOfValues);
- EnumeratedTypeName - Идентификатор перечислимого типа данных.
- ListOfValues - Описание перечислимого типа. Список идентификаторов значений, принадлежащих к указанному типу данных.
Список идентификаторов значений, это простое перечисление идентификаторов значений, разделенных запятой. Список идентификаторов не может быть пустым.
Value0, Value1, Value2, …, ValueN
Примеры описания перечислимых типов
Предположим мы хотим иметь перечислимый тип данных, обозначающий цвет светофора (о том, что светофор бывает выключенным, на время забудем). Как мы все знаем, светофор может светить одним из следующих цветов: красный, желтый, зеленый. Обозначим каждый цвет своим идентификатором: Red, Yellow, Green. Назовем перечислимый тип данных, определяющий возможные цвета светофора, как TSemaphoreColor. Описание нашего типа данных будет выглядеть так:
type
TSemaphoreColor=(Red, Yellow, Green);
| |
NB! Не забываем, что секция описания типов данных начинается с ключевого слова type, см. лекцию 2 "Переменные, типы данных и константы".
Вспоминая известную фразу "Каждый охотник желает знать, где сидит фазан", определяем перечислимый тип данных TRainbow.
type
TRainbow=(Red, Orange, Yellow, Green, Azure, Blue, Violet);
| |
type
TGender=(Male, Female);
TCar=(Mercedess, BMW, Toyota, TVR, Mazda);
TPlayerCharacterClass=(Fighter, Cleric, Rogue, Wizard);
| |
Порядковые номера
Каждое значение в перечислимом типе имеет свой порядковый номер. Первое значение имеет порядковый номер 0. Второе значение имеет порядковый номер 1 и т.д. Порядковые номера назначаются компилятором в том порядке, в котором идентификаторы перечислены в описании типа.
Пользователь может явным образом задать порядковые номера, если хочет, чтобы они не совпадали с порядком, назначаемым компилятором по умолчанию. В этом случае синтаксис списка идентификаторов значений будет выглядеть следующим образом:
Value0=NumberOfValue0, Value1=NumberOfValue1, …, ValueN=NumberOfValueN | |
- NumberOfValueX - константное целочисленное выражение, задающее порядковый номер соответствующего идентификатора.
type
TNumber=(Two=2, Three=3, Five=5, Seven=Two+Five);
| |
Следует заметить, что эта особенность языка применяется крайне редко и скорее вносит больше путаницы при работе с перечислимыми типами, нежели пользы.
При объявлении переменных перечислимого типа нет необходимости объявлять идентификатор типа данных. Описание перечислимого типа может быть использовано прямо при объявлении переменной:
var
VariableName: (ListOfValues);
| |
var
Fighter:(Hornet, Tomcat, Falcon, Raptor);
| |
Проблема, при таком способе определения типа переменной, состоит в том, что становится невозможно описать другую переменную такого же типа. Нижеследующее описание приведет к ошибке компиляции, не смотря на то, что обе переменные описаны идентично:
var
Fighter1:(Hornet, Tomcat, Falcon, Raptor);
Fighter2:(Hornet, Tomcat, Falcon, Raptor);
| |
Чтобы избежать ошибки компиляции следует изменить описание переменных следующим образом:
var
Fighter1, Fighter2:(Hornet, Tomcat, Falcon, Raptor);
| |
Операции со значениями перечислимых типов данных
В сущности, перечислимые типы данных не сильно отличаются от целочисленных типов данных.
Функции стандартной библиотеки Succ и Pred обеспечивают получение последующего и предыдущего значений, как и для любого другого порядкового типа данных вроде Integer или Byte.
program enumdemo1;
type
TRainbow=(Red, Orange, Yellow, Green, Azure, Blue, Violet);
var
Color:TRainbow;
begin
Color:=Succ(Yellow);
case Color of
Red: WriteLn('Red');
Orange: WriteLn('Orange');
Yellow: WriteLn('Yellow');
Green: WriteLn('Green');
Azure: WriteLn('Azure');
Blue: WriteLn('Blue');
Violet: WriteLn('Violet');
end;
Color:=Blue;
Color:=Pred(Color);
case Color of
Red: WriteLn('Red');
Orange: WriteLn('Orange');
Yellow: WriteLn('Yellow');
Green: WriteLn('Green');
Azure: WriteLn('Azure');
Blue: WriteLn('Blue');
Violet: WriteLn('Violet');
end;
WriteLn('Press Enter key…');
ReadLn;
end.
| |
Поскольку для Red отсутствует предыдущее значение, а для Violet последующее, то попытка получить их приведет к ошибке времени выполнения. Например такой код, вызовет ошибку времени выполнения:
…
var
Color:TRainbow;
begin
Color:=Pred(Red);
…
| |
Последовательно выведем с помощью цикла for названия цветов радуги, начиная с Orange и заканчивая Azure.
program enumdemo2;
type
TRainbow=(Red, Orange, Yellow, Green, Azure, Blue, Violet);
var
Color:TRainbow;
begin
for Color:=Orange to Azure do
case Color of
Red: WriteLn('Red');
Orange: WriteLn('Orange');
Yellow: WriteLn('Yellow');
Green: WriteLn('Green');
Azure: WriteLn('Azure');
Blue: WriteLn('Blue');
Violet: WriteLn('Violet');
end;
WriteLn('Press Enter key...');
ReadLn;
end.
| |
Сделаем то же самое, только с использованием цикла while. Обратите внимание на функции стандартной библиотеки Low и High. Каждая принимает в качестве единственного параметра идентификатор перечислимого типа данных и возвращает минимальное или максимальное значение, возможное для переменных данного типа.
program enumdemo3;
type
TRainbow=(Red, Orange, Yellow, Green, Azure, Blue, Violet);
var
Color:TRainbow;
begin
Color:=Low(TRainbow);
while Color<=High(TRainbow) do
begin
case Color of
Red: WriteLn('Red');
Orange: WriteLn('Orange');
Yellow: WriteLn('Yellow');
Green: WriteLn('Green');
Azure: WriteLn('Azure');
Blue: WriteLn('Blue');
Violet: WriteLn('Violet');
end;
Color:=Succ(Color);
end;
WriteLn('Press Enter key...');
ReadLn;
end.
| |
В зависимости от цвета выведем, к каким цветам он относится, "холодным" или "теплым". Будем считать, что все цвета до зеленого включительно являются "теплыми". Для разнообразия будем считать цвета от "конца" к "началу", т.е. от фиолетового цвета к красному.
program enumdemo4;
{$APPTYPE CONSOLE}
type
TRainbow=(Red, Orange, Yellow, Green, Azure, Blue, Violet);
var
Color:TRainbow;
begin
{В цикле перебираются значения от максимального
значения типа TRainbow, до минимального}
for Color:=High(TRainbow) downto Low(TRainbow) do
begin
{В зависимости от значения Color выведем название цвета}
case Color of
Red: Write('Red');
Orange: Write('Orange');
Yellow: Write('Yellow');
Green: Write('Green');
Azure: Write('Azure');
Blue: Write('Blue');
Violet: Write('Violet');
end;
{А затем добавим его описание, холодный или теплый.}
if Color>Green then
WriteLn(' is cold color.')
else
WriteLn(' is warm color.');
end;
WriteLn('Press Enter key...');
ReadLn;
end.
| |
Порядковый номер значения можно получить, используя функцию стандартной библиотеки Ord. Функция получает в качестве параметра выражение перечислимого типа, и возвращает его порядковый номер.
program enumdemo5;
type
TRainbow=(Red, Orange, Yellow, Green, Azure, Blue, Violet);
var
Color:TRainbow;
begin
for Color:=Low(TRainbow) to High(TRainbow) do
begin
Write('Ordinal value of ');
case Color of
Red: Write('Red');
Orange: Write('Orange');
Yellow: Write('Yellow');
Green: Write('Green');
Azure: Write('Azure');
Blue: Write('Blue');
Violet: Write('Violet');
end;
WriteLn(' is ', Ord(Color));
end;
WriteLn('Press Enter key...');
ReadLn;
end.
| |
Проблема состоит в том, что не существует стандартной функции библиотеки, позволяющей получить значение перечислимого типа по его порядковому номеру. Таким образом, нам придется проявить сообразительность. Простейшее решение состоит в том, чтобы сравнить порядковые номера всех значений перечислимого типа данных с исходным номером. В случае если номера совпадают, то можно считать, что нужный цвет найден.
program enumdemo6;
type
TRainbow=(Red, Orange, Yellow, Green, Azure, Blue, Violet);
var
Color:TRainbow;
LowIndex, HighIndex, ColorIndex:Integer;
begin
LowIndex:=Ord(Low(TRainbow));
HighIndex:=Ord(High(TRainBow));
WriteLn('Please enter index of rainbow color.');
WriteLn('Valid values are from ', LowIndex, ' to ', HighIndex);
ReadLn(ColorIndex);
if (ColorIndexor
(ColorIndex>HighIndex) then
begin
WriteLn('Invalid color index ',ColorIndex,' entered.');
Exit;
end;
Color:=Low(TRainbow);
while Ord(Color)<>ColorIndex do Color:=Succ(Color);
case Color of
Red: WriteLn('Red');
Orange: WriteLn('Orange');
Yellow: WriteLn('Yellow');
Green: WriteLn('Green');
Azure: WriteLn('Azure');
Blue: WriteLn('Blue');
Violet: WriteLn('Violet');
end;
WriteLn('Press Enter key...');
ReadLn;
end.
| |
[Перечислимые типы]
Обсуждение материала [ 15-08-2005 04:04 ] 6 сообщений |