Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Языки программирования. Концепции и принципы
.pdf
Данные и типы
131
Еще одна, четвертая возможность использовать тип как аргумент – настройка
РОДОВЫХ СЕГМЕНТОВ.
Вопрос. Можно ли атрибутную функцию запрограммировать на Аде?
Ответ. Если не использовать других атрибутных функций, то в общем случае
нельзя – нет примитивных операций над типами. Некоторые атрибутные функции
можно выразить через другие с помощью родовых сегментов.
4.8. Родовые (настраиваемые)
сегменты
Мы уже отмечали, что тип в Аде не может быть динамическим параметром. Одна
ко статическая определимость типов не противоречит статической же обработке
заготовок пакетов и процедур, с тем чтобы настраивать их (в период трансляции)
на конкретные значения так называемых РОДОВЫХ («СТАТИЧЕСКИХ»)
ПАРАМЕТРОВ.
Статическими параметрами заготовок пакетов и процедур (РОДОВЫХ СЕГ
МЕНТОВ) могут поэтому быть и типы, и процедуры. Определяя родовой сег
мент, можно ввести абстракцию, пригодную для использования в различных кон
кретных контекстах.
В Аде имеется мощный аппарат статической абстракцииконкретизации – ап
парат родовых сегментов. Приведем пример родового пакета с четырьмя родовы
ми параметрами. После ключевого слова generic следуют четыре спецификации
родовых параметров:
generic
type элемент is private;
-- допустим, любой тип (кроме ограниченного приватного)
type индекс is(< >);
-- допустим, любой дискретный тип
type вектор is array(индекс) of элемент;
-- любой регулярный тип, но имя типа индексов указывать обязательно нужно!
with function сумма(X, У: элемент) return элемент;
-- закончился список из четырех формальных родовых параметров,
последний параметр – формальная функция «сумма», применимая к объектам
формального типа «элемент»
package на_векторах is – «обычная» спецификация пакета
function сумма(А, В: вектор) return вектор;
function сигма(А: вектор) return элемент;
end на векторах;
Обратите внимание, здесь одна функция «сумма» – формальный родовой па
раметр, другая – обычная функция, объявленная в спецификации пакета. Как пи
сать тело такого пакета, должно быть понятно:
package body на_векторах is
function сумма(A,B: вектор) return вектор is

132
Z: вектор;
begin
for j in вектор'нигр .. вектор'вегр loop
Z(j) :=сумма (A(j), B(j));
end loop;
return Z;
end сумма;
function сигма(А: вектор) return элемент is
Z: элемент := А (вектор'нигр);
begin
for j in вектор'нигр + 1 .. вектор'вегр loop
Z :=сумма(Z, A(j));
end loop;
return Z;
end сигма;
end на_векторах;
Современное состояние языков программирования
Вот возможная конкретизация этого пакета:
package на_целых_векторах is new на_векторах (INTEGER, день, ведомость, '+');
Здесь тип «ведомость» считается введенным объявлением
tуре ведомость is array(день range < >) of INTEGER;
a '+' – предопределенная операция для целых.
Так что если
Т: ведомость(Вт..Пт) := (25,35,10,20);
R: ведомость(Вт..Пт) := (10,25,35,15);
то в соответствующем контексте
сумма(T,R) = (35,60,45,35); сигма(Т) = 90; сигма(R) = 85;
Обратите внимание на применение агрегатов, а также на то, как в них исполь
зуется линейный порядок на дискретных типах.
Родовые аргументы должны строго соответствовать спецификации родовых
параметров. За этим ведется строгий контроль. Так, функция '+' подошла, а, на
пример, «or» или тем более «not» не подойдет (почему?).
Замечание. Обратите внимание на нарушение принципа целостности объектов
в аппарате родовых сегментов.
Вопрос. В чем это проявляется?
Подсказка. Разве все аргументы конкретизации не связаны еще в объявлении типа
«ведомость»? Зачем же заставлять программиста дублировать эту связь?
Указывать категорию и некоторые другие характеристики родовых парамет
ров нужно для контроля за использованием параметра внутри родового сегмента.
В нашем случае, например, внутри этого сегмента недопустимы какиелибо опе
рации с объектами типа «элемент», кроме явно определяемых программистом
(например, «сумма»), а также присваиваний и сравнений. С другой стороны,
в качестве типоваргументов в данном случае допустимы любые типы (кроме

Данные и типы
ограниченных приватных). В общем случае в качестве спецификации родового
параметра можно указывать категории перечисляемых типов, целых, веществен
ных, ссылочных, регулярных, но не комбинированных.
Вопрос. Почему недопустимы комбинированные?
Подсказка. Иначе пришлось бы в родовом сегменте фиксировать названия и типы
полей. Кстати, чем это плохо?
133
4.9. Числовые типы
(модель числовых расчетов)
4.9.1. Суть проблемы
Рассматривая основные технологические потребности, невозможно обойти по
требность вести числовые расчеты. Эта потребность, как известно, в свое время
предопределила само возникновение ЭВМ. Хотя сейчас потребность в числовых
расчетах – далеко не самая главная в развитии компьютеров и ЯП, абсолютная
потребность в объеме, точности и надежности числовых расчетов продолжает рас
ти. Так что ни в одном базовом языке индустриального программирования ее иг
норировать нельзя.
Парадоксально, что так называемые машинно независимые языки для науч
ных расчетов (Фортран, Алгол и их диалекты) не предоставили удовлетворитель
ной модели числовых расчетов, в достаточной степени независимой от программ
ной среды.
В этом их аспекте особенно сказалась несбалансированность средств абстрак
ции и конкретизации. Абстрагироваться от особенностей среды можно, а настро
иться на конкретную среду – нельзя. Обеспечить надежность при изменении сре
ды – проблема.
Суть в том, что ни в Фортране, ни в Алголе нет возможности явно управлять
диапазоном и точностью представления числовых данных. Можно лишь указать,
что требуется «двойная точность» (в некоторых диалектах градаций больше), но
какова эта точность, зависит от реализации. Таким образом, пользователь «ма
шинно независимого» ЯП оказывается в полной зависимости от конкретной сре
ды, если ему нужно гарантировать надежность расчетов.
Одна из причин такой ситуации – в том, что в начале «эры ЭВМ» скорости
числовых расчетов придавалось исключительно большое значение. Поэтому счи
талось практически нереальным применять какоелибо представление чисел и
какиелибо базовые операции, отличные от непосредственно встроенных в маши
ну. Поскольку такие встроенные числовые типы различны на различных ЭВМ,
считалось невозможным эффективно решить проблему выбора представления
в соответствии с машинно независимыми указаниями пользователя. Поэтому
проблема обеспечения надежности числовых расчетов традиционно оставалась
вне рамок «машинно независимых» языков.

134
По мере накопления пакетов программ и осознания того факта, что зависи
мость от конкретных представлений числовых типов – одно из важнейших пре
пятствий при переносе программ из одной вычислительной среды в другую, рос
интерес к созданию достаточно универсальной схемы управления числовыми
расчетами.
К моменту создания Ады проблема надежности программного обеспечения
(в частности, при переносе из одной вычислительной среды в другую) была осоз
нана как важнейшая, потеснившая по своей значимости проблему скорости расче
тов. К тому же доля числовых расчетов в общем времени исполнения программ
существенно сократилась. Появились и методы относительно эффективной реа
лизации вычислений с любой заданной точностью (за счет микропрограммирова
ния, например).
Все это сделало актуальной и реальной попытку разработать гибкую модель
управления числовыми расчетами. Одна из таких моделей воплощена в Аде.
Управлять представлением числовых данных можно и в языке ПЛ/1, и в Кобо
ле. Однако в этих ЯП отсутствует явная связь представления данных с гарантией
надежности расчетов. В частности, отсутствуют машинно независимые требова
ния к точности реализации предопределенных операций над числами. Эти требо
вания – основная «изюминка» модели управления расчетами в Аде.
Современное состояние языков программирования
4.9.2. Назначение модели расчетов
Необходимо, чтобы при работе с числовыми типами программист мог гарантиро
вать надежность расчетов независимо от целевой среды (если только эта среда
пригодна для их выполнения). Гарантировать надежность означает, в частности,
гарантировать как необходимую точность расчетов, так и отсутствие незаплани
рованных исключительных ситуаций (переполнение, исчезновение порядка) при
допустимых исходных данных. Очевидно, что достичь такой цели можно, только
предоставив программисту возможность объявлять нужные ему диапазон и точ
ность представления чисел.
Искусство авторов языка (создателей модели управления расчетами) прояв
ляется в том, чтобы найти разумный компромисс между единообразием управле
ния и эффективностью расчетов при гарантированной надежности.
4.9.3. Классификация числовых данных
Начать естественно с разумной классификации числовых данных в зависимости
от характера расчетов. В Аде три категории числовых типов: целые, вещественные
плавающие и вещественные фиксированные. Для каждой категории типов име
ются свои средства объявления.
Целые типы предназначены для точных расчетов, вещественные – для при
ближенных. При этом плавающие типы воплощают идею действий с нормализо
ванными числами, представленными с относительной погрешностью, зависящей
от количества значащих цифр, а фиксированные – идею действий с ненормализо

Данные и типы
ванными числами, представленными с абсолютной погрешностью, зависящей от
положения десятичной точки.
Плавающие типы чаще встречаются в ЯП для числовых расчетов. Поэтому ог
раничимся демонстрацией основной идеи управления расчетами на примере пла
вающих типов Ады.
135
4.9.4. Зачем объявлять диапазон и точность
Оставим пока в стороне синтаксис и семантику соответствующих объявлений.
Ведь объявить диапазон и точность – дело относительно нехитрое. А вот что де
лать, когда разрядная сетка или принятое в компьютере представление чисел не
соответствует объявлению типа? Важно понимать, что объявление типа остается
особенно полезным именно в такой ситуации.
Действительно, в худшем случае исполнитель получает всю информацию, что
бы признать (и информировать пользователя), что он непригоден для такой про
граммы. Если объявленные требования к диапазону и точности критичны для
предлагаемых расчетов, то такой отказ, безусловно, предпочтительнее, чем трата
времени и усилий на заведомо ошибочные расчеты, да еще с риском оставить
пользователя в «счастливом» неведении. К тому же легко автоматически или ви
зуально выделить фрагменты программы, вызвавшие непригодность исполните
ля. Это помогает либо подобрать подходящий исполнитель (автоматически, если
доступна, например, неоднородная сеть машин), либо изменить выделенные ком
поненты программы.
4.9.5. Единая модель числовых расчетов
Чтобы гарантировать надежность расчетов в любой среде, то есть управлять диа
пазоном и точностью на достаточно высоком уровне абстракции, пользователь
должен опираться на модель расчетов, единую для всех сред. Однако, стремясь
в первую очередь к переносимости программ, нельзя забывать про эффективность
конкретизации (вспомните принцип реальности абстракций). Другими словами,
единая модель расчетов должна быть гибкой (требовать от конкретных реализа
ций минимума свойств и действий).
В Аде абстракция (единство) обеспечивается понятием модельных чисел,
а конкретизация (гибкость) – понятием допустимых чисел.
Совокупность модельных чисел каждого подтипа полностью фиксируется на
уровне, независимом от среды (то есть определяется семантикой ЯП и применяе
мым программистом объявлением).
Допустимые числа – это некоторое надмножество модельных чисел, завися
щее от конкретной реализации (рассчитанной на конкретную целевую среду).
Гибкость модели проявляется в том, что расчеты программируются и оценива
ются с помощью модельных чисел, а выполняются с допустимыми числами.
Из набора предопределенных числовых типов реализация имеет право подо
брать для заданного объявления числового типа такой (по возможности мини

136
мальный) базовый тип допустимых чисел, который удовлетворяет указанным
при объявлении ограничениям диапазона и точности.
Итак, с одной стороны, все реализации ориентированы на единую «систему
координат» – модельные числа; с другой – каждая реализация работает со своими
допустимыми числами.
Рассмотрим на примере управление диапазоном модельных чисел. Пусть
объявлен плавающий тип
type скорость is digits 8; – число значащих цифр(D) = 8
После ключевого слова digits указан спецификатор точности D (равный в на
шем случае восьми), определяющий диапазон модельных чисел типа «скорость»
по следующим единым для всех реализаций правилам.
Прежде всего необходимо вычислить количество В двоичных цифр, обеспечи
вающих ту же относительную погрешность, что и D десятичных цифр. В общем
случае
В = целая_часть(D * ln(10)/ln(2) + 1)
то есть приблизительно 3,3 двоичной цифры для представления одной десятич
ной. В нашем случае
 = [ 8 * 3,3 + 1 ] = 27.
Диапазон модельных чисел определяется как совокупность всех (двоичных!)
чисел, представимых в виде
знак * мантисса * (2 ** порядок),
где знак – это +1 или –1, мантисса – правильная двоичная дробь, записанная ров
но В двоичными цифрами, первая из которых 1 (то есть нормализованная дробь),
порядок – целое число между –4*В и +4*В.
Таким образом, с каждой спецификацией D связывается конечный набор ве
щественных модельных чисел. При этом фиксируется не только количество цифр
в мантиссе, но и максимальный порядок. В нашем случае двоичный порядок равен
27 * 4 = 108.
Соответствующий десятичный порядок 4*D = 32. Чем больше порядок, тем
«реже» встречаются модельные числа – точность представления плавающих ти
пов относительна.
Современное состояние языков программирования
4.9.6. Допустимые числа
Как уже сказано, диапазон допустимых чисел – это расширение диапазона мо
дельных чисел, самое экономичное с точки зрения реализации. В принципе, в на
шем случае в качестве допустимых чисел могут фигурировать, например, числа
с 40разрядной мантиссой и 7разрядным порядком. Так что для представления
такого диапазона допустимых чисел подойдет, например, 48разрядное машинное
слово. На практике транслятор подберет в качестве базового для типа «скорость»
ближайший из предопределенных плавающих типов REAL, SHORT_REAL,
LONG_REAL или иной из плавающих типов, определяемый реализацией.

Данные и типы
137
Уточнить диапазон и точность при объявлении производного типа, числового
подтипа или объекта можно, как обычно, с помощью ограничения. Например:
type высота is new скорость range 0.0 .. 1.0Е5;
(высота может меняться от нуля до десяти тысяч).
subtype высота_здания is высота range 0.0 .. 1.0ЕЗ;
высота_полета : высота digits 5;
(для переменной высота_полета допустимая точность меньше, чем в типе «высота»).
4.10. Управление операциями
Управление диапазоном и точностью для плавающих типов имеется во многих
ЯП. Однако этого мало для надежного и независимого от среды управления рас
четами, если программист лишен возможности на уровне модельных чисел учи
тывать погрешности, вносимые предопределенными (то есть элементарными
арифметическими) операциями. Такой возможности до Ады не предоставлял ни
один ЯП. Результаты всех предопределенных операций над допустимыми числа
ми определяются в Аде с помощью модельных чисел следующим единым для лю
бой среды способом.
Модельным интервалом называется любой интервал между модельными чис
лами. С каждым допустимым числом ассоциируется так называемый связанный
модельный интервал – это минимальный модельный интервал, к которому при
надлежит рассматриваемое число (для модельных чисел связанным интервалом
считается само модельное число!).
Основное требование к точности реализации предопределенных операций
(модельное ограничение) состоит в том, что результат операций над допустимы
ми числами должен попадать в минимальный модельный интервал, содержащий
точные математические результаты рассматриваемой операции при изменении
аргументов операции в пределах их связанных интервалов.
Наглядно это можно представить следующим рисунком (рис. 4.2).
!!!!!<!!!!!!!!!!!>!!!!!
!!!!!|!!!!!!!!!!!!|!!!!!
!<!|!!!!!!!!!!!!!!|!>!
Модельный интервал аргумента
Точные математические результаты на границе модельного
интервала
Допустимый интервал результата. Минимальный объемлю!
щий модельный интервал
Рис. 4.2
Таким образом, значениями операций вещественных типов в конкретных реа
лизациях могут быть любые числа, обладающие модельным интервалом, но от
клонения результатов операций регламентированы модельным ограничением.
Искусство программиста состоит теперь в том, чтобы гарантировать надеж
ность расчетов, подбирая диапазон и точность в соответствии с условиями задачи.

138
При этом он не должен забывать, что при реализации излишний диапазон или
излишняя точность могут стоить дорого и по времени, и по памяти (а могут и пре
высить возможности реализации). Короче говоря, нужно программировать как
можно ближе к нуждам решаемой задачи.
В Аде предусмотрено, что реализация может предоставлять несколько предоп
ределенных (именованных или анонимных) плавающих типов с различными диа
пазонами и точностью расчетов.
Искусство автора компилятора проявляется в том, чтобы компилятор был
в состоянии подобрать подходящий предопределенный тип (в качестве допусти
мых чисел) для любого (или почти любого) объявления типа, с оптимальными
затратами на расчеты при полной гарантии соответствия модельному ограни
чению.
Обратите внимание, в Аде отдано явное предпочтение двоичным машинам (ведь
допустимые числа включают модельные, причем в соответствии с модельным
ограничением результат операции над модельным числом должен быть точным,
если математический результат оказывается модельным числом; для элементар
ных (!) арифметических операций такое требование выполнимо, в отличие от про
извольных математических функций). К тому же не любым двоичным машинам,
а с достаточно большим порядком во встроенном представлении плавающих чисел
(ведь далеко не во всех машинах допустимы порядки, вчетверо превышающие дли
ну мантиссы; во всяком случае в БЭСМ6 это не так).
Современное состояние языков программирования
4.11. Управление представлением
Суть проблемы. Единая модель числовых расчетов, как мы видели, позволяет
программисту непосредственно управлять представлением данных числовых ти
пов в целевой машине. Но потребность управлять представлением возникает не
только для числовых типов и не только для данных. Причем требуется управлять
в некотором смысле окончательным представлением (как и в случае числовых
типов), а не промежуточным (когда, например, данные приватного типа мы пред
ставляли данными комбинированного типа). Другими словами, требуется управ
лять представлением объектов в терминах понятий, в общем случае выходящих за
рамки машинно независимого ЯП и определенных только на конкретной целевой
машине.
Такая потребность особенно часто возникает в системных программах, вынуж
денных взаимодействовать, в частности, с нестандартными устройствами обмена
(вводавывода) или со средствами управления прерываниями целевой машины.
Например, требуется явно указать адрес, с которого располагается реализация
реакции на конкретное прерывание.
Конечно, программы, использующие управление окончательным (или, как ча
сто говорят, «абсолютным») представлением, перестают быть машинно независи
мыми. Однако это та мера зависимости от машины (та мера конкретизации), без
которой программа неработоспособна.

Данные и типы
139
Вместе с тем это зависимость только от целевой машины. Сами средства управ
ления представлением данных могут оставаться нормальными конструктами ма
шинно независимого ЯП, где они играют роль компонент общего аппарата связыва
ния, а именно связывания абстрактной спецификации данных с их конкретным
абсолютным представлением. Такое связывание выполняется при трансляции и
может быть выполнено на любой транслирующей (инструментальной) машине.
С другой стороны, если связывание такого рода выделено как достаточно об
щее языковое понятие и ему соответствует легко идентифицируемый конструкт,
то настройка программы (ее перенос) на другую целевую машину сводится к из
менению только таких конструктов.
Именно такой идеей и руководствовались авторы ЯП Ада. Управление абсо
лютным представлением выделено в конструкт, который называется УКАЗАНИ
ЕМ ПРЕДСТАВЛЕНИЯ (спецификацией представления – representation
clauses). Указание представления должно следовать за объявлением тех сущно
стей, представление которых в нем конкретизируется. В Аде можно указывать
представление для типов, объектов данных, подпрограмм, пакетов и задач, а так
же для входов.
Подход к языковым конструктам как компонентам единого аппарата абстракции
конкретизации позволяет легко обнаружить перспективу развития средств управ
ления представлением в Аде. А именно высшим уровнем оформления абстракции
представления было бы выделение специального программного сегмента («модуля
представления»), предназначенного исключительно для описания привязки ма
шинно независимых сегментов к конкретной целевой машине.
Вопросы. Что бы это дало? Каковы недостатки такого решения?
Интересно, что эта перспектива неоднократно рассматривалась автором на лекци
ях за несколько лет до того, как ему стало известно о воплощении модулей управ
ления представлением в системе Кронус (новосибирской модификации Модулы2).
Так что анализ ЯП с самых общих «философских» позиций действительно позво
ляет иногда прогнозировать их развитие.
Рассмотрим несколько примеров одного из самых нужных указаний представ
ления – УКАЗАНИЯ АДРЕСА.
Указание адреса применяют для того, чтобы связать объект данных, подпрог
рамму или вход задачи с той ячейкой памяти целевой машины, которая играет
особую роль. Это может быть регистр определенного периферийного устройства;
адрес, по которому передается управление при определенном прерывании; ячей
ка, состояние которой определяет режим работы машины, и т. п.
Чтобы можно было записать указание адреса, должен быть доступен тип «ад
рес». Содержательно значения этого типа играют роль адресов памяти целевой
машины, формально это один из предопределенных типов. В Аде его определяю
щим пакетом считается предопределенный пакет «система», характеризующий
целевую машину. Следовательно, тип «адрес» становится доступным с помощью
указателя контекста вида
with система; use система;

140
Современное состояние языков программирования
Вот примеры указания адреса с очевидным назначением:
for управл_ячейка use at 16#0020#;
после at записана шестнадцатеричная константа типа «адрес». Такое указание ад
реса должно быть помещено среди объявлений блока, пакета или задачи после
объявления объекта управл_ячейка.
task обработка_прерывания is
entry выполнить;
for выполнить use at 16#40#;
-- вызвать вход «выполнить» – это значит передать управление в ячейку 16#40#
end обработка_прерывания;
Еще примеры использования указаний представления:
слово : constant := 4;
-- элемент памяти – байт, "слово" – из четырех байтов.
type состояние is(A,M,W,P);
-- четыре характеристики состояния:
-- каков код символов(ASCII или EBCDIC);
-- разрешены ли прерывания;
-- ждет ли процессор;
-- супервизор или задача
type маска_байта is array(0..7) of BOOLEAN;
type маска_состояния is array (состояние) of BOOLEAN;
type маска_режима is array(1..4) of BOOLEAN;
type слово_состояние_программы is record
маска_системы : маска_байта;
ключ_защиты : INTEGER range 0..3;
состояние_машины : маска_состояния;
причина_прерывания : код_прерывания;
код_длины_команды : INTEGER range 0..3;
признак_результата : INTEGER range 0..3;
маска_программы : маска_режима;
адрес_команды : адрес;
end record;
-- ниже следует указание представления для этого типа
for слово_состояния_программы use record at mod 8;
-- адреса записей указанного типа
-- должны быть нулями по модулю 8, то есть
-- адресами двойных слов. Далее указаны требования
-- к расположению полей записи относительно ее начала
маска_системы at 0 * слово range 0..7;
-- маска системы расположена в первом байте двойного слова.
ключ_защиты at 0 * слово range 10..11;
-- разряды 8 и 9 не используются.
состояние_машины at 0 * слово range 12.. 15;
причина прерывания at 0 * слово range 16..31;
код_длины_команды at 1 * слово range 0..1;
признак_результата at 1 * слово range 2 ..3;
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
