Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Языки программирования. Концепции и принципы

.pdf
Скачиваний:
2
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
Обмен с внешней средой
послать("цвет:"); установить_колонку (40);
-- чтобы выделялось количество автомобилей,
послать(таблица(выбранный_цвет), 4);
-- размер поля в 4 позиции достаточен
-- для чисел из таблицы
новая_строчка; -- конец вывода ответа
exception
-- реакция на ошибки пользователя when данные_неправильные =>
послать("Недопустимый цвет. Еще раз."); новая_строчка(2);
end; -- конец блока (и реакции на ошибку)
end loop;
end диалог;
231
Использованы подразумеваемые внешние файлы. Обычно это клавиатура для ввода и экран для вывода. Точнее, управлять назначением устройств вводавыво да в рамках абстрактной модели невозможно. Требуются данные (а возможно, и операции), описываемые в конкретной реализации ЯП.
11.3.4. Отступление о видимости и родовых пакетах
Мы уже отмечали ряд неприятных свойств аппарата управления видимостью в Аде. Покажем еще один пример, когда он проявляет себя нелучшим образом.
Допустим, что пользоваться обменом для нескольких конкретных типов (на пример, чисел, цветов, строк) приходится часто, и возникает идея создать подхо дящий пакет, полностью обеспечивающий нужный контекст. Слово «полностью» подчеркивает естественное требование, чтобы для работы в нужном контексте пользователю было достаточно написать указатель контекста с одним только име нем такого пакета. Пусть для определенности нужны именно те процедуры, кото рыми мы воспользовались при программировании диалога. Казалось бы, доста точно написать пакет
with текстовый_обмен; use текстовый_обмен; package обмен_чисел_цветов_строк is
type цвет is (белый, ... .коричневый);
package для_цвета is new перечисляемый_обмен(цвет);
package для_чисел is new целочисленный_обмен(INTEGER);
use для_цвета; для_чисел; end обмен_чисел_цветов_строк;
Так что процедуру, аналогичную нашей процедуре «диалог», можно начинать так:
with обмен_чисел_цветов_строк; use обмен_чисел_цветов_строк; procedure новый_диалог is ...;
232
Современное состояние языков программирования
Однако правила видимости в Аде не позволяют так работать. Ведь в процедуре «новый_диалог» будет непосредственно видимо лишь то, что объявлено в пакете из указателя сокращений use. Так что нужные процедуры придется указывать полным именем. Например:
для_чисел.послать(...); для_цветов.получить(...); и т. п.
Таким образом, пользователь вынужден знать не только имя пакета, но и его содержимое, да к тому же вынужден применять длинные имена. Это явно не вхо дило в наши планы. Другими словами, мы не достигли нужного уровня абстрак ции предоставляемой услуги.
Чтобы его достичь, необходимо в пакете для_чисел_цветов_строк применить переименование всех нужных процедур из предопределенных пакетов. Например:
procedure послать(элемент : out цвет;
поле : in размер_поля := для_цвета.подразумеваемое_поле;
нижний : in BOOLEAN := для_цвета.подразумеваемый_нижний)
renames для_цвета.послать;
И так для всех (!) нужных процедур.
Назовем отмеченную неприятность проблемой транзита импортированных имен (ведь суть проблемы – в передаче заимствованных коротких имен через про межуточный модуль). Эта проблема характерна и для других ЯП с развитым управлением контекстом (например, для Модулы2, которой мы еще займемся). В Аде она решена (в отличие от той же Модулы2), хотя и с весомыми накладны ми расходами.
Упражнение (повышенной трудности). Предложите и обоснуйте решение пробле мы транзита.
Итак, абстрактная модель Ады характеризуется:
1) понятием файла, разграничением внешних и внутренних файлов и предоп ределенным типом «файловый»;
2) развитым аппаратом связывания с файлами подходящих наборов опера ций, представленным совокупностью пакетов с родовыми параметрами;
3) однородностью файлов;
4) полиморфизмом и вариаргументностью предоставляемых операций (вари аргументность – это возможность применять процедуры с переменным числом аргументов за счет подразумеваемых значений опущенных аргу ментов);
5) форматированием, встроенным непосредственно в операции управления обменом (отсутствует, например, аналог именованного формата в Форт ране).
Упражнение. Придумайте варианты развития описанного нами диалога и реали зуйте его. В случаях, когда возникают сомнения в точной семантике операций, са мостоятельно опишите (и обоснуйте) нужное вам уточнение эффекта операции или воспользуйтесь литературой по Аде.
Обмен с внешней средой
233
11.4. Программирование специальных устройств
Чтобы управлять специальным внешним устройством, необходимо иметь соот ветствующую аппаратуру – само устройство и аппаратный адаптер, связываю щий подключаемое устройство с основным исполнителем. При этих условиях новое устройство становится источником (и потребителем) определенных воз действий на исполнитель (со стороны исполнителя). Так как обычно внешнее устройство работает асинхронно с основным исполнителем, целесообразно рас сматривать его как задачу, с которой можно взаимодействовать посредством ап паратных прерываний, специальных регистров, выделенных адресов и т. п. Назо вем такую задачу аппаратной.
Изменить принцип функционирования аппаратной задачи (перепрограмми ровать ее) часто практически невозможно или нецелесообразно. Будем считать его фиксированным. С другой стороны, с точки зрения создаваемого комплекса программ, возможности обмена, предоставляемые аппаратной задачей, обычно выглядят как возможности излишне низкого уровня (абстракции). Они детально отражают поведение устройства и обычно весьма далеки от планируемых его содержательных функций в создаваемой системе.
Единственный способ построить более содержательное (и вместе с тем более абстрактное) устройство – создать программную модель устройства в виде асинх ронного процесса, взаимодействующего с аппаратной задачей. Такая программ ная модель называется драйвером устройства.
При создании драйвера естественно моделировать аппаратные регистры и ад реса данными подходящих типов, использовать спецификацию представления для привязки этих данных к конкретным компонентам аппаратуры, а аппаратные прерывания использовать для синхронизации драйвера с аппаратной задачей, связывая входы драйвера с адресами соответствующих аппаратных прерываний.
Итак, общий метод программирования специальных внешних устройств сво дится к построению подходящего драйвера. При этом оказываются полезными развитые типы данных и спецификации их представления.
Важно понимать, что специфицировать представление приходится не только для привязки к адресам и структуре регистров. Например, перечисляемые типы удобны в моделидрайвере, но их обычно аппаратура не воспринимает. Поэтому часть работы, в обычных условиях «отдаваемой на откуп» компилятору, прихо дится делать вручную, выписывая явно конкретные коды для значений перечис ляемого типа. Например:
type защита is(обычная, ограниченная, строго_ограниченная,
секретная, совершенно_секретная); for защита use(обычная => 0, ограниченная => 1,
строго_ограниченная => 2, секретная => 4,
совершенно_секретная => 8);
234
Современное состояние языков программирования
В результате гарантируется, что драйвер передаст аппаратуре вполне опреде
ленные числовые коды, предусмотренные ее конструкцией.
Рассмотрим пример драйвера из [17]. Символ, вводимый с клавиатуры, выра батывает прерывание с адресом 8#100# и выдачей соответствующего символа в буферный регистр. (Человек за клавиатурой играет роль аппаратной задачи.)
Напишем драйвер, который сохраняет введенный символ в локальной пере менной, доступной из любой обслуживаемой задачи по входу «взять_символ»:
task драйвер_клавиатуры is
entry взять_символ(симв : out символьный);
entry есть_символ; -- аппаратное прерывание
for есть_символ use at 8#100#; end драйвер_клавиатуры;
-- драйвер позволяет обслуживаемой задаче быть независимой
-- от конкретных адресов и прерываний и в этом смысле
-- служит абстрактной моделью клавиатуры.
task body драйвер_клавиатуры is
символ : символьный; -- рабочая переменная
буф_регистр : символьный;
for буф_регистр use at 8#177462#;
-- так элементы аппаратуры представляются данными адовских типов
begin
loop
accept есть_символ do символ := буф_регистр end есть_символ; accept взять_символ(симв : out символьный) do
симв := символ;
end взять_символ;
end loop; end драйвер_клавиатуры;
Достигнув в цикле оператора приема «есть_символ», драйвер ждет аппаратно го прерывания. Предварительно он обязан разрешить его и заслать в вектор адре сов прерываний ссылку на тело оператора приема этого входа. Делать все это обязана реализация оператора приема аппаратного входа в Аде. Отличить аппа ратный вход можно по спецификации его представления.
После прерывания тело оператора приема выполняется (происходит аппарат нопрограммное рандеву). И драйвер готов обслужить машинно независимую за дачу по входу «взять_символ». Здесь рандеву самое обычное. Далее все повторя ется в бесконечном цикле.
Итак, при программировании специального устройства потребовалось:
• построить модель аппаратной задачи (в нашем случае она представлена пе
ременной буф_регистр и входом есть_символ);
• связать эту модель с конкретной аппаратурой (в нашем случае – две специ
фикации представления);
• построить на основе аппаратной модели содержательную модель устрой
ства – задачудрайвер.
Обмен с внешней средой
Теперь можно пользоваться драйвером в качестве специального устройства
обмена в прикладных программах.
Упражнение. Разберите примеры управления специальными устройствами в кни
гах Пайла [17] и Янга [18]. Убедитесь, что в них явно или неявно присутствуют и
аппаратная модель, и связывание, и драйвер.
На этом закончим построение модели А (а тем самым изучение основных абст
ракций современного индустриального ЯП с технологической позиции) – изу ченные аспекты Ады и составили модель А.
235
Глава 12
Два альтернативных принципа создания ЯП
12.1. Принцип сундука ................... 238
12.2. Закон распространения
сложности ЯП ................................ 238
12.3. Принцип чемоданчика........... 239
12.4. Обзор языка Модула!2.......... 239
12.5. Пример М!программы .......... 241
12.6. Языковая ниша...................... 248
12.7. Принцип чемоданчика в проектных решениях ЯП
Модула!2 ....................................... 249
12.8. Принцип чайника .................. 257
12.9. ЯП Оберон ............................ 258
238
Современное состояние языков программирования
12.1. Принцип сундука
Построив модель А, мы достигли переломного момента книги. До сих пор накап ливалось знание о технологических потребностях и удовлетворяющих эти по требности языковых конструктах. Теперь накоплено достаточно материала, что бы взглянуть на него критически, с позиции автора современного ЯП.
И раньше мы не забывали об авторской позиции, выделяли концепции и кон структы, помогающие создавать ЯП, анализировать и оценивать его. Однако эти принципы и концепции, как правило, касались отдельных групп конструктов. Наша ближайшая крупная цель – указать на два принципа, касающихся ЯП в це лом, и обсудить их влияние на современное языкотворчество.
Для краткости и выразительности дадим им названия «принцип сундука» и «принцип чемоданчика».
На примере Ады мы видели, как выявляемые технологические потребности приводили к новым конструктам. Может показаться, что на этом пути будут полу чаться все более высококачественные ЯП. К сожалению, большинство современ ных индустриальных ЯП носят на себе родимые пятна такого примитивного кри терия качества. Это характерно и для Кобола, и для ПЛ/1, и для Фортрана77, и для Ады.
Основной принцип конструирования, которым руководствовались авторы этих ЯП, в упрощенной форме можно сформулировать так: для каждой значимой в ПО технологической потребности в языке должно быть готовое выразительное средство. Короче: каждой значимой потребности – готовый конструкт. Этот принцип заготовленности конструктов и назовем принципом сундука (именно в сундуках хранят много всякого на всякий случай).
Как показывает опыт, безудержное применение принципа сундука ведет к гро моздким, сложным, дорогим в реализации, обучении и использовании языкам монстрам с тяжеловесным базисом и несбалансированными средствами развития. Сундук и есть сундук!
12.2. Закон распространения сложности ЯП
Бывают взаимодействия, сложность которых по существу не зависит от собствен ной сложности взаимодействующих объектов. Например, процесс и результат столкновения человека с автомобилем в первом приближении никак не связаны со сложностью человека и автомобиля. Сложность вызова процедуры непосред ственно не связана с ее внутренней сложностью и сложностью вызывающего контекста. В подобных ситуациях сложность инкапсулирована. Образно говоря, простота взаимодействия обеспечивается «небольшой площадью» взаимодей ствия потенциально весьма сложных объектов.
С другой стороны, язык программирования сам служит «поверхностью» взаи модействия авторов, реализаторов, преподавателей и пользователей ЯП.
Два альтернативных принципа создания ЯП
Такая специфическая роль ЯП определяет справедливость для него следую
щего закона распространения сложности: собственная сложность ЯП распрост раняется на все аспекты его «жизни» (описание, реализацию, использование, обу чение и т. д.). Никлаус Вирт отмечает частный случай этого закона [19] как самое главное, что следует усвоить о реализации ЯП.
Н. Вирт – один из самых авторитетных специалистов по ЯП, лауреат премии
Тьюринга за создание таких известных ЯП, как Алгол W, Паскаль, Модула, Мо
дула2.
Итак, ориентация на принцип сундука повышает собственную сложность ЯП,
что по закону распространения сложности приводит к росту сложности его освое ния (которая, в свою очередь, может оказаться катастрофической для его судь бы – достаточно сопоставить судьбу Паскаля с судьбой Алгола68).
Вспомним, однако, что основной принятый нами критерий качества базового
ЯП – его способность снижать сложность, помогать в борьбе с основной пробле мой программирования. Налицо тупик, в который ведет принцип сундука. Вирту принадлежит принцип, указывающий выход из этого тупика.
239
12.3. Принцип чемоданчика
Н. Вирт неоднократно отмечал, что самое трудное при создании ЯП – решить, от чего следует отказаться. Объясняя принципы конструирования своего (теперь
уже предпоследнего) ЯП Модула2 (поразившего специалистов элегантностью), Вирт развил эту идею и сформулировал следующий принцип языкового миниму
ма: в ЯП следует включать только такие концепции и конструкты, без которых совершенно невозможно обойтись.
Назовем этот принцип минимума принципом чемоданчика по контрасту с прин
ципом сундука (в чемоданчик кладут только абсолютно необходимое).
Продемонстрируем этот важнейший метаязыковый принцип на примере конк
ретных решений, принятых при создании ЯП Модула2, и сопоставим их с реше ниями, воплощенными в Аде. Но сначала придется в общих чертах познакомиться с Модулой2.
12.4. Обзор языка МодулаE2
Язык Модула2, созданный в конце 70х годов, прямой наследник ЯП Паскаль и Модула, созданных Н. Виртом в конце 60х и середине 70х годов соответственно. Это ЯП общего назначения, ориентированный на относительно скромные ре сурсы как инструментальной, так и целевой машины. Автор предназначал его для небольших, в том числе персональных, компьютеров («для небольшой однопро цессорной ЭВМ»).
В Модуле2 автору удалось соединить простоту и естественность основных
конструктов Паскаля с мощными и изящными средствами модуляризации. Коро че говоря, Модула2 – это модульный Паскаль.
240
Специалисты обратили внимание на очередное достижение Вирта с первых публикаций. В настоящее время интерес к Модуле2 становится всеобщим, так как появились высококачественные его реализации. Замечательная особенность ЯП, обеспечившая его достоинства, – следование принципу чемоданчика.
Современное состояние языков программирования
12.4.1. Характеристика Модулы'2 в координатах фон'неймановского языкового пространства (технологическая позиция)
По сравнению с моделью А отсутствуют производные типы; концепция типа ори ентирована скорее на структуру, чем на имена; существенно меньше предопреде ленных типов; резко ограничен аппарат управления асинхронными процессами; сильно упрощены управление видимостью и аппарат раздельной компиляции (в частности, отсутствуют вторичные модули); отсутствует аппарат управления точностью расчетов; упрощена предопределенная модель обмена; отсутствуют родовые модули; резко ограничено управление представлением; отсутствует управление исключениями.
С другой стороны, с точки зрения языкового пространства Модула2 ничего не добавляет к модели А. Иначе говоря, верна неформальная теорема: для всякого
конструкта Модулы2 в Аде найдется конструкт с аналогичными возможностя ми, то есть Модулу2 можно назвать технологическим подмножеством Ады.
12.4.2. Характеристика Модулы'2 в терминах концептуальной схемы
Для краткости свойства Модулы2 назовем Мсвойствами, а свойства Ады (Амо дели) – Асвойствами.
Базис. Скалярная Мсигнатура – точное подмножество Асигнатуры. Отсут ствуют фиксированные вещественные типы и вообще управление точностью. Но имеются целые, вещественные, символьные, логические типы (с более или менее устоявшимся набором операций).
Структурная Мсигнатура содержит регулярные, комбинированные и ссылоч ные типы, аналогично Асигнатуре. Но в ней имеются также процедурные и мно жественные типы. Правда, весьма ограниченные.
Именно значением процедурного типа не может быть стандартная процедура или процедура, вложенная в другую процедуру. А «множественный» тип (SET OF Т), класс значений которого состоит из всех возможных множеств значений исходного типа Т, можно образовывать только для исходных типов малой мощ ности (чтобы множество можно было представить одним машинным словом – двоичной шкалой; это пример влияния на язык реализаторской позиции).
Имеются обычные управляющие структуры (последовательности, развилки, циклы, процедуры) и ограниченные средства управления асинхронными процес
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]