Подробнее о записях
Типы записей динамически изменяемого размера
Ранее мы видели несколько простых примеров типов записей. Давайте рассмотрим некоторые из более продвинутых возможностей этой фундаментальной конструкции языка Ада.
Следует отметить, что размер объекта для типа записи не обязательно должен быть известен во время компиляции. Это проиллюстрировано в приведенном ниже примере:
Совершенно нормально определять размер ваших записей во время выполнения, но учтите, что все объекты этого типа будут иметь одинаковый размер.
Записи с дискриминантом
В приведенном выше примере размер поля Items определяется один раз во
время выполнения, но размер всех экземпляров Growable_Stack
будет
совпадать.
И, возможно, это не то, что вы хотите получить. Мы видели, что массивы
в целом имеют такую гибкость: для неограниченного типа массива
разные объекты могут иметь разные размеры.
Получить аналогичную функциональность для записей можно используя специальную разновидность компонент, которые называются дискриминантами:
Дискриминанты, грубо говоря, являются константами: вы не можете изменять их значение после инициализации объекта. Это интуитивно понятно, поскольку они определяют размер объекта.
Кроме того, они делают тип неопределенным. В независимости от того, используется ли дискриминант для указания размера объекта или нет, тип с дискриминантом считается неопределенным, пока дискриминант не имеет выражение для инициализации:
Это также означает, что в приведенном выше примере вы не можете объявить массив значений Point, потому что размер Point неизвестен.
Как упоминалось выше, мы могли бы предоставить
значение по умолчанию для дискриминантов, чтобы легально
объявлять переменные типа Point
без указания значения дискриминантов.
В приведенном выше примере это будет выглядеть так:
Также обратите внимание, что, хотя тип Point
теперь имеет дискриминанты по
умолчанию, это не мешает нам указывать дискриминанты, как мы это
делаем в объявлениях P2
и P3
.
Во многих других отношениях дискриминанты ведут себя как обычные поля: вы должны указать их значения в агрегатах, как показано выше, и вы можете извлекать их значения с помощью точечной нотации.
Note
В примере выше, мы использовали дискриминант чтобы указать размер массива, но возможны и другие применения, например, определение дискриминанта вложенной записи.
Записи c вариантами
Ранее мы привели примеры использования дискриминантов для объявление записей разлного размера, содержащих компоненты, размер которых зависит от дискриминанта.
Но с помощью дискриминантов также можно построить конструкцию часто именуемую «запись с вариантами»: это записи, которые могут содержать разные наборы полей.
Поля, которые находятся в варианте when
, будут доступны только тогда, когда
значение дискриминанта совпадает с указанным. В приведенном выше примере
вы сможете обращаться к полям Left
и Right
, только если
Kind
равен Bin_Op_Plus
или Bin_Op_Minus
.
Если вы попытаетесь получить доступ к полю, когда значение дискриминанта не
совпадает, будет возбуждено исключение Constraint_Error
.
А вот как можно написать вычислитель выражений:
На других языках
Записи вариантов Аде очень похожи на Sum типы в функциональных языках, таких как OCaml или Haskell. Основное отличие состоит в том, что дискриминант является отдельным полем в Аде, тогда как «тег» Sum типа является встроенным и доступен только при сопаставлении шаблонов.
Есть и другие различия (записи с вариантами в Аде могут иметь несколько дискриминантов). Тем не менее, они допускают тот же подход к моделированию, что и типы Sum функциональных языков.
По сравнению с объединениями C/C++ записи с вариантами Аде более мощны, а также благодаря проверкам во время выполнения, более безопасны.