&Shift (G, [h1, h2, ..., hn], x1, x2, ..., xn) ⇒ G(x1 + h1, x2 + h2, ..., xn + hn)
Его первый операнд определяет имя функции, второй - список величин сдвигов по ведущим переменным функции и третий - последовательность ведущих переменных. Между элементами второго и третьего операндов предполагается взаимно-однозначное соответствие. Наряду с рядом полезных приемов, использованных при определении данного пользовательского &-оператора, иллюстрируется пример анализа операндов, над которыми определен оператор, на корректность. Предыдущий пример представляет определение оператора и некоторые результаты его применения для реализации указанного выше оператора функционального сдвига &Shift.
Тогдда как второй пример фрагмента представляет процедуру lop3(O, x, y), поддерживающую следующие четыре операции (О) 3-значной логики над переменными (x, y) из множества {0, 1, 2}, а именно: Inv – инверсия, Dis – дизъюнкция, Con – конъюнкция и Webb – функция Вебба. Данная процедура может быть довольно несложно расширена и на другие операции 3-значной логики (а в общем случае и к-значной), что позволит использовать ее для представления любых многозначных функций алгебры логики. Читателю в качестве весьма полезного упражнения рекомендуется, используя описанный метод, запрограммировать несколько &-операторов и процедур, определяющих какиелибо интересные нестандартные операции над данными и/или структурами данных, как рассмотренных, так и других практически полезных типов.
В завершении раздела целесообразно сделать одно полезное в практическом отношении замечание. Определения процедур можно объединять в модули, помещая их в качестве элементов (имя - вход, определение - выход) модульных таблиц, как это весьма наглядно иллюстрирует следующий достаточно простой фрагмент:
> PT[Sr]:= () -> `+`(args)/nargs: PT[H]:= () -> sqrt(`*`(args)): PT[G]:= () -> (`+`(args))^2: > PT[Ds]:= proc() local k; sum((args[k] - Sr(args))^2, k = 1 .. nargs)/nargs end proc: > type(PT, 'table'), whattype(eval(PT)), eval(PT); true, table, table([ Ds = (proc() local k; sum((args[ ]k − Sr args( ))^ ,2 k = 1 .. nargs nargs)/ end proc ), Sr = ( ) → `+` argsnargs( ) , H = (( ) → `*` args( ) ),G = (( ) → `+`(args)2 ) ]) > with(PT); PT[G](64, 59, 39, 10, 17, 44), Sr(64, 59, 39, 10, 17, 44), PT[H](64, 59, 39, 10, 17, 44), PT[Ds](64, 59, 39, 10, 17, 44); [Ds, G, H, Sr] 54289, , 16 4302870 , |
Данный простой прием можно применять как для создания пользовательских модулей, так и для организации отложенных определений процедур, что в целом ряде случаев позволяет создавать более эффективные программные Maple-средства [8-14,29,41,103].
Представленное выше описание структурной организации Maple-процедуры иллюстрируется нижеследующими примерами, отражающими основные ее элементы и принципы, что позволяет непосредственно приступать с учетом ранее рассмотренного материала к созданию, на первых порах, относительно несложных пользовательских процедур различного назначения. В основу иллюстративных примеров положим ряд процедур из нашей Библиотеки [41,103,109], содержащих практически полезные приемы программирования в среде Maple-языка. Наряду с этим, примеры представляют процедуры, имеющие непосредственный прикладной интерес при программировании приложений.
В ряде случаев для упрощения разработки Maple-приложений (например, для обеспечения их совместимости относительно релизов) мы вынуждены одновременно открывать несколько релизов в текущем сеансе Windows. В этой связи вполне естественным представляется вопрос программного тестирования наличия релизов Maple в текущем сеансе работы с операционной средой. Данная задача решается процедурой mapleacs, исходный текст и примеры применения которой представляет следующий фрагмент. Успешный вызов процедуры mapleacs() возвращает вложенный список, подсписки которого имеют {2|3} элемента в зависимости от релиза пакета и его клона.
mapleacs := proc() local a b c d q h k p t r v, , , , , , , , , , ; assign(a = interface(warnlevel), b = cat([libname ][1][1 .. 2], "\$$$Avz_Agn$$$"), h = [ ], p = [ ]); assign67 interface(( warnlevel = 0)), com_exe2({`tlist.exe`}), system(cat("tlist > ", Path(b))); do d := readline(b); if d = 0 then break elif Search2 Case(( d), map(cat, {'maplew8', 'wmaple', 'maplew', 'cwmaple9', 'java'}, `.exe`)) ≠ [ ] then h := [op(h), d] end if end do; for k in h do if search(k, "java.exe", 't') then if not search( ,k "[Server " ' ', q ) then p := [op(p), ['Maple9s', [Suffix(k[t + 8 .. -1], " ", ,v del), v][2]]] else p := [op(p), ['Maple9s', cat(``, [Suffix(k[t + 8 .. q − 4], " ", ,v del), v][2]), cat(``, k[q + 1 .. -2])]] end if else search(k, "-" ' ', t ), assign(' 'd = SLD(Red_n(k[1 .. t − 3], " " 2, ), " ")), assign(' 'd = cat(d[3], d[4])); |
assign('r' = k[t + 2 .. -1]), `if`(d[-1] = "6", assign67(' 'r = r[2 .. -2], ' 'q = NULL), [ search(r, "[Server " ' ', t ), assign(' 'r = r[2 .. t − 4], ' 'q = r[t + 1 .. -3]) ]); p := [op(p), map(convert, [d r q, , ], symbol )] end if end do; null interface(( warnlevel = a)), fremove(b), p end proc > mapleacs(); ⇒ [[Maple7, uplib.mws, Server 1], [Maple8, mapleacs.mws, Server 1], [Maple6, MkDir.mws], [Maple9, helpman.mws, Server 1], [Maple10, Lprot.mws, Server 1]] > mapleacs(); ⇒ [[Maple8, mapleacs.mws, Server 1], [Maple8, ProcLib_6_7_8_9.mws, Server 1]] |
Первый элемент такого подсписка определяет релиз и его клон. Например, Maple9s определяет релиз Maple 9 для ядра 'maplew9.exe' (стандартный режим) и Maple9 для ядра пакета 'cwmaple9.exe' (классический режим). Тогда как второй элемент подсписка определяет имя либо полный путь к текущему Maple-документу соответствующего релиза, тогда как третий элемент (при его наличии) подсписка определяет номер сервера. Процедура mapleacs представляется достаточно полезным средством при нескольких одновременно загруженных релизах (могут быть и одинаковыми) в текущий сеанс Windows.
В качестве еще одного примера приведем процедуру `convert/proc`, иллюстрирующую как подход к расширению стандартных средств пакета, так и полезную в практическом отношении. Процедура обеспечивает конвертирование равенства или их списка/множества в процедуру или список/множество. При этом, левые части равенств должны быть функциями вида f(x, y, z, ...), тогда как правые – любыми алгебраическими выражениями. В случае одного равенства вызов процедуры convert(x, `proc`) возвращает тело процедуры, в остальных случаях возвращается NULL-значение с обеспечением требуемой конвертации. Следующий фрагмент представляет исходный текст процедуры и примеры ее применения.
convert/proc := proc(x::{equation, set(equation ), list(equation )}) local a b, ; a := proc( )x if not type(lhs( )x , 'function'('symbol')) then return x else parse cat ""( ( , op 0,( lhs( )x ), ":= proc(", convert([op(lhs(x))], 'string')[2 .. -2], ") ", convert(rhs( )x , 'string'), " end proc;"), 'statement') end if end proc ; if type (x, 'equation') then a x( ) else for b in x do a b( ); NULL end do end if end proc > convert(y(x, y) = sqrt(x^2 + y^2), `proc`); ⇒ proc (x, y) (x^2+y^2)^(1/2) end proc >evalf(y(64, 59)), convert(a = b, `proc`); ⇒ 87.04596487, a = b > convert({v(x) = sin(x), z(h) = cos(h)}, `proc`); eval(v), eval(z); proc(x) sin(x) end proc, proc(h) cos(h) end proc |
Представленная процедура `convert/proc` полезна в целом ряде приложений, например, при работе с дифференциальными уравнениями. В качестве достаточно полезного упражнения рекомендуется рассмотреть организацию этой небольшой процедуры. Ниже будет представлен ряд других процедур, иллюстрирующих те или иные аспекты Mapleязыка. Большое же количество самых разнообразных процедур в исходном виде, использующих немало достаточно полезных, эффективных (а в ряде случаев и нестандартных) приемов программирования в Maple, можно найти в архиве, поставляемом с книгой [41, 103] либо бесплатно скачать архив с Библиотекой с адреса, указанного в [109].
В целом ряде случаев возникает необходимость определения имен, которым в текущем сеансе были просвоены заданные выражения. Задача решается процедурой nvalue.
nvalue := proc(A::anything) local a b c d f k t h, , , , , , , ; assign(a = (b → `if`(search(cat "",( b), "RTABLE_SAVE/"), NULL b, )), d = { }, f = cat(currentdir( ), "/$Art_Kr$" );) if nargs = 0 then {seq(`if`(eval cat( ,( `` k)) = NULL, cat(`` k, ), NULL), k = map(convert, {anames( )}, 'string'))} elif whattype args( ) = 'exprseq' then a := map(convert, {anames( )}, 'string'); {seq(`if`([eval cat( ,( `` k))] = [args], cat(`` k, ), NULL), k = a)} elif type(A `module`, ) then assign('a' = sstr([" " = NULL], convert( ,A 'string')), ' 'b = {anames(`module`)}); for k to nops(b) do (proc( )x save args, f end proc )(b k[ ]); assign ' '( c = sstr([" " = NULL], readbytes(f, ∞, 'TEXT')[1 .. -3])), search(c, ":=", 't'), fremove( )f ; if c[t + 3 .. -1] = a then d := {op(d), cat(``, c[1 .. t − 2])} end if end do; d elif type(eval(A), {function Matrix Vector matrix vector Array array table `module` range, , , , , , , , , }) then map(a, {seq(`if`(convert(eval( )k , list) = convert(eval(A), 'list'), ,k NULL), k = {anames whattype(( eval(A)))})}) else { seq(`if`(evalb(eval(k) = A), k, NULL), k = {anames(whattype(eval(A)))}) } end if end proc > a:=64: b:=64: c:= "ransian64": c9:= "ransian64": d:= table([1=64, 2=59, 3=39]): `): L:= [x, y, z]: t47:= 19.42: assign('h' = `svegal`): t47:= 19.42: t42:= 19.42: R:= a+b*I: Z:= (a1+b1)/(c1+d1): B:= a+b*I: g:= proc() `+`(args)/nargs end proc: r:= x..y: m:= x..y: n:= x..y: Grodno:= NULL: Lasnamae:= NULL:Tallinn:= NULL: Vilnius:= NULL: Moscow:= NULL: f:= cos(x): > map(nvalue, [64, "ransian64", table([1=64, 2=59, 3=39]), svegal, 19.42, [x, y, z], a+b*I, (a1+b1)/(c1+d1), cos(x), proc () `+`(args)/nargs end proc, x..y]), nvalue(); ⇒ [{a,b}, {c,c9}, {d}, {h}, {t47, t42}, {L}, {R, B}, {Z}, {f}, {g}, {r, n, m}], {Lasnamae, Tallinn, Grodno, Vilnius, Moscow} |
Задача решается процедурой, базирующейся на стандартной процедуре anames с использованием некоторых особенностей Maple [41,103,109]. Вызов процедуры nvalue(v) возвращает список глобальных имен, которым в текущем сеансе было присвоено выражение v, тогда как вызов nvalue() возвращает список глобальных имен, которым в текущем сеансе присваивалось NULL-значение. Вызов процедуры возвращает пустое множество, т.е. {}, если в текущем сеансе таких назначений не производилось.