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

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

.pdf
Скачиваний:
2
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
Два альтернативных принципа создания ЯП
зоваться Модулой2 как естественным преемником Паскаля (если угодно, его мо дульным диалектом)), то есть у этих ЯП – потенциально пересекающиеся ниши.
Обратите внимание, закон консерватизма ниш в данном случае работает не
против нового ЯП, а за него, потому что Модулу2 следует рассматривать не как конкурента Паскаля, а как его естественное развитие, учитывающее консерва тизм ниши.
ально конкурируют!) между собой и, в частности, с Модулой2.
Правда, различные развития Паскаля вполне могут конкурировать (и ре
251
12.7.2. Инкапсуляция
Особенно наглядно принцип чемоданчика проявляется в методе Минкапсуля ции. Обсуждая необходимость приватной части в Аде, мы привлекали реализа торскую позицию (соображения эффективности реализации: без приватной части компилятор не в состоянии распределять память под объекты приватных типов). И отмечали, что при этом нарушается согласованность с концепцией разделения спецификации и реализации, а также пошаговой детализации.
Другими словами, эффективность реализации в этом случае достигается за
счет ряда нарушений общих принципов и усложнения языка (кстати, тоже нару шение общего принципа, а именно принципа чемоданчика).
Анализируем проблему по принципу чемоданчика. Что совершенно необходи
мо? Инкапсуляция как средство достижения надежности и целостности. Ищем компромисс между потребностями и возможностями простых проектных реше ний. Находим его в отказе от особо эффективной реализации (по сути – от стати ческого распределения памяти под инкапсулированные объекты; сравните работу с А и Мсетями). Следствие – возможность отказаться от весьма неприятной приватной части и тем самым обеспечить соблюдение четких принципов проекти рования программы (точное разделение спецификации и реализации между дву мя категориями модулей).
Главная цель достигнута. ЯП стал проще (и технологичнее), так как распреде
ление памяти под составные инкапсулированные объекты должно выполняться не компилятором, а динамически – генератором «непрозрачных» указателей.
Ограничение непрозрачных типов ссылочными и отрезками предопределен
ных типов позволяет компилятору выделять память для них как для скаляров (по одной «единице» памяти). Остальное выполняется в динамике процедурами мо дуляэкспортера. Изящное решение.
Упражнение. Докажите неформальную теорему: отсутствие приватной части в со
четании с раздельной компиляцией спецификаций и тел модулей влечет динамизм
составных инкапсулированных типов. Как следствие – ограничение непрозрачных
типов ссылочными или скалярными.
12.7.3. Обмен
Мы видели, как вся мощь Амодели использовалась для управления обменом. Но вся мощь потребовалась именно потому, что Амодель претендует на удовлетво
252
рение отнюдь не минимальных потребностей. Например, можно создавать файлы с совершенно произвольными (а не только предопределенными) типами элемен тов – именно это потребовало, чтобы пакеты обмена стали родовыми. Аналогич ные претензии удовлетворяются при создании драйверов специальных устройств обмена – именно это потребовало развитых спецификаций представления.
С другой стороны, для реализации драйверов привлекается универсальный аппарат управления асинхронными процессами. Когда он уже имеется в ЯП, та кое решение может показаться даже изящным. Однако на уровне машинных ко манд организация взаимодействия с аппаратной задачей может существенно от личаться от организации взаимодействия «полностью программных» задач. Мы уже отмечали это, приводя пример драйвера клавиатуры.
Так что компилятор вынужден выделять драйверы и всетаки программиро вать их не так, как другие задачи. Выделять драйверы приходится по специфика циям представления. Итак, применение универсального аппарата асинхронных процессов для реализации драйверов заставляет сначала тщательно замаскиро вать то, что затем приходится столь же тщательно выискивать.
Создавать трудности, чтобы потом их преодолевать, – не лучший принцип не только в программировании.
Наконец, хотя асинхронность внешних устройств – одна из причин появления в ЯП асинхронных процессов, совершенно не очевидно, что для создания драйве ров требуется столь абстрактный (и дорогой в реализации) аппарат, как рандеву из Амодели.
Итак, с учетом ориентации Модулы2 в основном на однопроцессорные ком пьютеры (но с асинхронными устройствами обмена) взглянем на управление об меном, руководствуясь принципом чемоданчика.
Что совершенно необходимо? Дать возможность писать драйверы, обеспечи вающие взаимодействие основной программы с асинхронно работающим устрой ством обмена.
Снова ищем компромисс между потребностями в асинхронных процессах и возможностями простых проектных решений.
Находим его в отказе, вопервых, от того, чтобы драйвер работал асинхронно с основной программой (оставаясь программной моделью аппаратной задачи), и, вовторых, от абстрактного механизма взаимодействия относительно равно правных задач (подобного рандеву).
Действительно, минимальные потребности состоят в том, чтобы основная программа (точнее, ее часть, драйвер устройства) имела лишь возможности:
1) запустить устройство для выполнения конкретного обмена;
2) продолжать работать, пока устройство исполняет задание;
3) реагировать на завершение обмена (на факт выполнения устройством за дания).
Именно такие минимальные (совершенно необходимые) возможности уп равления устройствами встроены в Модулу2. Точнее говоря, то, что мы назвали аппаратной задачей, в Модуле2 называется периферийным (асинхронным) процессом.
Современное состояние языков программирования
Два альтернативных принципа создания ЯП
Периферией обычно называют совокупность устройств обмена. В Модуле2 име
ются еще и квазипараллельные процессы (сопрограммы).
253
Примеры периферийных процессов в МодулеE2
Рассмотрим на примере периферийные процессы в Модуле2. Как и в Аде, в Мо дуле2 обмен требует всей мощи ЯП. Существенно используется основной меха низм абстракции – модули. Определяемые реализацией («системно зависимые») имена инкапсулированы в предопределенном модуле «Система» (SYSTEM). Так как транзит импортированных имен запрещен, то любые модули, где применя ются системно зависимые имена, должны явно импортировать модуль «Систе ма». (По этому признаку системно зависимые модули легко распознавать.)
Модуль «Система» экспортирует, в частности, типы «Адрес» (ADDRESS),
«Слово» (WORD – машинное слово), «Процесс» (PROCESS – к этому типу отно сятся как сопрограммы, так и периферийные процессы), а также процедуры для работы с объектами этих типов: «НовыйПроцесс» (NEWPROCESS), «Переклю чить» (TRANSFER) и «ПереключитьСЗаказом» (IOTRANSFER).
Так что в системно зависимых модулях (в том числе и в драйверax) можно ра
ботать с машинными адресами, словами и процессами. Последние характеризу ются двумя компонентами – телом (представленным некоторой процедурой) и рабочей областью, в свою очередь характеризуемой начальным адресом и разме ром (представленным натуральным числом).
Процедура
НовыйПроцесс(Р, A, n, p1);
создает новый процесс (объект типа «Процесс» с телом Р и рабочей областью с начальным адресом А и размером n) и присваивает его переменной p1 типа «Про цесс». Новый процесс при этом не запускается (ведь процессор один), продолжает выполняться текущий процесс (основная программа также считается процессом).
Переключение на новый процесс осуществляется процедурой
Переключить(p1, р2);
При этом текущий процесс приостанавливается и присваивается переменной p1, а активным становится процесссодержимое переменной р2. Напомним, что это сопрограммы; р2 начинает работать с начала своего тела или с того места, где ра нее приостановился.
Процедура
ПереключитьСЗаказом(p1, р2, А);
делает то же, что и предыдущая, но еще и заказывает переключение снова на p1 после прерывания по адресу А.
Именно эта процедура и позволяет обеспечить указанные выше потребности
2) и 3). Для этого достаточно указать в качестве р2 процесс, который должен рабо тать асинхронно (параллельно) с устройством, а в качестве адреса А указать адрес вектора прерываний, приписанный управляемому устройству.
Тогда если непосредственно перед выполнением процедуры Переключить
СЗаказом запустить обмен с устройством, то текущий процесс, приостановив
254
Современное состояние языков программирования
шись на этой процедуре, будет ждать прерывания, свидетельствующего о завер шении обмена. При этом параллельно с устройством будет работать процесс р2. А после прерывания произойдет заказанное переключение снова на p1, то есть на процесс, запустивший обмен с устройством (с заказом прерывания). Обычно об мен запускается засылкой единицы в соответствующий разряд регистра состоя ния устройства – так реализуется указанная выше потребность 1).
Вот такими скупыми средствами реализовано в Модуле2 управление перифе рийными процессами. С точки зрения языка не понадобилось вообще ничего ново го, а с точки зрения модуля «Система» – всего одна процедура ПереключитьСЗака зом. Так действует принцип чемоданчика! Чтобы лучше понять взаимодействие описанных средств, приведем (с переводом идентификаторов на русский язык) модуль обмена с телетайпом из авторского описания Модулы2.
Драйвер на Модуле2. Чтобы все в нижеследующей программе (модуле «Те летайп») было понятно, нужно сказать несколько слов о приоритетах процессов. Приоритет – это целое число, характеризующее срочность процесса. Приоритет связывается с каждым модулем и с каждым устройством, посылающим прерыва ния. Исполнение программы может быть прервано тогда и только тогда, когда приоритет прерывающего устройства выше приоритета исполняемого (текущего) процесса. Приоритет процессора (то есть приоритет текущего процесса) можно временно понизить процедурой УменьшитьПриоритет (LISTEN) из модуля «Си стема». Нужно это для того, чтобы разрешить прерывания от устройств.
1 MODULE Телетайп [4]; (* приоритет этого модуля равен 4 *) 2 FROM Система IMPORT Слово, Процесс, НовыйПроцесс, Переключить, ПереключитьСЗаказом, УменьшитьПриоритет; 3 EXPORT Печатать; 4 CONST N = 32; (* размер буфера литер *) 5 VAR n : INTEGER; (* текущее количество литер в буфере *) 6 Класть, Брать : [1..N]; (* индексы в буфере, отмечающие, куда класть и откуда брать литеры *) 7 Буф : ARRAY [1..N] OF CHAR; (* буфер, массив литер *) 8 Дай, Возьми : Процесс; 9 РабОбл : ARRAY [0..20] OF Слово; 10 РегСост [177564В] : BITSET; (*регистр состояния телетайпа*) 11 РегБуф [177566В] : CHAR; (* буферный регистр телетайпа *)
12 PROCEDURE Печатать (Лит : CHAR); 13 BEGIN 14 INC(n); (* предопределенная процедура; n := n + 1 *) 15 WHILE n > N DO УменьшитьПриоритет END; 16 Буф [Класть] := Лит; 17 Класть := (Класть MOD N) + 1; (* MOD – операция взятия по модулю;
Индекс "Класть" циклически пробегает буфер *)
18 If n = 0 THEN
Переключить(Дай, Возьми) END;
19 END Печатать;
Два альтернативных принципа создания ЯП
20 PROCEDURE Драйвер; 21 BEGIN 22 LOOP 23 DEC (n); (* предопределенная процедура; n := n – 1; *) 24 if n < 0 THEN
Переключить(Возьми, Дай) END;
25 РегБуф:= Буф [Брать];
Брать := (Брать MOD N)+l; 26 РегСост := {6}; (* шестой разряд инициирует обмен *) 27 ПереключитьСЗаказом(Возьми, Дай, 64В); 28 РегСост:= { }; (* обмен завершен *) 29 END; 30 END Драйвер;
31 BEGIN n:=0; Класть:=1; Брать:=1;
(* Инициализация *) 32 НовыйПроцесс(Драйвер, ADR (РабОбл), SIZE (РабОбл), Возьми); (* Предопределенные функции доставляют соответственно адрес и размер объекта *) 33 Переключить(Дай, Возьми); 34 END Телетайп;
255
Подробности о функционировании модуля Телетайп. Представим себе при менение этого модуля по такой схеме:
35 MODULE Печать; 36 FROM Телетайп IMPORT Печатать; 37 CONST Ì = 100; 38 VAR Текст : ARRAY [1..N] OF CHAR; ... 39 FOR J:= 1 TO M DO 40 Печатать(Текст [J]); 41 END; 42 END Печать;
Проследим взаимодействие компонент программы, указывая обрабатываемые (выполняемые) номера строк.
Инициализация. В самом начале модуля Печать происходят связывание с мо дулем Телетайп и выполнение его «инициализирующих» строк 31–33. Создается процесс с телом Драйвер и присваивается переменной Возьми. С этого момента Возьми используется для идентификации сопрограммы, непосредственно работа ющей с внешним устройством.
Ее принципиальное отличие от процедуры Драйвер состоит в том, что переключе ние на Возьми означает продолжение работы сопрограммы, а не вызов процедуры Драйвер (с ее начала).
Затем (строка 33) эта сопрограмма запускается и одновременно текущий про цесс (то есть основная программа) присваивается переменной Дай и приостанав ливается (перед выполнением строки 37).
256
Современное состояние языков программирования
С этого момента основная программа выступает как процесс Дай, а драйвер –
как Возьми.
Буф, а драйвер забирает их оттуда.
Названия оправданы тем, что основная программа подает литеры в буфер
Итак, запомним, что строка 32 нужна для создания процесса Возьми, а строка
33 – для создания процесса Дай. Взаимодействие начинается.
Начало. Буфер пуст. После строки 33 управление достигает цикла 22 с усло
вием n=0, свидетельствующим о пустом буфере. Поэтому после строки 23 в стро
ке 24 следует переключение на основную программу Дай. (Вернется оно в драйвер
на строку 25!)
Так будет всегда, когда драйвер в своем основном цикле освобожда
ет буфер и переключается при n=–1 на основную программу Дай.
Эта программа продолжается со строки 37, рано или поздно доходит до строки 40 и вызывает Печатать с очередной литерой текста. Через строку 14 при условии n=0 проходим на 16 и помещаем литеру в буфер. Строка 18 отправляет на драйвер (строка 25) при n=0 (несколько неестественном условии; ведь в буфере имеется одна литера).
Основное взаимодействие. Буфер не пуст и не полон. Извлекая очередную литеру из буфера (в строке 25), драйвер запускает обмен с внешним устройством в строке 26 (присваивая его регистру состояния 1 в шестом разряде и активизируя тем самым аппаратную задачу).
Принципиально важная для нас строка 27 приостанавливает драйвер, пере ключает управление на основную программу (в первый раз – на строку 19, то есть сразу же на 39) и заказывает прерывание по концу обмена очередной литеры. Это прерывание (от телетайпа) в соответствии с семантикой процедуры Переклю читьСЗаказом приводит к переключению от Дай снова на Возьми в момент окон чания обмена.
Пока идет обмен (работает аппаратная задача асинхронно с исполнением про цессов Дай и Возьми), процесс Дай в цикле 39–41 может наполнять буфер. После прерывания драйвер в цикле 22–29 очищает буфер по одной литере. Это и есть основное взаимодействие процессов Дай и Возьми. При этом скорости заполне ния и очистки буфера жестко не связаны.
Вопрос. За счет чего буфер может очищаться быстрее, чем наполняться, и на оборот?
Особые ситуации. Буфер полон и пуст. Основное взаимодействие прекраща ется, если буфер оказывается полным (в строке 15 n > N) или пустым (в строке 24 n < 0).
Когда буфер полон, необходимо дать приоритет процессу Возьми, очищающе му буфер, приостановив заполняющий процесс Дай. Это реализует цикл умень шения приоритета (строка 16). Ведь по логике модуля Телетайп заполнение буфера более чем на одну позицию возможно только одновременно с работой ап паратной задачи (собственно обменом или ожиданием ею разрешения на преры вание (убедитесь в этом!)).
Поэтому переполнение буфера означает, что нужно обеспечить беспрепят ственное выполнение очищающего цикла драйвера. Для этого процесс Дай и за
Два альтернативных принципа создания ЯП
257
держивается на цикле 15, в конечном итоге уступая (единственный!) процессор драйверу (при достаточном понижении приоритета). И буфер начинает очи щаться.
Когда же буфер пуст, то строка 24 переключает управление на Дай с n=–1. Это
соответствует уже разобранной ситуации «Начало. Буфер пуст».
Еще одно решение. Не видно причин, почему не написать модуль Телетайп
концептуально проще, изъяв строку 33 и (как следствие) попадание в зону отри цательных n (такие значения не соответствуют назначению этой переменной – считать количество литер в
Упражнение. Найдите это решение.
буфере).
(Пишем только «Печатать» и «Драйвер» при условии, что строки 33 нет.)
PROCEDURE Печатать(Лит : CHAR); BEGIN
WHILE n = N DO УменьшитьПриоритет END; Буф [Класть]:= Лит; INC(n); Класть:= (Класть MOD N) + 1; If n=1 THEN Переключить(Дай, Возьми) END;
END Печатать;
PROCEDURE Драйвер; BEGIN
LOÎÐ
РегБуф := Буф [Брать]; DEC(n); Брать := (Брать MOD N) +1; РегСост:={6}; ПереключитьСЗаказом(Возьми, Дай, 64В); РегСост := {}; If n=0 THEN Переключить(Возьми, Дай) END;
END (* цикла *);
END Драйвер;
Упражнение. Докажите эквивалентность первому решению.
12.8. Принцип чайника
Обсуждая методы борьбы со сложностью программирования, полезно обратить внимание на принцип, интуитивно хорошо знакомый опытным программистам и выражающий своего рода защитную реакцию на сложность и ненадежность опе рационной среды.
Суть этого принципа хорошо иллюстрирует старый анекдот: «Как вскипятить чайник? – Берем чайник, наливаем воду, ставим на огонь, доводим до кипения. Как вскипятить чайник, уже наполненный водой? – Выливаем воду из чайника и сводим задачу к предыдущей!» Почему математики так любят «сводить задачу к известной»? Потому, что для
них главное – ясность («прозрачность», «надежность») доказательства, а прямое решение новой задачи рискует оказаться ошибочным.
258
Но ведь и для программистов главное – надежность и понятность программы. Поэтому опытный программист без особой нужды не станет пользоваться элемен тами операционной среды, которые он лично не проверил.
Это относится, в частности, к использованию отдельных команд языковых конструктов, программ, пакетов, а также ЯП. Важными оказываются не столько их свойства сами по себе, сколько то, что программист эти свойства знает и этому своему знанию доверяет.
Если окажется возможным «свести задачу к предыдущей», она будет, как пра вило, решена традиционными, обкатанными методами. Намекая на упомянутый анекдот, назовем соответствующий технологический принцип «принципом чай ника».
Очевидное проявление принципа чайника – долгожительство классических ЯП, в особенности Фортрана. Менее очевидное (указанное впервые Дональдом Кнутом и затем многократно подтвержденное другими исследователями) – при страстие программистов к самым тривиальным оборотам (фразам) при использо вании ЯП.
Если шаг цикла, то 1; если прибавить, то 1; если присвоить, то простейшее вы ражение; если проверить, то простейшее отношение и т. п.
Принцип чайника помогает обосновать принцип чемоданчика (на этот раз уже с точки зрения психологии пользователя) – необязательными, чересчур изощрен ными конструктами будут редко пользоваться, они окажутся экономически не оправданными.
Современное состояние языков программирования
12.9. ЯП Оберон
В феврале 1988 г. стало известно о новом языке Н. Вирта – ЯП Оберон. Вирт не склонен связывать с выбором имени для своего очередного детища какихлибо глубоких соображений, однако отмечает, что для него «Оберон» – скорее самый крупный спутник Урана, чем король эльфов. Для нас Оберон интересен прежде всего как очередная попытка достичь идеала ЯП, следуя принципу чемоданчика с учетом новейших достижений в философии и технологии программирования.
Не вдаваясь в подробный анализ проектных решений, отметим лишь, что целью Вирта был минимальный базовый ЯП для персональной рабочей станции.
Более того, правильнее назвать требуемый ЯП не просто базовым, а монополь ным (интегрированным) ЯП [20]. Другими словами, ЯП должен быть таким, что бы ни создателю программного обеспечения станции (включая ее операционную систему), ни пользователю станции просто не нужен был никакой иной инстру мент программирования. Конечно, минимальное ядро реализации любого ЯП должно быть написано на ассемблере. Но этим и должно ограничиваться приме нение иного ЯП.
Идея монопольного ЯП, с одной стороны, очевидным образом перекликается с идеей единого универсального ЯП, которая, как известно, многократно терпела фиаско и вновь воскресала на очередном этапе развития программирования. Ясно, что идея монопольного ЯП жизнеспособнее за счет отказа от претензий на
Два альтернативных принципа создания ЯП
259
пригодность для любых классов задач, классов пользователей, любых компьюте ров и программных сред. Более того, она фактически реализована в таких ЯП, как Си для UNIXсовместимых сред, Эль76 для отечественной серии «Эльбрус», Том в одноименной интегрированной системе В. Л. Темова и др. Еще раз подчерк нем, что идеальный монопольный ЯП должен быть не просто принципиально воз можным, а реально наилучшим инструментом программирования в своей среде. Тем более интересно посмотреть, как справляется с задачей создания мини мального монопольного ЯП такой всемирно признанный мастер, как Вирт.
12.9.1. От Модулы'2 к Оберону
Укажем отличия Оберона от Модулы2, следуя [21].
Главная новинка – средства обогащения (extension) комбинированных типов
данных. Этот новейший аспект в ЯП мы подробнее рассмотрим в разделе, посвя щенном наследованию. Основная идея обогащения связана с воплощением «древней» мечты программистов – вводить при необходимости дополнительные поля в записи таким образом, чтобы сохранялась работоспособность всех ранее отлаженных программ. Один из известных учебников по структурному про граммированию [7] начинается с притчи о злоключениях программистов, не пре дусмотревших вовремя нужного поля. Вирт снимает все такого рода проблемы, предоставляя возможность обогащать комбинированный тип новыми полями с наследованием всех видимых операций исходного типа.
Например, если задан тип
Ò = RECORD õ. ó: INTEGER END,
то можно определить обогащенные типы
T1= RECORD(Ò) z: REAL END, Ò2 = RECORD(Ò) w: LONGREAL END,
наследующие все видимые операции, определенные для Т. При этом «обогащен ные» объекты типов Т1 и Т2 можно присваивать «бедным» объектам типа Т, а «бедные» «богатым» – нельзя. Обратите внимание, что не только в Модуле2, но и в Аде подобное невозможно.
Вопрос. Почему такое «странное» правило присваивания? Ведь в обогащенную
запись несложно разместить записи с меньшим числом полей, но это как раз запре
щено, в то время как обратное разрешено, хотя вся запись наверняка не поместится.
Упражнение. Попытайтесь уточнить правило присваивания, предложив вариант
размещения обогащенной записи в бедной.
Подсказка. Основной критерий – надежность программирования и применимость
старых операций к новым объектам.
Конечно, подобное нововведение требует иногда пожертвовать эффективнос
тью программы ради удобства ее изготовления, надежности и других преиму ществ. Но в этом и состоит истинный прогресс в ЯП – осознается фундаменталь ное значение компромиссов, казавшихся ранее немыслимыми.
260
Вопрос. За счет чего может снижаться эффективность программы? Подсказка. Не обойтись без указателей там, где ранее обходились.
Современное состояние языков программирования
Еще одно важное нововведение (точнее, коррекция исходного понятия) – трактовка спецификации как усеченной (без какихлибо добавлений!) реализа ции. Другими словами, спецификация полностью состоит из цитат, взятых из тек ста реализации, причем вне модуля видимо то и только то из реализации, что «проявлено» в спецификации. Назовем это идеей «экспортного окна», чтобы подчеркнуть, что экспортируется не более того, что имеется в реализации.
Вопрос. Что это дает? Подсказка. Смотрите перечень средств из Модулы2, не вошедших в Оберон. Вопрос. Знаете ли вы примеры ЯП, где в спецификации может оказаться не только
содержимое реализации?
Основные сокращения по сравнению с Модулой2. Сокращены в основном средства, которые функционально перекрываются обогащением типов, а также некоторые средства, по мнению Вирта, не оправдавшие себя в базовом ЯП.
Типы данных:
• нет вариантных комбинированных типов – вместо них работают обогащен
ные;
• нет закрытых (непрозрачных) типов – вместо них работает общая идея уп
равляемого «проявления» компонент реализации за счет их цитирования в спецификации. Особенно красиво это взаимодействует с обогащением ти пов. Например, если в реализации (теле модуля) содержатся объявления приведенных выше типов Т, T1, Т2, то в спецификации можно указать
TYPE T1 = RECORD z: REAL; END;
скрыв не только некоторые поля, но и «происхождение» типа. Вместе с тем, процитировав спецификации нужных операций, легко сделать их (и только их) доступными пользователям типа Т1. Все содержательные возможности закрытых и приватных типов при этом сохранены (так как в Обероне специ фикации и реализации размещаются и транслируются обязательно вместе);
• нет перечисляемых, поддиапазонов, тип множества только один (предопре
деленный над целыми), нет типа CARDINAL.
Вопросы. Как именно работают обогащенные типы вместо вариантных комби нированных? О каких возможностях приватных типов идет речь?
Другие аспекты:
• упрощен экспортимпорт, ликвидирован предопределенный модуль SYSTEM
с предопределенными типами ADRESS и WORD;
• убраны все средства для управления асинхронным исполнением процессов;
• убран оператор цикла (FOR);
• убран оператор присоединения (WITH) (точнее, он сильно переделан –
превратился в оператор для защиты правильности обращения с обогащен ными типами);
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]