В.Ш. Кауфман - Языки программирования - концепции и принципы (1990) (1160787), страница 9
Текст из файла (страница 9)
пакета). Во-вторых, нужно как-то сообщить пользователю, что же делает
объявляемая функция.
Например, из объявления в строке 16 пользователь поймет, что, задав
функции узел_есть аргумент типа узел, он получит в качестве результата
истину или ложь. Но откуда ему узнать, что истина соответствует случаю,
когда узел с указанным именем есть в сети, а ложь - когда его нет. Название
функции лишь намекает на такое истолкование. Конечно, названия должны быть
мнемоничными и помогать запоминать смысл программных объектов, но они не
могут заменить точных сведений.
Ада не предоставляет специальных средств для полного и точного описания
внешнего эффекта модуля. Ведь адовские спецификации рассчитаны прежде всего
на исполнителя, на компьютер, а отнюдь не на пользователя. Поэтому, как и в
случае с другими ЯП, проектирование Ада-модуля следует сопровождать
проектированием точного описания его внешнего эффекта (применяя при
необходимости средства, выходящие за рамки ЯП). Некоторые экспериментальные
языки предоставляют встроенные средства соответствующего назначения.
Часто бывает необходимо параллельно создавать и описание внешнего
эффекта, специально ориентированное на пользователей. Эта так называемая
пользовательская документация принципиально отличается от описаний,
рассчитанных на автомат-транслятор (именно таковы описания на ЯП) или
человека-реализатора, по структуре документов, стилю изложения, выделяемым
свойствам объектов и т.п.
С точки зрения пользовательской документации на программное изделие, ЯП
всегда выступает в роли инструмента реализации. Он тем лучше, чем проще
объяснить пользователю назначение выделенных программных компонент и чем ими
удобнее и дешевле пользоваться. Назовем соответствующий критерий качества
языка критерием выделимости.
По выделимости Ада превосходит, например, Алгол 60 или Бейсик, так как
позволяет адекватно оформлять не только компоненты-функции, но и компоненты-
данные, и компоненты-задачи, и компоненты более "тонкого" назначения.
Другими словами, Ада выигрывает в выделимости потому, что предоставляет
более развитые средства абстракции и конкретизации.
Причем здесь абстракция-конкретизация?.
Шаг 2.3 (строки 12-14). Предоставляя средства для изменения сети,
определяем три процедуры: вставить, удалить и связать (параметры у них типа
узел).
С одной стороны, после этого шага мы можем быть довольны - внешние
требования к проектируемому комплексу услуг в первом приближении выполнены.
С другой стороны, появилась необходимость определить упомянутые на
предыдущих шагах типы данных.
[Так всегда - завершив абстракцию и конкретизацию верхнего уровня,
создаем почву для аналогичной работы на нижнем уровне и наоборот].
Шаг 2.4 (строка 2). Определяем тип узел. Этот тип уже частично нами
охарактеризован (где?) - данные этого типа могут служить аргументами всех
процедур и функций, объявленных в нашем пакете. Другими словами, этот тип
уже охарактеризован по фактору применимых операций. Выписывая его явное
определение, мы характеризуем данные этого типа по фактору изменчивости -
указываем, что диапазон (range) их возможных значений - целые числа от 1 до
числа макс_узлов (пока еще не определенного). Одновременно мы относим
объявляемый тип к категории целых числовых типов и тем самым завершаем его
характеристику по фактору применимых операций (в Аде для целых типов
предопределены обычные операции целой арифметики - сложение "+", вычитание
"-", умножение "*" и др.).
Шаг 2.5 (строки 6-10). Определяем тип "связи" результата функции
все_связи. Замысел в том, чтобы эта функция сообщала число связей указанного
узла и перечень связанных с ним узлов. В Алголе-60 или Фортране не могло
быть функций, которые в качестве результата выдают составной объект. В Аде
можно ввести составной тип, объекты которого либо состоят из однотипных
подобъектов - являются массивами, либо из разнотипных - являются записями.
Результат задуманной нами функции все_связи - пара разнотипных объектов
(число и узлы). Другими словами, это запись, первое поле которой называется
"число", а второе - "узлы". Тип значений первого поля назван "число_связей",
второго - "перечень_связей".
В этом же объявлении указано, что при создании объекта типа "связи" его
поле "число" получает начальное значение 0. Это так называемая ИНИЦИАЛИЗАЦИЯ
объектов, которой нет, например, в Алголе-60, но для знающих Фортран - дело
привычное (вспомните объявление начальных данных DATA).
Итак, на шаге 2.5 снова кое-что определилось, но опять появились новые
имена - число_связей и перечень_связей.
Шаг 2.6 (строка 5). Перечень_связей определяем как регулярный тип
одномерных массивов, составленных из объектов типа узел, доступ к которым -
по индексам типа индекс_узла.
Шаг 2.7 (строка 4). Индекс_узла определяем как тип объектов, значения
которых лежат в диапазоне целых чисел от 1 до макс_связей (максимального
допустимого числа связей у узла в сети - оно пока не определено).
Шаг 2.8 (строка 3). Число_связей определяем как тип объектов, значения
которых лежат в диапазоне целых чисел от 0 до макс_связей . Как видите, этот
тип похож на предыдущий, но отличается своей ролью и диапазоном значений.
Остались неопределенными только имена макс_узлов и макс_связей. Их
неудобно фиксировать в том же модуле - ведь они могут изменяться в
зависимости от потребностей пользователя и наличных ресурсов. Поэтому будем
считать, что эти имена определены во внешнем для нашего модуля контексте, а
именно в пакете с именем "параметры_сети". Доступ к этому контексту из
модуля управление_сетью обеспечивается его нулевой строкой.
Это так называемое УКАЗАНИЕ КОНТЕКСТА. После ключевого слова with в нем
перечисляются пакеты, объявления из которых считаются видимыми в модуле,
непосредственно следующем за таким указанием.
Пакет параметры_сети можно определить, например, так:
1. package параметры_сети is
2. макс_узлов : constant INTEGER := 100;
3. макс_связей: constant INTEGER := 8;
4. end параметры_сети;
Тем самым макс_узлов определено в качестве ПОСТОЯННОЙ целого типа со
значением 100, а макс_связей - в качестве постоянной того же типа со
значением 8. Значения постоянных нельзя менять при исполнении программы (вот
еще один элемент прогнозирования и контроля в Аде).
2.4. Замечания о конструктах
Рассмотрим написанные фрагменты программы еще раз. Теперь поговорим о
строении, смысле и назначении использованных конструктов.
В целом мы написали две СПЕЦИФИКАЦИИ ПАКЕТА. Отличительный признак
этого конструкта - ключевое слово package (пакет). Спецификация пакета
содержит объявления имен, которые становятся доступными при использовании
пакета посредством указания контекста (например, объявления из пакета
параметры_сети становятся доступны в пакете управление_сетью, если указать
контекст with параметры_сети).
Спецификацию пакета можно оттранслировать и поместить в ТРАНСЛЯЦИОННУЮ
БИБЛИОТЕКУ. Получится модуль, пригодный для связывания (посредством указаний
контекста) с другими (использующими его) модулями в процессе их трансляции и
загрузки.
Пакет может состоять из одной спецификации или из спецификации и тела.
Например, для пакета параметры_сети тело не требуется в отличие от пакета
управление_сетью (как Вы думаете, почему?).
Если пакет состоит из двух частей (спецификации и тела), то выполнять
программу, в которой отсутствует одна из них, нельзя. Однако для трансляции
использующих модулей достаточно одной только спецификации используемого
пакета. Итак, создавать и транслировать спецификации пакетов можно отдельно
от их тел, но исполнять - только совместно с телами пакетов. В спецификацию
пакета входит совокупность ОБЪЯВЛЕНИЙ. Вернемся к программе на стр. Вставить
вычисление номера
В каждой из строк 2-3 спецификации пакета параметры_сети находится
ОБЪЯВЛЕНИЕ ПОСТОЯННОЙ, точнее, ОБЪЯВЛЕНИЕ ЧИСЛА. Это одна из разновидностей
ОБЪЯВЛЕНИЯ ОБЪЕКТА. Назначение всякого объявления объекта - связать имя с
характеристиками поведения объекта, названного этим именем. Поэтому
обязательными компонентами объявления служат само вводимое имя, ключевые
слова, отличающие разновидность объявления и тем самым характеризующие
поведение объявляемого объекта в целом, и компоненты-параметры, уточняющие
характеристики поведения.
Так, в строке 2 объявляемое имя - макс_узлов, уточняющие параметры -
имя типа (INTEGER) и константа 100 (изображение целого числа). Полное
объявление связывает с именем объекта макс_узлов тип INTEGER и константу 100
как характеристику поведения объекта. Попросту говоря, имя макс_узлов
начинает обозначать константу 100 типа INTEGER.
[Чтобы понять, зачем нужно обозначать константы именами, достаточно
представить себе программу, где константа 100 используется в десяти местах,
и допустить, что нужно изменить ее значение на 200. Тогда в нашей
спецификации достаточно изменить одну цифру в строке 2, а иначе пришлось бы
изменять десять мест с риском где-нибудь заменить не ту константу (или не на
то значение). Так объявления постоянных способствуют надежности Ада-
программ].
Перейдем к спецификации пакета управление_сетью. В каждой из ее строк
2,3 и 4 мы написали ОБЪЯВЛЕНИЕ ТИПА. В нем всегда указывают, как
совокупность значений объявляемого типа образуется из совокупности значений
ранее известных типов (предопределенных или ранее объявленных). В нашем
случае в строке 2 указано, что новый тип узел образован из предопределенного
типа INTEGER (является типом, ПРОИЗВОДНЫМ от типа INTEGER), причем данные
типа узел могут обозначать только целые из диапазона от 1 до макс_узлов. В
строке 3 и 4 аналогичные сведения сообщаются о типах число_связей и
индекс_узла, только здесь указаны другие диапазоны.
Напомним, зачем нужны объявления типов. В том модуле, где будет
использоваться пакет управление_сетью, можно объявить переменную (например,
А) типа узел и переменную (например, В) типа число_связей. Так вот
переменную А можно указать в качестве аргумента процедуры "вставить" или
"связать", а переменную В - нельзя. Это ошибка, обнаруживаемая при
трансляции, В сущности, ради такого контроля и нужны объявления типов,
прогнозирующие поведение (возможные роли) соответствующих данных.
В строке 5 - объявление типа, но на этот раз не скалярного (как в
строках 2-4), а СОСТАВНОГО, точнее РЕГУЛЯРНОГО. Указано, как значения нового
типа перечень_связей образуются из значений типов узел и индекс_узла.
Именно, значения типа перечень_связей - это одномерные (так как указан лишь
один диапазон индексов) МАССИВЫ, компонентами которых служат значения типа
узел, а доступ к этим компонентам - по индексам типа индекс_узла.
В строках 6-10 - также объявление составного типа, но на этот раз -
КОМБИНИРОВАННОГО. Указано, что значениями нового типа "связи" могут быть
любые ЗАПИСИ с двумя полями. Первое поле с именем "число" и допустимыми
значениями типа "число_связей" (при создании записи этому полю присваивается
начальное значение 0). Второе поле с именем "узлы" типа перечень_связей.
Если в модуле, использующем наш пакет, объявлена переменная, например,
X типа "связи" и I типа индекс_узла, то через
X.узлы(I)
обозначается значение типа узел, которое служит I-й компонентой поля
"узлы" переменной X.
Строки 11 и 15 - это примечания, не влияющие на смысл модуля.
Примечанием считается остаток любой строки, начинающийся с двух минусов.
В строках 12-14 - ОБЪЯВЛЕНИЯ ПРОЦЕДУР. В скобках указаны имена
(названия) формальных параметров, их типы и РЕЖИМ использования (in - только
для чтения - ВХОДНЫЕ параметры; out - только для записи - ВЫХОДНЫЕ; in out -
и для чтения, и для записи - ОБНОВЛЯЕМЫЕ). Режим in напоминает вызов
параметров значением в Алголе или Паскале, in out - вызов параметров со
спецификацией var в Паскале или ссылкой в Фортране, out - точного аналога в