Вторым средством, обеспечивающим временной контроль вычислений, служит встроенная функция timelimit(t, V). Если время в сек., затребованное процессором для вычисления V-выражения, не превысило значения, определенного первым фактическим t-аргументом, то возвращается результат вычисления V-выражения. В противном случае генерируется ошибочная ситуация с диагностикой "Error, (in V) time expired", обрабатываемая рассмотренной выше traperror-функцией или try-предложением, рассматриваемым ниже. Однако функция timelimit не используется с функциями машинной арифметики, т.к. не обрабатывает ситуацию исчерпания отведенного временного интервала для вычислений, например:
> S:= proc(n) local k; for k to n do sin(10.3*n) end do end proc: S(10^5), evalhf(S(10^5)), timelimit(3, evalhf(S(10^7)));
-0.5431520991, -0.543152099236042907, 0.764330635010303183
> timelimit(3, S(10^7));
Error, (in sin) time expired
Из фрагмента легко заметить, что на evalhf-функции машинной арифметики timelimitфункция не приводит к желаемому результату, не ограничивая времени вычисления.
Timetest := proc(t::{float, integer}, x::anything, V::symbol, p::evaln) local h z, ; z := time( ); if traperror timelimit( ,( t assign(h = V x( )))) = "time expired"then p := [evalf(time( ) − z, 6), 'undefined ']; false else p := [evalf(time( ) − z, 6), h]; true end if end proc > G:= proc(n) local k; for k to n do k end do end proc: > Timetest(0.5, 10^6, G, p), p; ⇒ true, [0.234, 1000000] > Timetest(0.5, 10^7, G, p), p; ⇒ false, [0.516, undefined] |
В качестве примера применения timelimit-функции предыдущий фрагмент представляет простую Timetest(t, x, V, p)-процедуру, возвращающую true-значение только в том случае, когда вычисление V(x)-выражения укладывается в отведенный ему интервал в tсекунд, в противном случае ею возвращается false-значение. При этом, через аргумент p процедуры возвращается список, первый элемент которого определяет время вычисления искомого выражения, а второй - результат вычисления либо undefined-значение соответственно. В данном фрагменте Timetest-процедура применяется для временного тестирования вызова простой G-процедуры. При этом, следует иметь в виду, что в общем случае совместное использование функций time и timelimit в одной процедуре может приводить к некорректным результатам [12]. Вместе с тем, при получении определенного навыка использования функциональных средств языка существующих средств обработки особых и аварийных ситуаций оказывается вполне достаточным для создания в среде Maple довольно эффективных пользовательских систем обработки ситуаций такого рода. Более того, на основе знания специфики реализуемого в среде языка алгоритма решаемой задачи функциональные средства Maple-языка дают возможность проводить предварительный программный анализ на предмет предупреждения целого ряда возможных особых и аварийных ситуаций. Рассмотрим теперь вопросы обработки особых и аварийных ситуаций в контексте программирования процедур.
По функции ERROR, имеющей формат кодирования следующего вида:
ERROR({V1, V2, …, Vn})
производится немедленный выход из процедуры в точке ее вызова с возвратом диагностического сообщения следующего вида:
Error, (in {Proc|unknown}) {<V1>, <V2>, ... ,<Vn>}
где Proc - имя процедуры, вызвавшей ERROR-ситуацию, и <Vk> - результат вычисления Vk-выражения (k = 1 .. n); unknown-идентификатор выводится для непоименованной процедуры. Тогда как по предложению error, имеющему формат кодирования вида: error {Msg {, p1, p2, …, pn}} {:|;}
производится немедленный выход из процедуры в точке его выполнения с возвратом диагностического сообщения следующего вида:
Error, (in {Proc|unknown}) { Msg({p1, p2, …, pn})}
где Proc – имя процедуры, вызвавшей error-ситуацию, и <Msg({p1,p2,…,pn})> - результат подстановки pk-параметров в Msg-сообщение; unknown-идентификатор выводится для непоименованной процедуры. Msg - строка текста, определяющая суть ошибочной ситуации. Она может содержать пронумерованные параметры вида «%n», где n – целое число от 0 до 9. Тогда как pk – один или более параметров (Maple-выражений), которые подставляются вместо соответствующих по номеру вхождений '%n', когда возникает ошибочная ситуация. Например:
> error “invalid arguments <%1> and <%2>”, 3, 7; Error, invalid arguments <3> and <7> > 64/0;
Error, numeric exception: division by zero
> error;
Error, numeric exception: division by zero
> restart; error;
Error, unspecified error
При этом, если Msg отсутствует, то выполнение error-предложения в качестве диагностики выводит диагностику последней ошибочной ситуации текущего сеанса, если же и такой нет, то возвращается сообщение о неспецифицированной ошибке. Если параметр имеет вид '%n', то в возвращаемом сообщении он появляется как n-й параметр in lprintнотации, тогда как вид '%-n' обеспечивает его появление в сообщении в обычной нотации, например:
> error “%1 and %2 arguments are invalid”, 3, 7; Error, 3rd and 7th arguments are invalid
> error “%-1 and %-2 arguments are invalid”, 3, 7; Error, 3rd and 7th arguments are invalid
> ERROR(“%-1 and %-2 arguments are invalid”, 3, 7);
Error, 3rd and 7th arguments are invalid
Сказанное в полной мере относится и к оформлению ERROR-функции, начиная с релиза 6 Maple. Детальнее с оформлением диагностических сообщений для ERROR-функции (error-предложения) можно ознакомиться в справке по пакету.
При этом, значения Vk-выражений из ERROR-функции (Msg для error-предложения) помещаются в глобальную lasterror-переменную пакета и доступны для последующей обработки возникшей процедурной ошибки, которая с точки зрения Maple-языка в общем случае может и не быть ошибочной, как это иллюстрирует следующий фрагмент:
> restart; lasterror; ⇒ lasterror > A:= proc() local k; `if`(type(nargs, ‘odd’), ERROR(“odd number of arguments”), `+`(args)) end proc: A(64, 59, 39, 10, 17, 44, 95, 2006); ⇒ 2334 > A(64, 59, 39, 10, 17, 44, 2006); Error, (in A) odd number of arguments > lasterror; ⇒ "odd number of arguments" > if lasterror = “odd number of arguments” then `Introduce new arguments` end if; Introduce new arguments > proc(x, y) `if`(x > y, ERROR(“for args[1] & args[2]”,args[1] > args[2]), y/x) end proc(17,10); Error, (in unknown) for args[1] & args[2], 10 < 17 > proc(x, y) if x > y then error “for args[1] & args[2]”, args[1] > args[2] else y/x end if end proc(17, 10); Error, (in unknown) for args[1] & args[2], 10 < 17 |
В данном фрагменте простая А-процедура предусматривает обработку ошибочной ситуации, определяемой фактом получения процедурой при вызове нечетного числа фактических аргументов. В случае такой ситуации по ERRОR-функции производится выход из процедуры с возвратом соответствующего сообщения. После этого на основе анализа значения lasterror-переменной производится выбор пути дальнейших вычислений. Последний пример фрагмента иллюстрирует возврат ошибочного сообщения, связанного с определенным соотношением между аргументами, для непоименованной процедуры пользователя. При этом, использованы как ERROR-фунция, так и эквивалентное ей error-предложение.
> plot(x);
Plotting error, empty plot
> lasterror, lastexception; ⇒ lasterror, lastexception
> ulibrary(8, `C:/AVZ/AGV\VSV/Art\Kr`, MKDIR); Error, During delete of MKDIR - could not open help database
> lasterror, lastexception; ⇒ lasterror, lastexception
Следует отметить, что к сожалению пакет Maple не отражает в переменных lasterror и lastexception целый ряд весьма существенного типа ошибочных ситуаций, связанных, прежде всего, с графическими объектами (а в общем случае со средствами GUI пакета), как это весьма наглядно иллюстрирует предыдущий достаточно простой фрагмент. В свою очередь, это же не позволяет обрабатывать такого типа ошибки средствами пакета. При этом, другого типа недоработки Maple иначе как «чехардой» назвать и вовсе трудно. В частности, доступ к отсутствующему файлу или каталогу для релизов 6–9 пакета инициирует ошибочную ситуацию "file or directory does not exist", тогда как в релизе 10 возвращается несколько иная диагностика такой ситуации "file or directory, %1, does not exist". Диагностика ошибочной ситуации "wrong number (or type) of parameters in function …" релизов 6 – 9 пакета Maple изменена в релизе 10 на диагностику "invalid input: …". Серьезными причинами такие замены объяснить трудно. Указанные моменты потребовали очередной корректировки нашей Библиотеки [41,103], например, путем корректировки try-предложения, а именно:
try ….. ……… catch "file or directory does not exist": … catch "file or directory, %1, does not exist": …… (введено дополнительно) …….... end try |
В других ситуациях использовался подобный подход либо корректировалась используемая процедурами диагностика с учетом новых реалий. Естественно, подобная практика не отвечает принципам создания качественного ПО, не говоря уже о самом престиже разработчиков подобных программных средств.
Так как механизм ERROR-функции (error-предложения) позволяет производить обработку ситуаций, ошибочных с точки зрения внутренней логики реализуемого процедурой алгоритма, т. е. прогнозируемых ошибок, то для этих же целей может быть использована и функция RETURN (return-предложение), как это иллюстрирует следующий фрагмент:
> WM:= proc() local k; global V; for k to nargs do if whattype(args[k]) <> 'float' then error nargs, [args] end if end do: `End of WM-procedure` end proc: WM(6.4, 350, 10, 17); lasterror; Error, (in WM) 4, [6.4, 350, 10, 17] 4, [6.4, 350, 10, 17] > WM1:= proc() local k; global V; for k to nargs do if whattype(args[k]) <> 'float' then return nargs, [args] end if end do: `End of WM1-procedure` end proc: WM1(6.4,350,10,17); 4, [6.4, 350, 10, 17] > if whattype(%) = exprseq then WM1(op(evalf(lasterror[2]))) end if; End of WM1-procedure > if whattype(%%) = exprseq then WM1(op(evalf(%%[2]))) end if; End of WM1-procedure |
В приведенном фрагменте иллюстрируются эквивалентные конструкции по обработке передаваемых WM-процедуре фактических аргументов, созданные на основе предложений error и return. При этом, если результат выполнения предложения error можно получать через глобальную переменную lasterror, то по return-предложению он доступен непосредственно, что в целом ряде случаев бывает более предпочтительным.