> ('x', 'y', 'z', 'g', 'h') &ma _NULL; x, y, z, g, h;
> ('x', 'y', 'z', 'g', 'h') &ma 2006; x, y, z, g, h; ⇒ 2006, 2006, 2006, 2006, 2006
> ('x', 'y', 'z', 'g', 'h') &ma (sin(a)*cos(b)); x, y, z, g, h;
sin(a) cos(b), sin(a) cos(b), sin(a) cos(b), sin(a) cos(b), sin(a) cos(b) > ('x', 'y', 'z', 'g', 'h') &ma ((a+b)/(c-d)); x, y, z, g, h;
a + b a + b a + b a + b a + b
, , , ,
c − d c − d c − d c − d c − d
Первый пример фрагмента представляет расширение тестирующей type-функции на новый тип float_list, определяющий списочную структуру с элементами float-типа. С этой целью идентификатору `type/<Id>` присваивается определение процедуры, вызываемой в тот момент, когда делается попытка протестировать произвольное выражение посредством вызова type(Выражение, float_list). В остальных случаях применяется вызов встроенной type-функции пакета. Представленная процедура тестирует, в первую очередь, на соответствие типа выражения типу list и в случае наличия такого соответствия на втором этапе тестирует на соответствие типа каждого элемента списка float-типу, возвращая в зависимости от наличия/отсутствия такого соответствия true/false-значение.
Во втором примере определяется модификация стандартного complex-типа. Вызов стандартной функции type(Z, complex) возвращает true-значение, если Z – выражение формы x + y*I, где x (если существует) и y (если существует), конечны и типа 'realcons'. При этом, вызов процедуры type(X, complex(t)) возвращает true-значение, если Re(X) (если существует) и Im(X) (если существует) – оба типа t. К сожалению, стандартная процедура не обеспечивает, на наш взгляд, корректного тестирования Maple-выражений упомянутого complex-типа, включая в него и действительные выражения. Следующий простой пример убедительно подтверждает вышесказанное, а именно:
> map(type, [-9.1942, 64, 350, -2006, 10/17], 'complex'); ⇒ [true, true, true, true, true]
К сожалению, эта серьезная ошибка имеет место для всех релизов Maple, начиная с шестого. Процедура 'type/complex1' имеет те же самые формальные аргументы как и стандартная процедура и устраняет ошибки, свойственные второй. Кроме того, она обеспечивает более широкую проверку Maple-объектов complex-типа. Между тем, процедура не различает числовые типы {fraction, rational}, идентифицируя их более общим numericтипом. В отличие от стандартной процедуры, процедура 'type/complex1' обеспечивает более корректную проверку выражений Maple на complex-тип. Приведенные примеры иллюстрирую применение как стандартной, так и 'type/complex1'-процедуры.
В третьем примере определяется расширение convert-функции Maple-языка на случай конвертации символов строчного значения в их заглавный эквивалент. В данном случае вызов функции convert(`Строка`, 'uppercase') инициирует вызов `convert/uppercase`-процедуры, обеспечивающей соответствующую конвертация указанной строки.
В конструкции lhs: = rhs оператор назначения `:=` присваивает lhs выражение rhs. Кроме того, оператор назначения допускает многократные назначения. Однако, в этом случае количество элементов последовательности lhs должно строго соответствовать количеству элементов последовательности rhs, иначе возникает ошибка с диагностикой "Error, ambiguous multiple assignment". Тогда как в ряде случаев возникает необходимость назначения того же самого выражения достаточной длинной последовательности имен или вызовов функций.
Данная проблема решается оператором &ma, который имеет идентичный с оператором `:=` приоритет. Оператор &ma имеет два формата кодирования, а именно: процедурный и операторный. Вообще говоря, в обоих случаях элементы lhs должны быть закодированы в невычисленном формате. Исключение – только самое первое назначение. Кроме того, в операторном формате, левая часть lhs должна быть закодирована в скобках. Кроме того, если правая часть rhs удовлетворяет условию type(rhs, {`..`, `<`, `<=`, `.`, `*`, `^`, `+`, `=`}) = true, то правая часть должна быть также закодирована в скобках. Наконец, если необходимо присвоить NULL-значение элементам левой части lhs, то в качестве rhs кодируется _NULL-значение. Успешный вызов процедуры &ma либо применения оператора &ma возвращает NULL-значение с выполнением указанных назначений. В целом ряде приложений оператор &ma оказывается достаточно полезным.
Наконец, последний пример фрагмента иллюстрирует определение пользовательского оператора &ma [103], обеспечивающего многократные присвоения одного и того же выражения переменным либо вызовам функций. На данном вопросе имеет смысл остановиться отдельно, учитывая его важность для практического программирования в среде пакета.
Наряду со стандартно определяемыми, Maple-язык допускает пользовательские операторы (в терминологии пакета называемые нейтральными операторами), идентифицируемые ключевыми словами вида &<символ>, при этом символ кодируется без верхних кавычек и не должен содержать следующих символов:
& | ( ) [ ] { } ; : ‘ ` # % \ пробел перевод строки
Длина &-цепочки символов не должна превышать 495. Сам Maple-язык использует такого типа оператор &* для представления некоммутативного произведения, тогда как все другие идентификаторы &<символ> описанного формата рассматриваются пакетом в качестве ключевых слов для идентификации пользовательских операторов.
Пользовательский оператор можно использовать в качестве унарного префиксного, бинарного инфиксного операторов или вызова процедуры/функции. В любом из указанных случаев производится вызов процедуры, чье определение имеет следующий вид:
`&<Символ>` := proc(x, y, z, ...) ... end proc {:|;}
при этом, в случае одного формального аргумента получаем унарный префиксный оператор, двух аргументов – бинарный инфиксный оператор и более двух – n-арный (n ≥ 3) префиксный оператор; в любом из этих случаев определен вызов &<Символ>(x, y, z, ...). Более того, Maple-язык не накладывает на пользовательские операторы специальной семантики и рассматривает идентификатор оператора в качестве имени сооответствующей пользовательской процедуры. В общем случае, пользовательский оператор является n-арным префиксным оператором либо вызовом n-арной функции, т.е. следующие две конструкции эквивалентны в среде Maple-языка, а именно:
&<Символ> (x1, x2, ..., xn) ≡ &<Символ>(x1, x2, ..., xn) (n ≥ 1)
Простой фрагмент иллюстрирует рассмотренные типы пользовательских операторов:
> `&Cs`:=proc(x) subs(I= -I, evalc(x)) end proc: z:=(a + b*I)/(c - d*I)*I + h*I: [&Cs z, &Cs(z)]; − (ac − + bd II) I − h I, − (ac − + bd II) I − h I > `&Kr`:= proc(x::numeric, y::numeric) evalf(sqrt(x*y)/(x + y) + sin(x*y)) end proc: > [1942 &Kr 2006, &Kr(1942, 2006)]; ⇒ [1.490076388, 1.490076388] > `&Art`:= proc() sqrt(product(args['k'], 'k'=1..nargs))/sum(args['k'], 'k'=1..nargs) end proc: 16 4302870233 , 16 4302870233 > [&Art (59, 64, 39, 10, 17, 44), &Art(59, 64, 39, 10, 17, 44)]; ⇒ |
Первый пример представляет унарный префиксный &Cs-оператор, применение которого к комплесному числу возвращает сопряженное ему число. Второй пример представляет бинарный инфиксный &Kr-оператор, определенный над двумя числовыми значениями и возвращающий значение, вычисляемое по указанной формуле. Наконец, третий пример представляет n-арный префиксный &Art-оператор, обеспечивающий над выражениями вычислительную процедуру также формульного характера. Каждый из приведенных примеров иллюстрирует применение соответствующего оператора как в традиционной для него нотации, так и в форме вызова соответствующей ему процедуры. Таким образом, в среде Maple-языка пользовательский (нейтральный) &-оператор представляется вызовом соответствующей процедуры. При этом, инфиксная нотация допустима лишь для случая двух операндов, тогда как префиксная – для любого числа операндов. Описанный метод определения пользовательских операторов с учетом механизма процедур Maple-языка довольно прозрачен и имеет важное прикладное значение, позволяя вводить собственные &-операторы для специальных операций. В качестве одного из таких приложений рассмотренного метода уже был приведен пример &ma оператора, приведем еще пару полезных примеров.
&Shift := proc() local k; `if`(nargs < 3, ERROR("incorrect quantity <%1> of actual arguments" nargs, ), `if`( not (type(args 1[ ], 'symbol') and type(args 2[ ], 'list')), ERROR("inc\ orrect type of the first argument <%1> and/or the second argument <%2>", args[1], args[2]), `if`(nops(args[2]) ≠ nargs − 2, ERROR( "incorrect quantity of shifts <%1> and/or leading variables of function <%2>" , nops args[2]( ), nargs − 2), `if`( member(false, {op(map(type, [args ' '[ k ] $ (' 'k = 3 .. nargs)], name))}) = true, ERROR("incorrect types of actual arguments %1"3 .. nargs, ), NULL)))) ; args 1[ ]((args[' 'k + 2] + args 2[ ][' 'k ]) $ (' 'k = 1 .. nops(args 2[ ]))) end proc > &Shift (Ar, [a, b, c, d, g, s, q], x, y, z, t, u, r, h); ⇒ Ar(x+a, y+b, z+c, t+d, u+g, r+s, h+q) > &Shift (Ar, k, x, y, 64, z); Error, (in &Shift) incorrect type of the first argument <Ar> and/or the second argument <k> lop3 := proc(O::symbol, x::{0, 1, 2}, y::{0, 1, 2}) if O = 'Inv' then if x = 0 then 2 else x mod 2 end if elif O = 'Dis' then if [x y, ] = [0, 0] then 0 elif type [( x y, ], list {( 0, 1})) then 1 else 2 end if elif O = 'Con' then if [x y, ] = [2, 2] then 2 elif x y× = 0 then 0 else 1 end if elif O = 'Webb' then if [x y, ] = [0, 0] then 1 elif type [( x y, ], list {( 0, 1})) then 2 else 0 end if else error "Operation <%1> is not stipulated", O end if end proc > lop3(Inv, 0), lop3(Dis, 0, 2), lop3(Con, 1, 2), lop3(Webb, 1, 1); ⇒ 2, 2, 1, 2 > lop3(Art, 0, 2); Error, (in lop3) Operation <Art> is not stipulated |
Вышеприведенный фрагмент иллюстрирует применение описанного способа для реализации пользовательского оператора сдвига `&Shift` для функции от нескольких переменных, определяемого следующим соотношением: