Error, (in A1) 1st argument must has type {list, set} but had received symbol-type > A1([1, 2, 3, 4, 5, 6], 64);
Error, (in A1) 1st argument must be symbol but had received list
Попытка определить такую операцию для стандартно типированного L-аргумента в Апроцедуре вызывает ошибку выполнения, тогда как, определив этот же L-аргумент как аргумент uneval-типа и использовав в дальнейшем обращение к нему через eval-функцию, получаем вполне корректную А1-процедуру, обеспечивающую обновление «на месте» списка/множества L путем удаления его элементов, определенных вторым a-аргументом, в качестве которого может выступать как отдельный элемент, так и их список/ множество. Проверка же на тип фактического L-аргумента производится уже программно в самой процедуре; при этом, проверяется не только на тип {list, set}, но и на получение идентификатора объекта, а не его значения (т.е. в качестве фактического L-аргумента должно выступать имя списка/множества). Данный прием может оказаться весьма полезным в практическом программировании, именно он используется рядом процедур нашей Библиотеки [41,103,108,109].
Начиная с Maple 10, кроме типирования формальных аргументов пакет допускает определение для позиционных аргументов значений по умолчанию. Кодируется это посредством оператора присвоения в формате <аргумент> := <значение>, например:
> P:= proc(a, b, c:=sin(x), d) (a+b)/(c+d) end proc; Error, optional positional parameters must follow any required positional parameters > P:= proc(a, b, c:=sin(x), d:=cos(x)) (a+b)/(c+d) end proc: > P(a, b), P(10, 17, 5), P(10, 17, 5, 2); a + b 27 27 , , sin( )x + cos( )x 5 + cos( )x 7> P1:= proc(a, b, c, d) (a+b)/`if`(nargs = 2, (sin(x) + cos(x)), `if`(nargs = 3, (c+cos(x)), (c+d))) end proc: > P1(a, b), P(10, 17, 5), P(10, 17, 5, 2); a + b 27 27 , , sin( )x + cos( )x 5 + cos( )x 7 |
При этом, определенные таким способом позиционные аргументы должны быть последними в списке формальных аргументов, иначе инициируется ошибочная ситуация как это иллюстрирует первый пример фрагмента. Этого можно избежать, кодируя их в фигурных скобках, как иллюстрирует следующий простой пример:
> P:=proc(f::symbol, x::symbol, {b:= evalf}, c::range(posint)) b(int(f(x), x=c)) end proc;
P := proc(f::symbol, x::symbol, c::range(posint), {b := evalf}) b(int(f(x), x = c)) end proc
> P(sin, y, 1..5, b=G), P(sin, y, 1..5, G), P(sin, y, 1..5, evalf), P(sin, y, 1..5, b);
G(cos(1) - cos(5)), 0.2566401204, 0.2566401204, true(cos(1) - cos(5))
Из него, в частности, следует, что использование в вызове процедуры такого аргумента в виде уравнения либо значения по умолчанию приводит к требуемым результатам, тогда как вызов только на левой части вместо фактического аргумента производит подстановку вместо фактического аргумента true-значения. В определенной мере данный механизм и его расширения в ряде случаев оказываются довольно полезными, однако вызывают несовместимость с более ранними релизами. Между тем, этот механизм в целом ряде случаев несложно реализуется программно и прежними средствами Maple-языка. Например, второй пример предпоследнего фрагмента может быть реализован способом, представленным последним примером фрагмента. Естественно, при большем количестве позиционных параметров программирование усложняется и представленный механизм более эффективен, одно ограничение – его действие ограничивается последними формальными аргументами.
В общем случае последовательность формальных аргументов можно представить в виде двух подпоследовательностей – позиционные и необязательные и/или ключевые аргументы; при этом, последние могут произвольно чередоваться только среди себе подобных, не пересекаясь с позиционными. Выше представлен один из механизмов поддержки необязательных и ключевых аргументов с использованием для них значений по умолчанию.
Между тем, пользователь и сам может создавать весьма эффективные системы обработки аргументов процедуры, как позиционных, так и ключевых со значениями для них по умолчанию; один из таких простых подходов представляет следующий фрагмент:
P := proc(x, y, z) if type(eval( )y , 'symbol') then return procname( ,x 2006, )z end if; x + + y z end proc > P(a, b, c), P(a, x-y, c), P(sin(x), h, cos(y)); ⇒ a + 2006 + c, a + x - y + c, sin(x) + 2006 + cos(y) |
Читателю рекомендуется разобраться с используемым практически полезным приемом.
Для организации процедуры наряду с предложениями, описывающими непосредственный алгоритм решаемой задачи (а в ряде случаев и для обеспечения самого алгоритма), язык Maple предоставляет ряд важных средств, обеспечивающих функции, управляющие выполнением процедуры. Прежде всего, к ним можно отнести переменные args и nargs, возвращающие соответственно последовательность переданных процедуре фактических аргументов и их количество. Оба эти средства имеют смысл только в рамках процедуры, а по конструкциям вида args{|[n]|[n..m]} можно получать {последовательность фактических аргументов|n-й аргумент| аргументы с n-го по m-й включительно} соответственно. Тогда как nargs-переменная возвращает количество полученных процедурой фактических аргументов. Назначение данных средств достаточно прозрачно и обусловливает целый ряд их важных приложений при разработке пользовательских процедур. В первую очередь, это относится к обработке получаемых процедурой фактических аргументов. В частности, nargs-переменная необходима с целью обеспечения определенности выполнения вычислений в случае передачи процедуре неопределенного числа аргументов. Следующий простой фрагмент иллюстрирует вышесказанное:
> SV:= proc() product(args[k], k= 1 .. nargs)/sum(args[k], k= 1 .. nargs) end proc: > 137*SV(42, 47, 62, 67, 89, 96, 350, 39, 44, 59, 64); 22698342960272179200 > GN:= proc() [nargs, [args]] end proc: GN(V, G, S, A, Art, Kr); [6, [V, G, S, A, Art, Kr]] > map(whattype, [59, 17/10, ln(x), 9.9, "RANS"]); ⇒ [integer, fraction, function, float, string] > Arg_Type:= proc() map(whattype, [seq(args[k], k= 1 .. nargs)]) end proc: > Arg_Type(59, 17/10, ln(x), 9.9, "RANS"); ⇒ [integer, fraction, function, float, string] > Arg_Type:= proc() map(whattype, [args[k]$k= 1 .. nargs]) end proc: > Arg_Type(59, 17/10, ln(x), 9.9, "RANS"); ⇒ [integer, fraction, function, float, string] |
Приведенный фрагмент достаточно прозрачен и особых пояснений не требует. Более того, как иллюстрирует уже первый пример, число передаваемых процедуре фактических аргументов не обязательно должно соответствовать числу ее формальных аргументов. Данный пример иллюстрирует, что в общем случае Maple-процедуру можно определять, не привязываясь к конкретному списку ее формальных аргументов, но определять формальной функциональной конструкцией следующего общего вида:
proc() <ТЕЛО> {Ψ(args[1], args[2], ..., args[n])|n = nargs} end proc {;|:} что оказывается весьма удобным механизмом для организации процедур, ориентированных, в первую очередь, на задачи символьных вычислений и обработки [9-14,39].
Дополнительно к переменным args и nargs можно отметить еще одну важную переменную procname, возвращающую имя процедуры, ее содержащей. В целом ряде случаев данная переменная оказывается весьма полезной, в частности, при возвращении вызова процедуры невычисленным. С этой целью используется конструкция простого формата 'procname(args)'. Многие пакетные процедуры возвращают результат именно в таком виде, если не могут решить задачу. Ряд процедур и нашей Библиотеки [103] поступают аналогичным образом. Между тем, переменная procname может использоваться и в других полезных приложениях. Следующий фрагмент иллюстрирует применение указанной переменной как для организации возврата вызова процедуры невычисленным, так и для вывода соответствующего сообщения:
AVZ := proc() if nargs ≤ 6 then WARNING "%1(%2) = %3", procname,( seqstr args( ), `+` args nargs( )/ ) else 'procname args( )' end if end proc > AVZ(64, 59, 39, 44, 10, 17); Warning, AVZ(64, 59, 39, 44, 10, 17)=233/6 > AVZ(64, 59, 39, 44, 10, 17, 6); ⇒ AVZ(64, 59, 39, 44, 10, 17, 6) |
Процедура AVZ при получении не более 6 фактических аргументов выводит соответствующее сообщение, описывающее вызов процедуры и его результат, тогда как в противном случае вызов процедуры возвращается невычисленным.
Наконец, для проверки допустимости передаваемых процедуре необязательных аргументов служит процедура DefOpt(args), где args – передаваемые аргументы [41-43,103].
DefOpt := proc() local `0` `1` `2` `3`, , , ; [assign('`0`' = [unassign '( `2`', '`3`')], '`1`' = [ ], '`3`' = (( ) → {seq(lhs([args] 1[ ][`2`]), `2` = 1 .. nops(args 1[ ]))})), seq(`if`( type(args[`2`], 'equation') and type(lhs args[( `2`]), 'symbol'), assign('`0`' = [op(`0`), lhs(args[`2`]) = [op(rhs(args[`2`])), 'makelist']]), `if`( type(args[`2`], 'equation') and type(lhs args[( `2`]), 'posint'), assign('`1`' = [op(`1`), args[`2`]]), ERROR "invalid arguments %1", [( args]))), `2` = 1 .. nargs) `if`, (nops(`1`) < `3` `1`( )[-1] or nops(`1`) ≠ nops(`3` `1`( )), ERROR "invalid positional values %1",( `1`), `if`(nops(`0`) ≠ nops(`3` `0`( )), ERROR("invalid options values %1", `0`), TABLE [( 'OptionParms' = TABLE(`0`), 'PositionalParms' = TABLE(`1`)])))] end proc |
Исходные тексты процедур Библиотеки, прилагаемой к книгам [41,103] и к настоящей, предоставляют хороший иллюстративный материал по использованию аргументов, переменных процедуры args, nargs и procname для разработки различных приложений.
Используемые в теле процедуры переменные по области определения делятся на две группы: глобальные (global) и локальные (local). Глобальные переменные определены в рамках всего текущего сеанса и их значения доступны как для использования, так и для модификации в любой момент и в любой Maple-конструкции, где их применение корректно. Для указания переменной глобальной ее идентификатор кодируется в global-секции определения процедуры, обеспечивая процедуре доступ к данной переменной. В этой связи во избежание возможной рассинхронизации вычислений и возникновения ошибочных ситуаций рекомендуется в качестве глобальных использовать в процедурах только те переменные, значения которых ими не модифицируются, а только считываются. Иначе не исключено возникновение отмеченных ситуаций, включая и непредсказуемые. Следующий пример иллюстрирует некорректность определения в процедуре глобальной хпеременной: