В.Ш. Кауфман - Языки программирования - концепции и принципы (1990) (1160787), страница 41
Текст из файла (страница 41)
качестве языка спецификаций в настоящее время довольно широко
распространено.]
11.3.2. Комментарий
Итак, в контексте пакета "последовательный обмен" внешний мир
исполнителя представлен совокупностью внешних файлов, идентифицируемых по
уникальным именам-строкам. Дополнительные свойства внешних файлов
(возможность только вводить, только выводить, данные о разметке и т.п.)
указываются в специальной строке "доступ". Таким образом, внешние файлы -
это абстрактные внешние устройства.
Во внутреннем мире исполнителя внешние файлы представлены внутренними
объектами ограниченного приватного типа "файловый" с набором операций и
исключений, зафиксированным в определяющем пакете последовательный_обмен.
После соответствующей конкретизации (настройки) этого пакета на нужный тип
вводимых и (или) выводимых данных (тип_элемента) в контексте этого пакета
можно:
1. объявлять внутренние файлы:
А,В : файловый;
2. создавать внешние файлы и связывать их с объявленными внутренними:
создать (А, вывод, "пример", "последовательный");
При этом правила указания имени и доступа зависят от конкретной внешней
среды ("определяются реализацией").
3. открывать ранее созданные внешние файлы, связывая их с внутренними:
открыть (А, ввод, "пример", "последовательный");
Ясно, что открывать для ввода имеет смысл только такие внешние файлы, в
которые ранее что-то уже выводилось, либо файлы, которым в реальной внешней
среде соответствуют источники данных (клавиатура, устройство ввода с
перфокарт и т.п.).
4. закрывать файлы, разрывая связь внутреннего файла с внешним:
закрыть (А);
Операция открытия может потребовать установки подходящего диска или
кассеты с нужным внешним файлом на подходящее устройство обмена. Операция
закрытия позволяет освободить это устройство для других целей.
5. удалять файлы из внешней среды, делая их впредь недоступными:
удалить (А);
Этой операцией следует пользоваться очень осторожно.
6. установить файл в начальную позицию.
Позиции линейно упорядочены начиная с 1. Операция чтения или записи
увеличивает позицию на 1 (после своего выполнения). Понятие позиции касается
внутренних, а не внешних файлов. Допустимо связывать с одним внешним файлом
несколько внутренних. Они могут быть разных режимов и могут находиться в
разных позициях. Однако это возможно не во всякой среде.
7. узнать режим обмена, внешнее имя, характеристику доступа, а также
узнать, открыт ли файл.
8. наконец, можно прочитать или записать объект данных нужного типа.
Например, если объявлен тип "таблица", то после конкретизации
package обмен_таблиц is new последовательный_обмен (таблица);
use обмен_таблиц;
Т : таблица;
можно объявить
А : файловый; ...
открыть (А, вывод, "таблицы", "последовательный");
loop -- формирование таблицы
писать (А, Т);
end loop ;
закрыть (А);
Затем в аналогичном контексте можно прочитать сформированный ранее файл
таблиц :
открыть (А, ввод, "таблицы", "последовательный");
if not конец_файла (А) then читать (А, Т);
закрыть (А);
Тем самым показано и применение функции "конец_файла". Смысл исключений
указан в комментариях определяющего пакета.
Вопрос. Зачем нужен отдельный пакет исключений обмена, а также
переименования в родовом пакете последовательный_обмен? Почему нельзя просто
объявить исключения в этом родовом пакете?
Ответ. Такой прием описания позволяет организовать единообразную
реакцию на исключения обмена. Если бы исключения не были переобъявлены, то
они не были бы видимы в контексте, где применяется пакет
последовательный_обмен (они были бы видимы только в самом этом пакете).
Поэтому нельзя было бы в этом контексте задавать реакцию на эти исключения.
А если объявить исключения в родовом пакете, то для каждой конкретизации
этого пакета они были бы своими (другими) и при совместном применении
различных конкретизаций (для обмена данных различных типов) попытка задать
реакцию на исключения приводила бы к неудобствам или конфликту наименований.
Доказана неформальная теорема : исключения обмена рационально объявлять
в предопределенном пакете и переименовывать в родовых специализированных
пакетах.
Вопрос. Почему реализация обмена родовая? Почему нельзя в одном пакете
определять обмен данных различных типов?
Ответ. В соответствии с концепцией уникальности типа процедуры обмена
должны иметь точную спецификацию параметров. Тем самым фиксируется тип
обмениваемых данных. Отсюда следует и фундаментальное свойство адовских
файлов - однородность - каждый файл характеризуется единым типом элементов
(все элементы файла - одного типа!).
Доказана еще одна неформальная теорема : концепция уникальности типа
влечет однородность файлов.
Представление внутренних объектов во внешних файлах в общем случае в
Аде не определено. Более того, оно не обязано быть зафиксированным в каких-
либо документах, доступных программисту. Это представление "зависит от
реализации", но не "определяется реализацией" (последнее означает, что
свойство обязано быть описано в документации для программистов).
Поэтому вводить можно только то, что ранее было выведено с помощью
пакета для того же типа данных. Другими словами, последовательный (и прямой)
обмен "неполноценен" в том отношении, что создавать и потреблять внешние
данные при таком обмене невозможно без компьютера.
Это еще одна неформальная теорема. Она не распространяется на текстовый
обмен, при котором можно вводить любые данные, представленные в соответствии
с синтаксисом ЯП Ада. Именно синтаксис и служит документом, фиксирующим в
этом случае правила представления внутренних объектов во внешних файлах. При
текстовом обмене допустимы и некоторые дополнительные возможности
структуризации текстов (форматирования), выходящие за рамки синтаксиса Ады
(разбиение на строчки, страницы и т.п.).
В пакете прямой_обмен предоставлена возможность управлять позицией
(индексом) обмена в операциях "читать" и "писать", а также устанавливать и
узнавать текущую позицию с помощью операций установить_индекс и дай_индекс.
Функция "размер" позволяет узнать максимальное значение индекса, по которому
производилась запись в данный файл (т.е. узнать число элементов во внешнем
файле).
Последовательный и прямой обмены учитывают относительную независимость
внешних объектов, их динамизм и (частично) разнообразие внешних устройств.
Однако совершенно не учитывают человеческий фактор. В сущности,
последовательный и прямой обмены предназначены для взаимосвязи с
устройствами внешней памяти (магнитными лентами, магнитными дисками и т.п.)
и не предназначены для взаимодействия с человеком или устройствами, которые
служат не для хранения данных (датчики, органы управления и т.п.).
Аппаратом, явно учитывающим человеческий фактор, в Аде служит
предопределенный пакет текстовый_обмен. Приведем только его структуру,
которая нам понадобится в дальнейшем:
with исключения_обмена;
package текстовый_обмен is -- это не родовой пакет!
... -- далее идут вложенные родовые пакеты
generic -- Родовой пакет для обмена значений целых типов
type число is range <>;
package целочисленный_обмен is ...
generic -- Родовые пакеты для обмена вещественных
type число is digits <>;
package плавающий_обмен is ...
generic
type число is delta <>;
package фиксированный_обмен is ...
generic -- Родовой пакет для обмена перечисляемых типов.
type перечисляемый is (<>);
package перечисляемый_обмен is ...
exception ... -- исключения (как в последовательном обмене
-- плюс одно дополнительное "нет_места")
private ... -- определяется реализацией
end текстовый_обмен ;
11.3.3. Пример обмена. Программа диалога
Постановка задачи. Следует организовать диалог с пользователем
системы, хранящей сведения о товарах (например, автомобилях), имеющихся в
продаже.
Обратите внимание, это совершенно новый вид задачи. Мы уже
программировали алгоритм вычисления некоторой функции - задача ставилась в
форме спецификации требуемой функции. Программировали совокупность модулей,
предоставляющую комплекс программных услуг - задача ставилась в форме
спецификации перечня услуг. Теперь нужно организовать диалог. Это не функция
и не комплекс услуг - это взаимодействие.
Удобной формой спецификации взаимодействия служит сценарий. Другими
словами, это описание ролей партнеров по взаимодействию (описание их
поведения с учетом возможного поведения партнера). Отличие от обычного
театрального сценария в том, что в общем случае последовательность действий
партнеров не фиксируется.
Вопрос. В чем отличие сценария от комплекса услуг?
Таким образом, при решении диалоговых задач начинать проектирование
следует с разработки сценария как исходной "функциональной" спецификации
задачи, а затем продолжать решение обычной детализацией.
Сценарий нашего диалога прост.
Система: начинает диалог, предлагая пользователю выбрать желательный
цвет (автомобиля).
Пользователь: отвечает, печатая название цвета (тем самым запрашивая
автомобиль указанного цвета).
Система: в ответ на запрос сообщает число автомобилей нужного цвета,
имеющихся в продаже, либо указывает на ошибку в запросе и предлагает
повторить попытку.
Пример диалога (Ответы пользователя - справа от двоеточия)
Выберите цвет: Черный.
Недопустимый цвет, попытаемся еще раз.
Выберите цвет: Голубой.
Голубой цвет : 173
Выберите цвет: Желтый.
Желтый цвет : 10
Программа диалога. Приведем вариант программы диалога.
with текстовый_обмен; use текстовый_обмен;
procedure диалог is
type цвет is (белый, красный, оранжевый, желтый,
зеленый, голубой, коричневый);
таблица : array (цвет) of INTEGER := (20,17,43,10,28,173,87);
выбранный_цвет : цвет;
package для_цвета is new перечисляемый_обмен (цвет);
package для_чисел is new целочисленный_обмен (INTEGER);
use для_цвета, для_чисел;
begin
loop
declare -- блок нужен для размещения реакции на исключение
-- ввод цвета:
послать ("Выберите цвет" :");
получить (выбранный_цвет);
-- конец ввода цвета
-- вывод ответа:
установить_колонку (5); -- отступ - 5 позиций
послать (выбранный_цвет);
послать ("цвет :");
установить_колонку (40); -- чтобы выделялось
-- количество автомобилей
послать (таблица(выбранный_цвет), 4); -- размер поля в 4 позиции
-- достаточен для чисел из таблицы
новая_строчка; -- конец вывода ответа
exception -- реакция на ошибки пользователя
when данные_неправильные => послать ("Недопустимый цвет. Еще раз.");
новая_строчка (2);
end; -- конец блока (и реакции на ошибку)
end loop;
end диалог;
Использованы подразумеваемые внешние файлы. Обычно это клавиатура для
ввода и экран для вывода. Точнее управлять назначением устройств ввода-
вывода в рамках абстрактной модели невозможно. Требуются знания (а возможно
и операции), описываемые в конкретной реализации ЯП.
11.3.4. Отступление о видимости и родовых пакетах
Мы уже отмечали ряд неприятных свойств аппарата управления видимостью в
Аде. Покажем еще один пример, когда он проявляет себя не лучшим образом.
Допустим, что пользоваться обменом для нескольких конкретных типов
(например, чисел, цветов, строк) приходится часто и возникает идея создать
подходящий пакет, полностью обеспечивающий нужный контекст. Слово
"полностью" подчеркивает естественное требование, чтобы для работы в нужном
контексте пользователю было достаточно написать указатель контекста с одним
только именем такого пакета. Пусть для определенности нужны именно те
процедуры, которыми мы воспользовались при программировании диалога.
Казалось бы, достаточно написать пакет
with текстовый_обмен; use текстовый_обмен;
package обмен_чисел_цветов_строк is