Смекни!
smekni.com

Программирование и разработка приложений в Maple (стр. 52 из 135)

Механизм возврата результата вызовов процедуры на основе error-предложения представляется весьма важным средством обработки особых ситуаций, связанных именно со спецификой самой процедуры, а не средств Maple-языка. В этой связи пользователю предоставляется возможность организации собственных алгоритмов обработки как ошибочных, так и особых ситуаций, связанных с алгоритмом вычислений или передаваемыми процедуре аргументами. Как это иллюстрирует следующий простой фрагмент:

VK := proc() local k; if nargs = 0 then error "procedure call has no arguments" else for k to nargs do if whattype args[ ]( k ) ≠ 'float' then error "%-1 argument has a type different from `float`", k

end if

end do

end if;

"Procedure body"

end proc

> VK(6.4, 5.9, 10.2, 3, 17);

Error, (in VK) 4th argument has a type different from 'float'

> lasterror; ⇒ "%-1 argument has type different from 'float'", 4

> VK(6.4, 10.2, 5.9, 3.9, 2.7, 17.7); ⇒ "Procedure body"

Процедура VK программно обрабатывает ситуацию получения как NULL-значения, так и аргумента некорректного типа на основе error-предложения с возвратом соответствующей диагностики, которая вне тела процедуры может обрабатываться программно.

Особенности и полезные рекомендации по использованию функции ERROR (error-предложения) для организации дополнительных выходов из процедур достаточно детально представлены в наших книгах [12-14,39,41]. Здесь же мы лишь сделаем одно замечание относительно реализации функций {RETURN, ERROR} и предложений {return, error}. В настоящее время данные средства попарно функционально эквивалентны, однако синтаксис их использования при создании процедур и программных модулей различен, а именно. В пакете Maple 5 и ниже использовались функции RETURN и ERROR, что в силу их концепции позволяло использовать их в любой точке, подобно обычным функциям. Во многих случаях это представляется весьма удобным. Однако, начиная с Maple 6, пакет, одновременно допуская указанные функции и предложения, между тем, настоятельно рекомендует использовать все же последние, чей синтаксис существенно уже. Вероятно, возможен вариант исключения функций из последующих релизов, что создаст еще один аспект несовместимости релизов уже на уровне исходных текстов. Такой подход несколько настораживает и порождает два естественных вопроса, а именно: (1) почему при проектировании встроенного языка – базовой компоненты пакета – не были решены столь важные концептуальные моменты (это наводит на мысль об отсутствии концептуальной целостности пакета), и (2) если будут удалены указанные средства, то нарушится одно из краеугольных требований, которым должно удовлетворять качественное ПО – совместимость «снизу-вверх» на уровне встроенного языка пакета. Все это вызывает определенные недоумения и опасения со стороны пользователей.

Наконец, по вызову функции traperror(<Выражение>) возвращается результат вычисления выражения, указанного ее фактическим аргументом; если при этом в процессе вычисления возникает ошибочная ситуация, то возвращается идентифицирующее ее диагностическое сообщение, доступное для последующей обработки и не нарушающее естественный порядок вычислений. Таким образом, traperror-функция в отличие от функции ERROR (error-предложения) и результата стандартной обработки ошибочной ситуации не прекращает выполнения процедуры, а позволяет “блокировать” ошибочную ситуацию, предоставляя возможность более гибкой ее обработки. Простой фрагмент иллюстрирует вышесказанное:

H := proc() local k h z, , ; assign(z = 64 + sqrt(`+` args( ))), assign(h = traperror(z/`+` args( ))); if z = 0 then 'NULL − argument' elif h ≠ "numeric exception: division by zero" then return h else z/(59 + `+`(args))

end if

end proc > evalf([H(64, -59, 10, 17), H(64, 59), H(0, 0, 0)]); ⇒ [2.176776695, 0.6104921668, 1.084745763]

В данном фрагменте Н-процедура использует traperror-механизм для обработки особой ситуации, которая может приводить к аварийному завершению процедуры в период ее выполнения - нулевая сумма значений ее фактических аргументов инициирует ошибочную ситуацию `division by zero`. Данная ситуация обрабатывается traperror-функцией во избежание стандартной обработки Maple и обеспечивает пользовательскую обработку, хорошо усматриваемую из листинга процедуры.

При этом, следует иметь в виду, что по tracelast-команде языка можно получать полезную отладочную информацию, представляющую собой последнее содержимое стэка ошибок ядра пакета, как это иллюстрирует следующий достаточно простой фрагмент:

> Svegal:= proc(x) local y; y:= 64; 1/(x - y) end proc: Svegal(64); Error, (in Svegal) numeric exception: division by zero > tracelast;

Svegal called with arguments: 64

#(Svegal, 2): 1/(x-y)

Error, (in Svegal) numeric exception: division by zero locals defined as: y = 64

> S:= proc(n::integer) local k, G; G:= 0: for k to 10^n do G:= G+1 end do end proc: S(10); Warning, computation interrupted

> tracelast;

S called with arguments: 10

#(S, 3): G := G+1

Error, (in S) interrupted

locals defined as: k = 9089788, G = 9089787

Из второго примера фрагмента следует, что tracelast-команда позволяет идентифицировать точное местоположение прерванного вычисления процедуры и вывести промежуточные результаты на момент прерывания, что может оказаться весьма полезным при отладке, например, рекурсивных процедур.

Механизм try-предложения пакета. Для программной обработки ошибочных ситуаций, отражаемых в lasterror-переменной пакета, Maple-язык располагает встроенной функцией traperror, рассмотренной выше, и процедурами stoperror и unstoperror, наряду с try-предложением. Три первых средства остались в наследство от предыдущих релизов пакета (заинтересованный читатель детально с ними может ознакомиться, например, в [1012]), тогда как try-предложение явилось дальнейшим и более эффективным средством обработки ошибочных ситуаций. Данное средство впервые было включено в Maple 6.

Предложение try обеспечивает механизм для выполнения предложений Maple-языка в управляемой программной среде, когда ошибочные и особые ситуации без серьезной причины не будут приводить к завершению выполнения процедуры без возврата соответствующих сообщений. Предложение try имеет следующий формат кодирования:

try <Блок А Maple-предложений>

{catch "Строка соответствия 1": <последовательность Maple-предложений>}

=========================================================

{catch "Строка соответствия n": <последовательность Maple-предложений>}

{finally <последовательность Maple-предложений>} end try {:|;}

Формат предложения try указывает, что его блоки catch и finally необязательны. Более того, catch-блок имеет довольно простую структуру, а именно: "Строка соответствия": <последовательность Maple-предложений>. Механизм выполнения try-предложения сводится вкратце к следующему. После получения управления try-предложение инициирует выполнение блока А предложений Maple, в которых мы пытаемся локализовать ошибочные (особые) ситуации. Если в процессе выполнения А-блока не возникло таких ситуаций, то выполнение будет продолжено, начиная с Maple-предложений finally-блока, если он был определен. Затем выполнение продолжается с предложения, следующего за закрывающей скобкой `end try` try-предложения.

В случае обнаружения исключительной ситуации в процессе выполнения А-блока его выполнение немедленно прекращается и производится сравнение каждой из catch-строк на соответствие возникшей исключительной ситуации (данная ситуация отражается в переменных пакета lasterror и lastexception). Если такая catch-строка существует, то выполняется последовательность Maple-предложений, соответствующая данному catch-блоку (эти предложения расположены между ключевым словом catch и элементами try-предложения, а именно (1) следующий catch-блок, (2) finally-блок либо (3) закрывающая скобка «end try»). В данном случае предполагается, что исключительная ситуация распознана и обработана. Иначе исключительная ситуация предполагается необработанной и ее обработка передается вне try-предложения. При этом, найденный catch-блок может содержать функцию ERROR (error-предложение) без аргументов, которая также передает обработку исключительной ситуации вне try-предложения. Если исключительная ситуация передается вне try-предложения, то создается новый специальный объект, который повторяет имя текущей процедуры, текст диагностического сообщения, а также параметры исходной исключительной ситуации.

F := proc(x) try if x ≤ 42 then error "Branch_1"

elif 42 < x and x ≤ 47 then error "Branch_2" elif 47 < x and x ≤ 67 then error "Branch_3" elif 67 < x and x ≤ 100 then error "Branch_4" else sin( )x

end if

catch "Branch_1" Art( ): x

catch "Branch_2" Kr( ): x

catch "Branch_3": Sv(x) catch "Branch_4" Ar( ): x

end try

end proc

> seq(F(x), x = [42, 45, 64, 100, 350]); ⇒ Art(42), Kr(45), Sv(64), Ar(100), sin(350)

Предложение try может содержать любое количество catch-блоков и в каждом из этих блоков выполняется соответствующая последовательность Maple-предложений. Для иллюстрации вышесказанного и механизма активации catch-блоков рассмотрим реализацию некоторой кусочно-определенной функции посредством try-предложения (предыдущий фрагмент); в качестве строк совпадения catch-блоки используют диагностику по запрограммированным ошибкам, сформированным на основе предложений error и return. На наш взгляд, данный фрагмент достаточно прозрачно иллюстрирует механизм инициирования catch-блоков, обеспечивающий достаточно широкий спектр приложений try-предложения для обработки различного типа исключительных (особых) ситуаций, возникающих как естественным образом при выполнении некоторого вычислительного алгоритма или работы с внешними данными и т.д., так и созданных пользователем с целью обеспечения ветвления выполнения алгоритма в зависимости от тех или иных условий, заранее предусмотренных.