| Записи | |
| Записи с вариантной частью (использование) | |
Type
VR = Record
s: string;
Case byte of { Безымянный селектор }
1: (i: integer); { или значение типа Integer }
2: (f: double); { или значение типа Double, причем оба значения,
i и f начинаются по одному и тому же адресу,
хотя и имеют разную длину }
end;
Теперь в программе можно образаться как к целочисленному полю i, так и к вещественному - f:
var R: VR;
...
R.i := 10; { <--- изменяем целочисленную переменную }
R.f := R.f + 0.34; { <--- а тут - изменение переменной вещественной }
Можно в описании записи использовать не безымянный селектор, а поле-селектор:
Type
styleType = (stInteger, stDouble);
VR = Record
s: string;
Case style: styleType of { Это - тоже полноценное поле записи, к которому можно обращаться }
stInteger: (i: integer);
stDouble: (f: double);
end;
И пример использования данного поля-селектора:
var R: VR;
...
R.i := 10; { <--- устанавливаем целочисленное значение в 10 }
R.style := stInteger; { <--- и ставим соотв. признак }
{
Теперь, анализируя поле-селектор, можно точно сказать, какое именно значение было сохранено -
целочисленное, или вещественное, и корректно работать с данными (к примеру, формат вывода для
целочисленных и вещественных значений различен, выбираем правильный):
}
case R.style of
stInteger: writeln('В записи хранится целое число: ', R.i:5);
stDouble: writeln('В записи - вещественное значение: ', R.f:10:5);
end;
...
Теперь немного о типе селектора... В общем случае, тип его может быть любым, главное условие - чтобы
он был перечислимым, и чтобы его емкость (т.е., количество значений, которые может принимать переменная этого типа)
была не меньше числа вариантов в записи... Например, для описанной выше записи вполне подошел бы селектор типа Boolean,
поскольку число вариантов = 2 (целое и вещественное поля), а переменная типа Boolean вполне способна хранить 2 разных
значения: True и False... Тоглда описывать варианты надо было бы так:
Type
VR = Record
s: string;
Case Boolean of { Безымянный селектор }
False: (i: integer);
True: (f: double);
end;, т.е. метки должны быть того же типа, что и селектор...Type
VR = Record
s: string;
Case Boolean of { Безымянный селектор }
False: (i: integer);
False: (f: double); { <--- !!! Ошибка - повторное определение метки !!! }
end;
Существуют 2 ограничения на использование вариантных полей:
Использование вариантных записей
А теперь обратимся ко второй части вопроса: "... зачем это нужно (практический пример использования)".Type
{ Описываем тип Точка - с тремя координатами }
TPoint = Record
x, y, z: Real;
End;
Однако у такого описания есть существенный недостаток... К примеру, нам надо все 3 координаты точки увеличить/уменьшить на определенную
величину... Или присвоить всем трем координатам одинаковое значение, скажем, установить точку в начало координат. С первоначальным
определением это делается так:
var p: TPoint;
...
p.x := p.x + delta; p.y := p.y + delta; p.z := p.z + delta;
{ или }
p.x := 0.0; p.y := 0.0; p.z := 0.0;
...
Уже не совсем удобно, правда? А если пространство будет иметь большее число измерений?Type
{ Описываем тип Точка - с тремя координатами }
TPoint = Record
arr: Array[1 .. 3] Of Real;
End;
Это дает возможность в цикле пробегать по всем координатам, и изменять их, или устанавливать в определенное значение, НО...
Теперь гораздо менее удобно, например, проверять значения определенной координаты, скажем проверка, не равняется ли координата X точки нулю,
будет записана вот так:
var p: TPoint; ... if abs(p.arr[1]) < eps then ..., что делает программу гораздо менее читабельной...
Type
{ Описываем тип Точка - с тремя координатами }
TAxis = (axisX, axisY, axisZ);
TPoint = Record
Case Boolean Of
True : (x, y, z: Real);
False: (arr: Array[TAxis] Of Real);
End;
Теперь там, где это удобно, можно в цикле работать с координатами точки (через массив arr), а там, где надо обратиться к определенной
координате - делать это напрямую, через поля x, y, z... Поскольку оба варианта находятся по одному адресу, то при изменении значения
любым из способов будет меняться одно и то же поле (изменение .arr[1] совершенно аналогично изменению .X, а изменив .arr[3] получаем такой
же результат, как и при работе с .Z)...