В.Ш. Кауфман - Языки программирования - концепции и принципы (1990) (1160787), страница 24
Текст из файла (страница 24)
комбинированного типов преобразует типы компонент регулярных и
комбинированных объектов в новый составной тип. Определяющий пакет
представляет собой преобразователь объявленных в нем типов, дополняющий
набор их базовых операций. Обратите внимание, все названные преобразователи
типов (иногда говорят "типовые" функции с ударением на первом слоге) в язык
Ада встроены. Они не только предопределены создателями ЯП, но и по виду
своего вызова отличаются от обычных функций. По смыслу это, конечно,
функции, аргументами и результатами которых служат типы. Но синтаксически
они функциями не считаются и работают не в период исполнения, как остальные
функции, а в период трансляции (т.е. это "статические" функции).
Таким образом, в преобразователях типов используются все три компоненты
типа. Имя - для идентификации; множества значений и базовых операций - как
аргументы для получения новых множеств значений и операций. Вместе с тем
статическая (т.е. ограниченная) семантика преобразователей типа подчеркивает
ограниченность концепции типа в языке Ада (подчеркивает ее "статичность" и
согласуется с ней). Такая ограниченность характерна для всех статических ЯП,
где тип играет роль основного средства статического прогнозирования-
контроля.
Во-вторых, с помощью типов можно управлять. Тип управляет
прогнозированием-контролем, когда используется в объявлении объектов и
спецификации параметров. Тип непосредственно управляет связыванием
спецификации и тела процедуры, когда работает принцип перекрытия. Тип
управляет выполнением цикла, когда непосредственно определяет диапазон
изменения управляющей переменной цикла. В перечисленных примерах тип в Аде
по-прежнему выступает в качестве аргумента предопределенных конструктов.
Пользователь лишен возможности ввести, например, новый принцип
прогнозирования-контроля, новый принцип перекрытия или новую разновидность
циклов.
В-третьих, тип может служить аргументом функций, вычисляющих отдельные
значения того же самого или другого типа. Это и есть АТРИБУТНЫЕ функции.
Например, функция "первый" вычисляет наименьшее значение заданного
дискретного типа, функция "длина" вычисляет целочисленное значение - длину
диапазона заданного дискретного типа. На самом деле аргументом таких функций
служит ПОДТИП (ведь тип - частный случай подтипа). А подтип, как мы видели
в примере с процедурой "минус", может быть связан с объявленным объектом.
Поэтому в качестве аргумента атрибутной функции может выступать не только
тип, но и объект данных, который в таком случае и идентифицирует
соответствующий подтип.
Так что уже использованные нами функции "нигр" и "вегр" считаются
атрибутными - их аргументами может быть любой регулярный подтип.
Обозначения всех атрибутных функций предопределены в Аде. Чтобы отличать их
от обычных функций (точнее, чтобы объекты высшего порядка - подтипы не
оказывались в синтаксической позиции динамических аргументов) и применяется
специфическая запись вызова атрибутных функций - аргумент-подтип отделяют
апострофом.
Еще одна, четвертая возможность использовать тип как аргумент -
настройка РОДОВЫХ СЕГМЕНТОВ.
Вопрос. Можно ли атрибутную функцию запрограммировать на Аде?
Ответ. Если не использовать других атрибутных функций, то в общем
случае нельзя - нет примитивных операций над типами. Некоторые атрибутные
функции можно выразить через другие с помощью родовых сегментов.
4.8. Родовые (настраиваемые) сегменты
Мы уже отмечали, что тип в Аде не может быть динамическим параметром.
Однако статическая определимость типов не противоречит статической же
обработке заготовок пакетов и процедур с тем, чтобы настраивать их (в период
трансляции) на конкретные значения так называемых РОДОВЫХ ("СТАТИЧЕСКИХ")
ПАРАМЕТРОВ.
Статическими параметрами заготовок пакетов и процедур (РОДОВЫХ
СЕГМЕНТОВ) могут поэтому быть и типы, и процедуры. Определяя родовой
сегмент, можно ввести абстракцию, пригодную для использования в различных
конкретных контекстах.
В Аде имеется мощный аппарат статической абстракции-конкретизации -
аппарат родовых сегментов. Приведем пример родового пакета с четырьмя
родовыми параметрами. После ключевого слова generic следуют четыре
спецификации родовых параметров:
generic
type элемент is private ; -- допустим любой тип
-- (кроме ограниченного приватного)
type индекс is (<>); -- допустим любой дискретный тип
type вектор is array (индекс) of элемент; -- любой регулярный тип,
-- но имя типа индексов указывать обязательно нужно!!
with function сумма (Х, У: элемент) return элемент;
-- закончился список из трех формальных родовых параметров,
-- последний - формальная функция, применимая к объектам
-- формального типа "элемент"
package на_векторах is -- "обычная" спецификация пакета
function сумма (А, В: вектор) return вектор;
function cигма (А: вектор) return элемент;
end на векторах;
Обратите внимание, здесь одна функция "сумма" - формальный родовой
параметр, другая - обычная функция, объявленная в спецификации пакета. Как
писать тело такого пакета, должно быть понятно:
package body на_векторах is
function сумма (А, В: вектор) return вектор is
Z:вектор;
begin
for j in вектор'нигр .. вектор'вегр loop
Z(j) := сумма (А(j), B(j));
end loop;
return Z;
end сумма;
function сигма (А: вектор) return элемент is
Z: элемент := А (вектор'нигр);
for j in вектор'нигр + 1 .. вектор'вегр loop
Z := сумма (Z, А(j));
end loop ;
return Z ;
end сигма;
end на_векторах;
Вот возможная конкретизация этого пакета:
package на_целых_векторах is
new на_векторах (INTEGER, день, ведомость,'+');
Здесь тип "ведомость" считается введенным объявлением
type ведомость is array (день range < >) of INTEGER;
a '+' - предопределенная операция для целых.
Так что если
Т: ведомость (Вт..Пт) := (25,35,10,20);
R: ведомость (Вт..Пт) := (10,25,35,15);
то в соответствующем контексте
сумма(T,R) = (35,60,45,35);
сигма(T) = 90; сигма(R) = 85;
Обратите внимание на применение агрегатов, а также на то, как в них
используется линейный порядок на дискретных типах.
Родовые аргументы должны строго соответствовать спецификации родовых
параметров. За этим ведется строгий контроль. Так, функция '+' подошла, а,
например "or" или тем более "not" - не подойдет (почему?).
Замечание. Обратите внимание на нарушение принципа целостности объектов
в аппарате родовых сегментов.
Вопрос. В чем это проявляется?
Подсказка. Разве все аргументы конкретизации не связаныеще в объявлении
типа "ведомость"? Зачем же заставлять программиста дублировать эту связь?
Указывать (категорию и некоторые другие характеристики) родовых
параметров, нужно для контроля за использованием параметра внутри родового
сегмента. В нашем случае, например, внутри этого сегмента недопустимы какие-
либо операции с объектами типа "элемент", кроме явно определяемых
программистом (например, "сумма"), а также присваиваний и сравнений. С
другой стороны, в качестве типов-аргументов в данном случае допустимы любые
типы (кроме ограниченных приватных). В общем случае в качестве спецификации
родового параметра можно указывать категории перечисляемых типов, целых,
вещественных, ссылочных, регулярных, но не комбинированных.
Вопрос. Почему недопустимы комбинированные?
Подсказка. Иначе пришлось бы в родовом сегменте фиксировать названия и
типы полей. Кстати, чем это плохо?
4.8. Числовые типы (модель числовых расчетов)
4.8.1. Суть проблемы
Рассматривая основные технологические потребности, невозможно обойти
потребность вести числовые расчеты. Эта потребность, как известно, в свое
время предопределила само возникновение ЭВМ. Хотя сейчас потребность в
числовых расчетах - далеко не самая главная в развитии компьютеров и ЯП,
абсолютная потребность в объеме, точности и надежности числовых расчетов
продолжает расти. Так что ни в одном базовом языке индустриального
программирования ее игнорировать нельзя.
Парадоксально, что так называемые машинно-независимые языки для научных
расчетов (Фортран, Алгол и их диалекты) не предоставили удовлетворительной
модели числовых расчетов, в достаточной степени независимой от программной
среды.
В этом их аспекте особенно сказалась несбалансированность средств
абстракции и конкретизации. Абстрагироваться от особенностей среды можно, а
настроиться на конкретную среду - нельзя. Обеспечить надежность при
изменении среды - проблема.
Суть в том, что ни в Фортране, ни в Алголе нет возможности явно
управлять диапазоном и точностью представления числовых данных. Можно лишь
указать, что требуется "двойная точность" (в некоторых диалектах градаций
больше), но какова эта точность, зависит от реализации. Таким образом,
пользователь "машинно-независимого" ЯП оказывается в полной зависимости от
конкретной среды, если ему нужно гарантировать надежность расчетов.
Одна из причин такой ситуации в том, что в начале "эры ЭВМ" скорости
числовых расчетов придавалось исключительно большое значение. Поэтому
считалось практически нереальным применять какое-либо представление чисел и
какие-либо базовые операции, отличные от непосредственно встроенных в
машину. Поскольку такие встроенные числовые типы различны на различных ЭВМ,
считалось невозможным эффективно решить проблему выбора представления в
соответствии с машинно-независимыми указаниями пользователя.
Поэтому проблема обеспечения надежности числовых расчетов традиционно
оставалась вне рамок "машинно-независимых" языков.
По мере накопления пакетов программ и осознания того факта, что
зависимость от конкретных представлений числовых типов - одно из важнейших
препятствий при переносе программ из одной вычислительной среды в другую,
рос интерес к созданию достаточно универсальной схемы управления числовыми
расчетами.
К моменту создания Ады проблема надежности программного обеспечения (в
частности, при переносе из одной вычислительной среды в другую) была
осознана как важнейшая, потеснившая по своей значимости проблему скорости
расчетов. К тому же доля числовых расчетов в общем времени исполнения
программ существенно сократилась. Появились и методы относительно
эффективной реализации вычислений с любой заданной точностью (за счет
микропрограммирования, например).
Все это сделало актуальной и реальной попытку разработать гибкую модель
управления числовыми расчетами. Одна из таких моделей воплощена в Аде.
Управлять представлением числовых данных можно и в языке ПЛ/1, и в
Коболе. Однако в этих ЯП отсутствует явная связь представления данных с
гарантией надежности расчетов. В частности, отсутствуют машинно-независимые
требования к точности реализации предопределенных операций над числами. Эти
требования - основная "изюминка" модели управления расчетами в Аде.
4.8.2. Назначение модели расчетов
Необходимо, чтобы при работе с числовыми типами программист мог
гарантировать надежность расчетов независимо от целевой среды (если только
эта среда пригодна для их выполнения). Гарантировать надежность означает, в
частности, гарантировать как необходимую точность расчетов, так и отсутствие
незапланированных исключительных ситуаций (переполнение, исчезновение
порядка) при допустимых исходных данных. Очевидно, что достичь такой цели
можно, только предоставив программисту возможность объявлять нужные ему
диапазон и точность представления чисел.
Искусство авторов языка (создателей модели управления расчетами)
проявляется в том, чтобы найти разумный компромисс между единообразием
управления и эффективностью расчетов при гарантированной надежности.