При нормальных условиях предложения finally-блока выполняются всегда, как только try-предложение потеряет управление. Данное поведение сохраняется даже, если предложения catch-блока обеспечивают обработку исключительной ситуации вне try-предложения, сгенерированной некоторой новой исключительной ситуацией, или выполнением любого из предложений return, break, next языка пакета. Если исключительная ситуация возникает в catch-блоке и она распознана отладчиком (debugger), то программа пользователя прекращает выполнение и предложения finally-блока не выполняются. Предложения finally-блока не выполняются и в том случае, если возникает одна из следующих исключительных ситуаций:
(1) вычисление превысило отведенный временной интервал (эта ситуация может быть обработана только в том случае, если timelimit-функция, ограничивающая время вычисления, инициирует прерывание типа «time expired», которое может быть опознано и обработано); (2) вычисление прервано извне вычислительного процесса (клавиши Ctrl+C, Break и т.д.);
(3) возникла внутренняя системная ошибка;
(4) неудачное выполнение ASSERT-функции или типификации локальной переменной;
(5) переполнение пакетного стэка.
Ошибка, возникающая в процессе упрощения try-предложения, не может быть распознана и finally-блок (если такой был определен) не выполняется, поэтому try-предложение не может быть выполнено в данной точке. При этом, если исключительная ситуация возникает в процессе выполнения catch-блока или finally-блока, то она рассматривается как возникшая вне области try-предложения. При проверке на совпадение строки соответствия catch-блока с диагностикой исключительной ситуации используются следующие соглашения пакета:
• строки соответствия рассматриваются как префиксы текстов сообщений, генерируе- мых исключительными ситуациями (переменные lastexception и lasterror);
• ни специальный объект, созданный ERROR-функцией (error-предложением), ни строки соответствия catch-блока не вычисляются;
• если строка соответствия имеет длину n, то текст, сгенерированный ERROR-функци- ей либо error-предложением, сравнивается только в пределах его первых n символов;
• отсутствующая строка соответствия сопоставима с любой исключительной ситуацией;
• в try-предложении catch-блок с конкретной строкой соответствия может быть только в единственном числе.
Результатом выполнения try-предложения является результат выполнения его последнего Maple-предложения. Таким образом, механизм try-предложения носит наиболее общий характер, обеспечивая возможность обработки, практически, любой исключительной ситуации, инициированной как ERROR-функцией (error-предложением) на основе конкретного вычислительного алгоритма, так и другими средствами пакета. В нашей книгах [41,103] представлен целый ряд интересных применений try-предложения для обработки различного типа ошибочных и особых ситуаций. Использованные в них приемы могут оказаться весьма полезными для программирования приложений в Maple.
В завершение отметим еще одно средство вывода процедурой результатов информационного характера, который обеспечивается процедурой WARNING, имеющей формат:
WARNING({Msg {, p1, p2, …, pn}})
где Msg и pk – полностью соответствуют описанию error-предложения, представленного в начале данного раздела с очевидной заменой диагностического типа сообщения Msg на информационное. В случае установки параметра warnlevel=0 (по умолчанию warnlevel= 3) процедуры interface вывод сообщений не производится и вызов WARNING подавляется. Следующий простой фрагмент иллюстрирует вышесказанное:
> H:= proc() local k; WARNING("Среди полученных значений оказалось %1 integer- типа", add(`if`(type(args[k], 'integer'), 1, 0), k = 1 .. nargs)); `+`(args) end proc; > H(64, 59, 19.95, 10, 17, 19.99, 39, 44, 3.1, 95.99, 350); ⇒ 722.03 Warning, Среди полученных значений оказалось 7 integer-типа > interface(warnlevel = 0); H(64, 59, 19.95, 10, 17, 19.99, 39, 44, 3.1, 95.99, 350); ⇒ 722.03 |
Возможность вывода сообщений предоставляет удобный механизм информирования о ходе выполнения текущего документа, процедуры или модуля. Для возможности обработки сообщений, выводимых процедурой WARNING, нами была создана ее полезная модификация [103], обеспечивающая через глобальную _warning-переменную возврат последнего в текущем сеансе сообщения, генерируемого WARNING, например:
> WARNING("Help database for library <%1> has been created",
"C:/AVZ/AGN\VSV/Art\Kr");
Warning, Help database for library <C:/AVZ/AGNVSV/ArtKr> has been created
> _warning;
"Help database for library <C:/AVZ/AGNVSV/ArtKr> has been created"
Данный подход позволяет успешно программно обрабатывать сообщения в Maple, начиная уже с шестого релиза пакета.
Представленные выше средства возврата результатов вызова процедуры как в нормальном режиме ее выполнения, так и при наличии особых и ошибочных ситуаций достаточно эффективны. В дальнейшем будет представлен целый ряд весьма интересных процедур, использующих все рассмотренные здесь механизмы возврата результатов вызова, а также ряд нестандартных приемов в данном направлении.
Рассмотрим теперь вопросы использования механизма процедур несколько детальнее, учитывая прикладную значимость процедур для разработки различных приложений в Maple. Прежде всего, рассмотрим вопрос создания вложенных процедур, т.е. процедур, определения которых, в свою очередь, содержат определения других процедур. В целом ряде случаев данная возможность может оказаться достаточно эффективным средством при программировании прикладных задач. В общем случае поименованная процедура наряду с общепринятым допускает и определение посредством следующей (часто весьма удобной) вычислительной конструкции: assign(<Id-процедуры> {,|=} proc() ... end proc)
эквивалентной рассмотренной выше стандартной конструкции Id := proc( ) ... end proc. Данное обстоятельство позволяет использовать определение процедуры не только внутри другой процедуры, но и внутри вычислительных конструкций, например:
> assign(A, proc() local k; sum(args[k], k=1..nargs) end proc); A(59, 64, 67); ⇒ 190
> [assign(G, proc(x, y) evalf(sqrt(x^2 + y^2)) end proc), G(42, 47)]; ⇒ [63.03173804]
Более того, как будет показано ниже, при использовании данных подходов к определению подпроцедур имеется принципиальное различие. Первый пример фрагмента иллюстрирует определение А-процедуры описанным способом с последующими ее вычислением и вызовом. Тогда как второй пример фрагмента иллюстрирует использование определенной вышеуказанным способом G-процедуры в вычислительной конструкции.
WM := proc(x::numeric, y::numeric, z::{10, 17}) `if`(z = 10, assign(AG, proc(x y, ) [x y, , evalf(sqrt(x y× ), 6)] end proc ), assign(AV, proc(x y, ) [x y, , evalf(sqrt(x + y), 6)] end proc )); AG(x y, ) end proc > [WM(64, 59, 10), AG(1995, 2006)]; ⇒ [[64, 59, 61.4492], [1995, 2006, 2000.49]] WM1 := proc(x::numeric, y::numeric, z::{10, 17}) local A G V AG AV, , , , ; if z = 10 then AG := proc(x y, ) [x y, , evalf(sqrt(x y× ), 6)] end proc ; AG(x y, ) else AV := proc(x y, ) [x y, , evalf(sqrt(x + y), 6)] end proc ; AV(x y, ) end if end proc > [WM1(59, 64, 10), AG(64, 42)]; ⇒ [[59, 64, 61.4492], AG(64,42)] WM2 := proc(x::numeric, y::numeric, z::{10, 17}) local A G V, , ; global AG AV, ; if z = 10 then AG := proc(x y, ) [x y, , evalf(sqrt(x y× ), 6)] end proc else AV := proc(x y, ) [x y, , evalf(sqrt(x + y), 6)] end proc end if; eval(parse("" || A || (`if`(z = 10, G V, )) || "(" || x || "," || y || ")")) end proc > [WM2(59,64,17), AV(59, 65), AG(64, 42)]; ⇒[[59, 64, 11.0905], [59, 65, 11.1355], AG(64,42)] |
Приведенный выше способ определения процедуры оказывается весьма полезным в целом ряде приложений. Между тем, вложенные процедуры можно создавать и на основе стандартного определения. Предыдущий простой фрагмент иллюстрирует применение обоих способов для определения вложенных процедур, генерируемых основной процедурой в зависимости от конкретных условий применения.
Первый пример фрагмента представляет определение вложенной WM-процедуры, возвращающей результат вызова AG-подпроцедуры, тело которой определяется на основе assign-конструкции в зависимости от значения третьего фактического z-аргумента, передаваемого внешней процедуре. При этом, в текущем сеансе доступной является и AGподпроцедура, если ее идентификатор не декларируется во внешней процедуре как локальная переменная.
Второй пример представляет вложенную WM1-процедуру, возвращающую результат вызова одной из подпроцедур (AG, AV) в зависимости от значения третьего фактического z-аргумента, передаваемого внешней процедуре. При этом, для текущего сеанса обе подпроцедуры носят локальный характер, т.е. доступны лишь в рамках содержащей их внешней WM1-процедуры. Наконец третий пример представляет вложенную WM2-процедуру, эквивалентную предыдущей, но лишь с той разницей, что входящие в нее подпроцедуры AG, AV определены глобальными. Результат последующих вызовов процедур WM2, AG, AV иллюстрирует вышесказанное.
Из сказанного следует вынести следующий важный вывод, иллюстрируемый табл. 8. Если в случае явного декларирования идентификаторов подпроцедур область их действия соответствует декларации независимо от способа их определения, то в случае отсутствия декларации определенная стандартно и посредством assign-процедуры подпроцедура становится соответственно локальной и глобальной.