Записи | |
Записи с вариантной частью (использование) |
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)...