В.Ш. Кауфман - Языки программирования - концепции и принципы (1990) (1160787), страница 47
Текст из файла (страница 47)
из текста реализации, причем вне модуля видимо то и только то из реализации,
что "проявлено" в спецификации. Назовем это идеей "экспортного окна", чтобы
подчеркнуть, что экспортируется не более того, что имеется в реализации.
Вопрос. Что это дает?
Подсказка. Смотрите перечень средств, не вошедших в Оберон.
Вопрос. Знаете ли Вы примеры ЯП, где в спецификации может оказаться не
только содержимое реализации.
Основные сокращения по сравнению с Модулой-2 касаются тех средств,
которые функционально перекрываются обогащением типов, а также некоторых
средств, по мнению Вирта не оправдавших себя в базовом ЯП.
Типы данных :
нет вариантных комбинированных типов - вместо них работают обогащенные;
нет закрытых типов - вместо них работает общая идея управляемого
"проявления" компонент реализации за счет их цитирования в спецификации.
Особенно красиво это взаимодействует с обогащением типов. Например, если в
реализации (теле модуля) содержатся объявления приведенных выше типов T, T1,
T2, то в спецификации можно указать
TYPE T1 = RECORD z: REAL; END;
скрыв не только некоторые поля, но и "происхождение" типа. Вместе с тем,
процитировав спецификации нужных операций, легко сделать их (и только их)
доступными пользователям типа T1. Все содержательные возможности скрытых и
приватных типов при этом сохранены (так как в Обероне спецификации и
реализации размещаются и транслируются обязательно вместе);
нет перечисляемых, поддиапазонов, тип множества только один
(предопределенный над целыми), нет типа CARDINAL.
Вопросы. Как именно работают обогащенные типы вместо вариантных
комбинированных? О каких возможностях приватных типов идет речь?
Другие аспекты:
упрощен экспорт-импорт, ликвидирован предопределенный модуль SYSTEM с
предопределенными типами ADRESS и WORD;
убраны все средства для управления асинхронным исполнением процессов;
убран оператор цикла (FOR);
убран оператор присоединения (WITH) (точнее, он сильно переделан -
превратился в оператор для защиты правильности обращения с обогащенными
типами);
убрано даже понятие программы - пользователь видит на экране меню,
составленное из спецификаций нужных ему модулей - это и есть перечень
предоставляемых ему услуг. В самом начале - предопределенное меню.
12.9.2. Управление сетями на Обероне.
Приведем переписанный на Обероне пример УправлениеСетями с краткими
комментариями, подчеркивающими отличия Оберона от Модулы-2. Для удобства
сопоставления сохранены старые номера строчек (отсутствие номера означает,
что соответствующая строчка убрана совершенно). Так как в Обероне нет
поддиапазонов, структура объявлений типа упрощена, но эквивалентных проверок
мы в программу не вставляем для простоты. Ясно, что надежность страдает.
Вирт, по-видимому, руководствовался таким принципом : затраты должны быть
видимы программисту (должны требовать и его усилий - записывая явные
проверки, программист лучше чувствует их стоимость, чем в случае
автоматических проверок, вставляемых компилятором). Подобные соображения (с
учетом упрощения транслятора) и привели к удалению поддиапазонов из Оберона.
1. DEFINITION ПараметрыСети;
(* это заголовок спецификации (сопряжения) модуля *)
3. CONST МаксУзлов = 100;
4. МаксСвязей = 8;
5. END ПараметрыСети;
(* Списка экспорта в Обероне нет. Клиентам модуля ПараметрыСети
становятся доступными все имена, объявленные в его спецификации *)
1. DEFINITION УправлениеСетями;
2. IMPORT П: ПараметрыСети; (* МаксУзлов, МаксСвязей убраны *)
(* в списке импорта - только имена модулей, "П" - локальное имя *)
(* список экспорта не нужен *)
4. TYPE
Узел = SHORTINT; (* - встроенный тип *)
7. ПереченьСвязей = ARRAY П.МаксСвязей OF Узел;
(* индексы всегда целые, нижняя граница - 0, верхняя - МаксСвязей-1 *)
8. Связи = RECORD
9. Число : SHORTINT;
10. Узлы : ПереченьСвязей;
11. END;
12. Сети = RECORD END;
(* Аналог объявления приватного (закрытого, непрозрачного) типа. *)
(* 13. PROCEDURE Создать (VAR Сеть : Сети);
Как и в Аде, снова не обязательна процедура динамического создания сетей
- закрытые типы реализуются тем же аппаратом, что и обогащаемые - за счет
встроенных указателей - объекты таких типов могут быть и статическими. *)
14. PROCEDURE Вставить (X : Узел; VAR ВСеть : Сети);
(* Обратите внимание на режим второго параметра! *)
15. PROCEDURE Удалить (X : Узел; VAR ИзСети : Сети);
16. PROCEDURE Связать (АУзел, ВУзел : Узел; VAR ВСети : Сети);
17. PROCEDURE Присвоить (VAR Сеть1, Сеть2 : Сети);
(* В Аде последней процедуры не было. Как и в Модуле-2, во внешнем контексте
содержательное присваивание сетей описать невозможно из-за отсутствия
информации об их строении (даже у транслятора - приватной части нет!).
Поэтому приходится определять специальную процедуру для присваивания
содержательных сетей. В Обероне нельзя присваивать (полные) значения
объектам таких типов, которые объявлены в видимой части модуля. Так что тип
Сети - аналог ограниченных приватных Ады. *)
18. PROCEDURE УзелЕсть (X : Узел; VAR ВСети : Сети) : BOOLEAN;
19. PROCEDURE ВсеСвязи (X : Узел; VAR ВСети : Сети; VAR R : Связи);
(* В Обероне, как и в Паскале результат функции - только скаляр *)
20. END УправлениеСетями;
DEFINITION Клиент; (* программы - главного модуля
в Обероне нет! *)
IMPORT У: УправлениеСетями;
PROCEDURE ПостроениеСетей;
END Клиент;
MODULE Клиент;
IMPORT У: УправлениеСетями;
PROCEDURE ПостроениеСетей;
VAR Сеть1, Сеть2 : У.Сети; (* объявление переменных
типа Сети *)
BEGIN
У.Вставить (33, 13, Сеть1);
У.Присвоить (Сеть1, Сеть2);
END ПостроениеСетей;
END Клиент;
Вопрос. Как же воспользоваться таким модулем?
MODULE УправлениеСетями;
IMPORT П: ПараметрыСети;
TYPE
Узел = SHORTINT;
ПереченьСвязей = ARRAY П.МаксСвязей OF Узел;
Связи = RECORD
Число : SHORTINT;
Узлы : ПереченьСвязей;
END;
ЗаписьОбУзле = RECORD
Включен : BOOLEAN;
Связан : Связи;
END;
Сети = RECORD C: ARRAY П.МаксУзлов OF ЗаписьОбУзле END;
(* Приходится так определять тип Сети, чтобы можно было скрыть поле С;
другого способа строить закрытый тип в Обероне нет! *)
PROCEDURE УзелЕсть (X : Узел; VAR ВСети : Сети) : BOOLEAN;
BEGIN
RETURN ВСети.С[X].Включен; (* вместо указателя - поле С *)
END УзелЕсть;
Вопрос. Зачем второй параметр получил режми VAR?
Подсказка. Нужно ли копировать сеть?
PROCEDURE ВсеСвязи (X : Узел; ВСети : Сети; VAR R : Связи);
BEGIN
R := ВСети.С[X].Связан;
END ВсеСвязи;
PROCEDURE Вставить (X : Узел; VAR ВСеть : Сети);
BEGIN
ВСеть.С[X].Включен := TRUE;
ВСеть.С[X].Связан.Число := 0;
END Вставить;
PROCEDURE Присвоить (VAR Сеть1, Сеть2 : Сети);
BEGIN
Сеть2.С := Сеть1.С; (* вне модуля такого не сделаешь *)
END Присвоить;
PROCEDURE Есть_связь(АУзел, ВУзел : Узел, VAR ВСети : Сети): BOOLEAN;
VAR i : 1..П.МаксСвязей;
z : Связи;
BEGIN
z := ВСети.С[АУзел].Связан; (* вместо присоединяющего оператора *)
i := 0;
REPEAT (* цикла FOR в Обероне нет *)
IF z.Узлы(i) = BУзел THEN
RETURN TRUE ;
END;
i := i + 1;
UNTIL i < z.Число
RETURN FALSE ;
END Есть_связь ;
PROCEDURE Установить_связь(Откуда, Куда : Узел; VAR ВСети : Сети);
VAR z: Связи;
BEGIN
z := ВСети.С[АУзел].Связан; (* вместо присоединяющего оператора *)
z.Число := z.Число+1 ;
z.Узлы(z.Число) := Куда ;
END Установить_связь ;
PROCEDURE Связать (АУзел, ВУзел : Узел; VAR ВСети : Сети);
BEGIN
IF ~ Есть_связь(АУзел, ВУзел, ВСети) THEN (* "~" - отрицание *)
Установить_связь(АУзел, ВУзел, ВСети) ;
IF АУзел # ВУзел THEN (* "#" в Обероне - знак неравенства *)
Установить_связь(ВУзел, АУзел) ;
END;
END;
END Связать ;
PROCEDURE Переписать (ВУзле : Узел;
После : SHORTINT; VAR ВСети : Сети);
VAR j : SHORTINT;
BEGIN
(* присоединяющий оператор в Обероне отсутствует *)
j := После;
WHILE J > ВСети.C[ВУзле].Связан.Число-1 DO
ВСети.C[ВУзле].Связан.Узлы[j] := ВСети.C[ВУзле].Связан.Узлы[j+1];
j := j+1;
END
END Переписать;
PROCEDURE Чистить (Связь, ВУзле : Узел; VAR ВСети : Сети);
VAR i : SHORTINT;
BEGIN
i := 0;
REPEAT
IF ВСети.С[ВУзле].Связан.Узлы[i] = Связь THEN
Переписать (ВУзле, i, ВСети);
ВСети.С[ВУзле].Связан.Число := ВСети.С[ВУзле].Связан.Число-1;
EXIT;
END; i := i+1;
UNTIL i < ВСети.С[ВУзле].Связан.Число
END Чистить;
(* Мы сознательно программируем близко к А- и М-программам,
хотя можно было бы действовать рациональней. Видно, что отсутствие
присоединяющего оператора мешает - мы использовали два варианта его замены
*)
PROCEDURE Удалить (X : Узел; VAR ИзСети : Сети);
VAR i : SHORTINT;
BEGIN
ИзСети.С[X].Включен := FALSE; i := 0;
REPEAT
Чистить (X, ИзСети.C[X].Связан.Узлы[i], ИзСети);
i := i+1;
UNTIL i < ИзСети.C[X].Связан.Число
END Удалить;
END УправлениеСетями;
Итак, задача полностью решена. Обеспечена аналогичная А- и М-случаям
целостность сетей и модифицируемость. Надежность программирования меньше.
Вопрос. В чем это может повредить при управлении сетями?
Подсказка. Не всякий недостаток ЯП должен сказываться на любой
программе.
Как и в случае с Модулой-2, можно заключить, что обычные программы
можно писать на Обероне почти с тем же успехом и комфортом, что на Аде или
на Модуле-2. Вместе с тем удалось почувствовать и неудобства от ликвидации
присоединяющего оператора и привычных циклов.
Закончим краткое знакомство с ЯП Оберон утверждением, что при всей
своей "аскетичности" он вполне пригоден для выполнения роли монопольного
языка персональной рабочей станции в основном за счет двух мощнейших средств
- высокоразвитой модульности, опирающейся на идею экспортного окна и
обогащаемых типов. Работа первого из них показана, а вторым займемся в
разделе о наследуемости.
[Интересно (и поучительно) отметить, что оба эти средства суть два
взаимно дополнительных (дуальных) проявления одного и того же известнейшего
математического понятия, оказавшегося, как недавно выяснилось, полезным для
понимания "момента истины" в самых современных концепциях программирования.
Такое понимание позволяет отделять "зерна от плевел", принимать решения при
развитии и стандартизации ЯП. Подробнее об этом сказано в разделе о
наследуемости в ЯП.]
Упражнение (повышенной трудности). Попытайтесь самостоятельно
догадаться, о каком математическом понятии идет речь.
Упражнение. Промоделируйте средствами Оберона отсутствующие конструкты
Модулы-2 или Ады (кроме управления параллелизмом и средств низкого уровня).
Например, оператор цикла (FOR), вариантные записи.
Часть 2. Перспективы языков программирования
13. Перспективные модели языка
13.1. Введение
Если в первой части книги мы стремились дать представление по
возможности о всех аспектах современного языка индустриального
программирования, то во второй части наша главная цель - дать представление
о перспективах и тенденциях развития ЯП. Конечно, и в первой части нас
интересовали прежде всего понятия, принципы и концепции фундаментального
характера, которые могут претендовать на долгую жизнь в области ЯП. Мы
особенно подчеркивали ситуации, когда такой подход позволял прогнозировать
развитие ЯП.
Вместе с тем в целом мы сознательно ограничили себя рамками одного
стиля программирования, часто называемого операционным (операторным, фон-
неймановским, традиционным, классическим и т.п.), представителями которого
выступают практически все упоминавшиеся нами ЯП. Такая ограниченность была
оправдана, пока нас интересовали по возможности все аспекты практического
программирования в их взаимносогласованном воплощении в целостной знаковой
системе. Именно поэтому был выбран и единый язык примеров - Ада, а все
сопоставления обычно делались с ЯП аналогичного стиля, тем более что
операционный стиль явно доминирует в ЯП массового программирования.