Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Языки программирования. Концепции и принципы
.pdf
Нотация
Интересно отметить, что «возвращение пробела» как значащего символа свя
зано с пониманием «ключевых слов» просто как зарезервированных слов (а не
«иероглифов», как в Алголе), ничем другим от остальных словлексем не отлича
ющихся. Но тогда естественно запретить сокращать ключевые слова (иначе их
можно спутать теперь уже не только с другими ключевыми словами, но и
с идентификаторами). Это в целом полезное ограничение, так как способствует
надежности программирования, помогая чтению за счет некоторой дисциплины
письма (что вполне в духе индустриального программирования). Кстати, не оче
видно, что напечатать слово procedure труднее, чем «рrос», с учетом переключе
ния внимания на спецзнаки. К тому же современные системы подготовки текстов
позволяют легко вводить словари сокращений (так что и чтения не затрудняют, и
печатать удобно).
181
7.9. Лексемы в Аде
Лексемы в Аде аналогичны словам естественного языка. Они делятся на шесть
классов: ограничители (знаки препинания), идентификаторы (среди которых –
зарезервированные ключевые слова), числа, обозначения символов, строки и при
мечания. В некоторых случаях, когда невозможно иначе однозначно выделить
лексему, требуется явный разделитель между смежными лексемами. В качестве
разделителя выступает или пробел, или управляющий символ, или конец строч
ки. Пробел, естественно, не действует как разделитель в строках, примечаниях и
в обозначении пробела (‘ ‘). Управляющие символы (кроме, возможно, горизон
тальной табуляции, эквивалентной нескольким пробелам) всегда служат разде
лителями лексем. Между лексемами (а также до первой и после последней лексе
мы) текста допустимы несколько разделителей. Заметим, что каждая лексема
должна располагаться на одной строке (ведь конец строки – разделитель).
Со списком ключевых слов Ады мы познакомились по ходу изложения. Мно
гие из них стали фактически стандартными для многих ЯП (procedure, begin, do и
т. д.). Сокращать ключевые слова недопустимо.
Ниже следует описание классов лексем.
Ограничитель. Это одиночный символ
&’()*+,–./:;<=>
и пара символов
=> .. ** := /= >= <= << >> < >
При этом символ может играть роль ограничителя только тогда, когда он не
входит в более длинную лексему (парный ограничитель, примечание, строку).
Идентификатор. Отличается от алгольного или паскалевского идентификато
ра только тем, что внутри него допускается одиночное подчеркивание. Пропис
ные и строчные буквы считаются эквивалентными. Идентификаторы считаются
различными, если отличаются хотя бы одним символом (в том числе и подчерки
ванием), например 'А', ‘*’, '", ' ' и т. п.

182
Современное состояние языков программирования
Строка. Это последовательность графических символов, взятая в двойные ка
вычки. Внутри строки двойная кавычка изображается повторением двойной ка
вычки («»), например «Message of the day».
Примечание. Начинается двумя минусами и завершается концом строки.
Число. Примеры целых чисел:
65_536 , 10.000
2#1111_1111# , 16#FF# , 016#0FF#
-- целые константы, равные 255
16#Å#Å1 , 2#1110_0000#
-- ýòî 222
Примеры вещественных чисел:
16#F.FF#E+2 , 2#1.1111_1111_111#Å11
-- 4095.0
(Пробелы внутри не допускаются – ведь они разделители.)

Исключения
8.1. Основная абстракция.............. 184
8.2. Определяющие требования .... 185
8.3. Аппарат исключений в ЯП ....... 187
8.4. Дополнительные особенности
обработки исключений .................. 194
Глава 8

184
Современное состояние языков программирования
8.1. Основная абстракция
Представим себе заводского технолога, планирующего последовательность опера
ций по изготовлению, например, блока цилиндров двигателя внутреннего сгора
ния. Аналогия с программированием очевидна. Соответствующая технологическая
карта (программа) предусматривает отливку заготовки, фрезеровку поверхно
стей и расточку отверстий. Каждый из этапов довольно подробно расписывается
в технологической карте. Однако все предусматриваемые технологом подробно
сти касаются создания именно блока цилиндров. В технологической карте, конеч
но, не сказано, что должен делать фрезеровщик, если выйдет из строя фреза, если
возникнет пожар, землетрясение, нападет противник. Если бы технолог был вы
нужден планировать поведение исполнителя в любых ситуациях, то он никогда не
закончил бы работу над такими «технологическими картами».
В сущности, специализация в человеческой деятельности основана на способ
ности выделить небольшой класс ситуаций, считающихся существенными для
этого вида деятельности, а от всех остальных абстрагироваться, считать чрезвы
чайными, необычными, исключительными, требующими переключения в другой
режим, в другую сферу деятельности.
Примерам нет числа. Кулинарный рецепт не описывает поведения хозяйки,
если в процессе приготовления блюда зазвонит телефон; физические модели при
менимы при определенных ограничениях и ничего не говорят о том, что будет при
нарушении этих ограничений (например, из законов Ньютона нельзя узнать о по
ведении объектов при релятивистских скоростях).
Вместе с тем важно понимать, что теми аспектами планируемой деятельности,
от которых приходится абстрагироваться, ни в коем случае нельзя пренебрегать.
При реальном возникновении чрезвычайных обстоятельств именно они и стано
вятся определяющими. Так что в жизнеспособной системе, а тем более системе,
претендующей на повышенную надежность, совершенно необходим аппарат,
обеспечивающий адекватную реакцию системы на чрезвычайные ситуации.
К счастью, технолог обычно вправе рассчитывать на интеллект, жизненный опыт
и общую квалификацию исполнителячеловека.
Программист, вынужденный создавать программу для автомата, по необходи
мости попадает в положение заводского технолога, которого заставляют писать
инструкции по гражданской обороне или поведению во время пожара. Ведь на
дежная программа должна вести себя разумно в любых ситуациях. Как выйти
из положения, нам уже нетрудно догадаться – снова абстракция (и затем
конкретизация). Нужно иметь возможность, занимаясь содержательной функ
цией программы, отвлекаться от проблемы чрезвычайных обстоятельств, а зани
маясь чрезвычайными обстоятельствами, в значительной степени отвлекаться от
содержательной функции программы. Вместе с тем на подходящем этапе про
граммирования и исполнения программы нужно, конечно, иметь возможность
учесть все тонкости конкретных обстоятельств.
Таким образом, мы приходим к одной из важнейших абстракций программи
рования – абстракции от чрезвычайных обстоятельств, от особых (исключитель

Исключения
185
ных) ситуаций. Будем называть их для краткости просто «исключениями», а со
ответствующую абстракцию – абстракцией от исключений.
Проще говоря, речь идет об аппарате, поддерживающем систематическое раз
деление нормальной и ненормальной работы, причем не только программы, но
в некотором смысле и программиста.
8.2. Определяющие требования
Требования к аппарату исключений в ЯП легко выводятся из самых общих сооб
ражений (и тем не менее нигде не сформулированы). Представим их в виде трех
принципов, непосредственно следующих из назначения исключений в условиях
систематического, надежного и эффективного программирования.
Принцип полноты исключений: на любое исключение должна (!) быть пре
дусмотрена вполне определенная реакция исполнителя.
Формулировка не претендует на строгость. Конечно, имеется в виду «любое»
исключение не из реального мира, а из «мира» исполнителя (трудно предусмот
реть реакцию компьютера, например, на любую попытку его поломать). Но зато
действительно имеется в виду любое исключение из этого «мира», что немедлен
но приводит еще к одному важному понятию, которому до сих пор мы не имели
случая уделить достойного внимания.
Дело в том, что предусматривать определенную реакцию на любое исключение
в каждой программе во всех отношениях неразумно. Читатель легко поймет, поче
му. Поэтому наиболее общие правила такого реагирования на исключения долж
ны быть предусмотрены априори, еще до начала программирования, то есть авто
рами ЯП. Другими словами, существенная часть реакции на исключения должна
предусматриваться «априорными правилами поведения исполнителя», а не соб
ственно программой.
К сожалению, этот принцип в полной мере воплотить не удается по многим
причинам (в частности, изза невозможности абсолютно точно определить ЯП
или безошибочно программировать). Однако он полезен в качестве ориентира
для авторов ЯП и программистов.
Об априорных правилах поведения исполнителя
Планировать поведение исполнителя означает, в частности, согласовывать его мо
дель мира с моделью решаемой задачи. Чем лучше эти модели согласованы априо
ри, тем проще планировать поведение, проще достичь взаимопонимания. В сущ
ности, изученные нами абстракции включают в ЯП именно для того, чтобы
приблизить мир исполнителя к миру решаемых задач. Однако до сих пор у нас не
было случая обратить внимание на то, что «мир исполнителя» не следует ограни
чивать операциями и данными (возможно, весьма мощными и разнообразными).
Сколь мощной ни была бы операция, ее нужно явно указать в программе. Между
тем одна из самых общих технологических потребностей – потребность писать
надежные программы, только что привела нас к абстракции новой категории, к аб
стракции от конкретной программы или понятию об априорных правилах поведе
ния исполнителя (то есть правилах поведения, предопределенных в ЯП и не обяза
тельно связанных непосредственно с какимилибо указаниями в программе).

186
Именно априорные правила поведения, а не специфические данные и операции
характеризуют современные развитые ЯП. С этой точки зрения удобно рассматри
вать и особенности взаимодействия асинхронных процессов, и перебор с возвра
том (backtracking), и вызов процедур по образцу, и поведение экспертных систем,
и вообще программирование достаточно высокоинтеллектуальных исполнителей
(в том числе программирование человеческой деятельности).
Связь с этим аспектом мира исполнителя вступает в действие при исполнении лю
бой программы, как только возникает исключительная ситуация. Вместе с тем
в развитом ЯП всегда имеются и специальные средства конкретизации, позволяю
щие корректировать априорные правила поведения с учетом потребностей конк
ретной программы.
Важно понимать, что без априорных правил поведения исполнителя не обойтись.
Ведь если программист захочет все предусмотреть, он должен планировать провер
ку соответствующих условий. Но исключительные ситуации могут возникнуть
в процессе исполнения программы проверки! Так что чисто программным путем
задачу не решить. Необходим определенный исходный (априорный) уровень под
готовки исполнителя.
В жизни примерами «общих правил» служат обязанность вызвать пожарную ко
манду по телефону 01, милицию – по 02, скорую помощь – по 03, а также выпол
нить инструкцию по оказанию неотложной помощи пострадавшим, мобилизаци
онное предписание и т. п.
Пример конкретизации – подробный план эвакуации людей из помещения при по
жаре, план мобилизационных и эвакуационных мероприятий на случай войны, пе
речень занятий дежурного при отсутствии телефонных звонков (ведь для него
обычная работа – отвечать на телефонные звонки, а их отсутствие – исключение).
Современное состояние языков программирования
Принцип минимальных возмущений: затраты на учет чрезвычайных обстоя
тельств должны быть по возможности минимальными (при гарантии сохранения
жизнеспособности системы).
Этот же принцип в терминах ЯП: языковые средства должны быть такими,
чтобы забота об исключениях в минимально возможной степени сказывалась на
всех этапах жизненного цикла программных сегментов, реализующих основную
содержательную функцию программы.
Другими словами, аппарат исключений в ЯП должен быть таким, чтобы про
граммирование поведения в чрезвычайных обстоятельствах могло быть в макси
мально возможной степени отделено от программирования основной функции
(в частности, не мешало понимать содержательную функцию программы), наклад
ные расходы на исполнение основной функции могли быть минимальными и т. п.
Принцип минимальных повреждений: ущерб при возникновении исключе
ний должен быть минимальным. Речь идет уже не о минимизации затрат на саму
способность реагировать на исключения, а об ущербе (иногда принципиально не
устранимом), который может быть нанесен при реальном возникновении чрезвы
чайных для программы обстоятельств.
Например, разумный аппарат исключений должен предусматривать возмож
ность реагировать на исключение как можно раньше и как можно точнее, чтобы
предотвратить дальнейшее разрушение программ и данных при аварийном их
функционировании. Ясно, что этот принцип имеет смысл только тогда, когда ис

Исключения
ключения трактуются как аварии или реально оказываются таковыми (часто это
как раз априорные исключения). Например, завершение файла при чтении часто
удобно считать исключением с точки зрения «нормальной» обработки его записей,
однако в этом случае не имеет смысла говорить об авариях или повреждениях.
Полезные примеры, помогающие лучше прочувствовать суть изложенных
принципов, содержатся в [14].
187
8.3. Аппарат исключений в ЯП
Концепция исключения в ЯП содержательно имеет много общего с концепцией
аппаратного внутреннего прерывания, однако могут быть и существенные отли
чия. Ближе всего к понятию прерывания трактовка исключений в языке ПЛ/1.
Выделим четыре аспекта аппарата исключений:
• определение исключений (предопределенные и определяемые);
• возникновение исключений (самопроизвольное и управляемое);
• распространение исключений (статика или динамика);
• реакция на исключения (пластырь или катапульта – см. ниже).
Кроме этого, уделим внимание другим особенностям исключений, в частности
особенностям исключений в асинхронных процессах.
8.3.1. Определение исключений
Рассмотрим концепцию исключения, ориентируясь на Аду, стараясь больше уде
лять внимания «авторской позиции», то есть объяснять, почему при проектирова
нии ЯП были приняты излагаемые решения. Основой послужат, конечно, прин
ципы полноты, минимальных возмущений и минимального ущерба.
Все потенциальные исключения в программе на Аде имеют индивидуальные
имена и известны статически. Они либо предопределены, либо объявлены (опре
делены) программистом.
Предопределенные исключения касаются, естественно, самых общих ситуа
ций. Например, при нарушении ограничений, связанных с типом (ограничений
допустимого диапазона значений, диапазона индексов и т. п.), возникает (иногда
говорят «возбуждается») исключение нарушение_ограничения (constraint_error);
при ошибках в числовых расчетах (переполнение, деление на нуль, исчезновение
и т. п.) – исключение численная_ошибка (numeric_error); при неправильной ком
поновке программы (отсутствие тела нужного программного сегмента и т. п.) –
исключение нет_сегмента (program_error); при нехватке памяти для размещения
динамических объектов – исключение нет_памяти; при нарушении во взаимодей
ствии асинхронных процессов (аварийное или нормальное завершение процесса,
содержащего вызываемый вход, и т. п.) – исключение ошибка_взаимодействия
(tasking_error).
Если, например, объявить
А:аггау(1 .. 10) of INTEGER;

188
Современное состояние языков программирования
то при I=11 или I = 0 в момент вычисления выражения А(I) возникает предопре
деленное исключение нарушение_ограничения.
На уровне ЯП принцип полноты исключений обеспечен тем, что нарушение
любого языкового требования при выполнении программы на Аде приводит
к возникновению некоторого предопределенного исключения.
Принцип минимальных возмущений проявляется в том, что предопределен
ные исключения возникают без какого бы то ни было указания программиста. Так
что, вопервых, программист избавлен от необходимости проектировать их воз
никновение, вовторых, упомянутые указания не загромождают программу и,
втретьих, соответствующие предопределенные проверки могут быть в принципе
реализованы аппаратурой или авторами компилятора так, чтобы требовать
минимума ресурсов при исполнении программы. Например, во фрагменте про
граммы
<<Â>>
declare
A: float;
begin
...
À:=Õ*Õ;
Y:=A*EXP(A); -- здесь возможно переполнение при возведении в степень или
-- умножении
...
exception – ловушка исключений
when NUMERIC_ERROR => Y:=FLOAT’LAST; – наибольшее вещественное
PUT(‘Переполнение при вычислении Y в блоке В’);
end В;
четко отделена часть, реализующая основную функцию фрагмента (до ключевого
слова exception), от части, реализующей предусмотренную программистом реак
цию на предопределенное исключение численная_ошибка, возникающее при пе
реполнении, – это ловушка исключений.
Первую часть программист мог писать, не думая об исключениях вообще, а за
тем мог добавить ловушку. Работать эта ловушка будет тогда и только тогда, ког
да возникнет исключение, а затрат на проверку переполнения в программе нет
вовсе – обычно это дело аппаратуры. После реакции на исключение переменная Y
получит «правдоподобное» значение, позволяющее сохранить работоспособность
программы после аварии, а программист получит точное сообщение об аварии
в своих собственных обозначениях (размещенное в операторе PUT).
Упражнение. Попытайтесь написать эквивалентную программу, не пользуясь ап
паратом исключений. Убедитесь, что при этом приходится нарушать все три прин
ципа со стр. 185–186.
Принцип минимальных повреждений (минимального ущерба) в современных
ЯП почти не влияет на возникновение исключений (хотя мог бы и влиять, позво
лив управлять информацией, которая становится доступной при возникновении
исключения). Зато он существенно влияет на их определение и обработку. В част

Исключения
ности, именно для того, чтобы программист мог предусмотреть быструю и точную
реакцию на конкретное возникновение исключения, для одного и того же исклю
чения можно объявить несколько различных реакций, учитывающих соответ
ствующий контекст.
Определяемые исключения явно вводятся программистом посредством
объявления исключения. Например, объявление
объект_пуст, ошибка_в_данных : exception;
вводит два исключения (exception). Очень похоже на объявление двух объектов
предопределенного типа exception. Такое объявление можно считать специфика
цией исключения (хотя оно так не называется). Программист обязан задать также
хотя бы одну реакцию на введенное исключение (примеры чуть ниже). Совокуп
ность таких реакций играет роль «тела» («реализации») объявленного програм
мистом исключения.
Таким образом, для исключений также действует принцип разделения специ
фикации и реализации. Ловушку (в которой размещаются реакции на исключе
ния) также естественно считать объявлением.
Вопрос. Что естественно считать «использованием» исключения?
Определяемые исключения возникают в момент, явно указываемый в про
грамме посредством оператора исключения (raise). Например, результатом ис
полнения оператора
raise ошибка_в_данных;
служит возникновение исключения ошибка_в_данных.
Факт возникновения исключения переводит исполнителя в новый режим, ре
жим обработки исключения – происходит так называемое «распространение» ис
ключения (ищется подходящая «ловушка исключений»), а затем выполняется
«реакция на исключение», описанная в найденной ловушке. В этом режиме в ос
новном и действуют упоминавшиеся «априорные правила поведения исполните
ля». Они определяют, как найти реакцию на исключение и что делать после вы
полнения предписанных в ней действий.
Как уже сказано, именно на выбор этих правил влияет принцип минимальных
повреждений. Рассмотрим эти правила.
189
8.3.2. Распространение исключений.
Принцип динамической ловушки
Итак, с каждым исключением может быть связана серия ловушек, содержащих
реакции на исключение и расположенных в различных программных конструк
тах. С учетом принципа минимальных повреждений в современных ЯП принят
принцип динамического выбора ловушки – всегда выбирается реакция на возник
шее исключение из ловушки, динамически ближайшей к месту «происшествия»,
то есть в режиме распространения исключения существенно используется дина
мическая структура программы.

190
Современное состояние языков программирования
Другими словами, конкретные действия исполнителя зависят от динамиче
ской цепочки вызовов, ведущей к тому программному конструкту, в котором воз
никло исключение. Поэтому в соответствующей реакции появляется возмож
ность учесть динамический, а не только статический контекст чрезвычайной
ситуации. Это, конечно, помогает предотвратить распространение повреждений.
Поясним принцип динамической ловушки на примере фрагмента программы:
procedure Ð is
ошибка : exception;
procedure R is
begin
. . . --(1)
end R;
procedure Q
begin
R; -- вызов процедуры R;
. . . --(2)
exception -- первая ловушка исключений
. . .
when ошибка => PUT(«ОШИБКА в Q»);
-- реакция на исключение
-- «ошибка» в первой ловушке
end Q;
begin
. . . -- (3)
Q; – вызов процедуры Q
. . .
exception – вторая ловушка
. . .
when ошибка => PUT(«ОШИБКА в Р»);
-- другая реакция на то же исключение во второй ловушке
end Ð;
Если исключение «ошибка» возникнет на месте (3), то сработает реакция на
это исключение во второй ловушке и будет напечатано «ошибка в Р». Если то же
исключение возникнет на месте (2), то есть при вызове процедуры Q и не в R, то
сработает реакция в первой ловушке и будет напечатано «ошибка в Q».
Пока подбор реакции как по динамической цепочке вызовов, так и по статиче
ской вложенности конструктов дал одинаковые результаты.
А вот когда исключение «ошибка» возникает на месте (1) в теле процедуры Q
(при вызове процедуры R, в которой ловушки нет), то отличие динамического
выбора от статического проявляется наглядно. Статический выбрал бы реакцию
из второй ловушки в теле Р, а динамический выберет реакцию из первой ловушки
в теле Q.
Будет напечатано «ошибка в Q», что существенно точнее отражает суть слу
чившегося. Именно для того, чтобы можно было точнее, конкретнее реагировать
на исключение, и принят практически во всех ЯП принцип динамической ло
вушки.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
