1) op(1, eval(M)) – последовательность экспортов модуля М
2) op(2, eval(M)) – оболочка определения модуля М
3) op(3, eval(M)) – последовательность локальных переменных модуля М Приведем пример на применение функции op относительно простого модуля:
> M:=module() local a,b; export x; a, b:= 64, 59; x:= (y) -> a*y+b*y end module:
> [op(1, eval(M))], op(2, eval(M)), [op(3, eval(M))];
[x], module() local a, b; export x; end module, [a, b]
Следует отметить, что по вызову op(2, eval(M)) возвращается не исходное определение модуля, а скорее его упрощенная копия (оболочка определения модуля) без самого тела. Она используется только для качественной печати модуля.
Дополнительно к ранее рассмотренным, для модулей в релизе 10 введен ряд дополнительных переменных, а именно. Если модуль М экспортирует переменную ModuleApply, то вызов вида M(args) обеспечивает вызов процедуры M:-ModuleApply(args), например:
> M:=module() export ModuleApply; ModuleApply:=()->evalf(`+`(args)/nargs) end module: > M(64, 59, 39, 17, 10, 44); ⇒ 38.8333333300
Если модуль содержит локальную либо экспортируемую переменную ModuleLoad, то определенная ею процедура вызывается, когда модуль читается из содержащей его Mapleбиблиотеки. Если модуль имеет локальную либо экспортируемую ModuleUnload-переменную, то определенная ею процедура вызывается, когда модуль больше недоступен или в случае завершения сеанса с Maple. Две последние переменные являются зеркальными средствами опций load и unload модуля. Если модуль имеет локальную или экспортируемую ModulePrint-переменную, то вместо модуля возвращается результат вызова ModulePrint(). Более детально с данными переменными модуля можно ознакомиться в справочной системе Maple по запросу ?module. Как следует из их описания, данные переменные ничего особо существенного не несут, однако в ряде случаев могут оказаться достаточно полезными.
Механизм вызовов Maple не позволяет использовать динамически генерируемые имена для экспортов модуля. Данную задачу решает процедура dcemod [41,103,109].
dcemod := proc(M::symbol, ex::symbol) local a; global _v63g58; unassign '( _v63g58'), `if`(nargs < 2, ERROR( "quantity of arguments should be more than 1 but has been received <%1>"nargs), , `if`(type(M, `module`), `if`(member(ex, {exports(M)}), assign67(a = "_$EE_", '_v63g58' = NULL), ERROR "<%1> does not export <%2>",( M ex, )), ERROR "<%1> is not a module",( M))), null writeline( (a, cat "`_v63g58`:="( , M, "[", ex, "](" `if`(, 2 < nargs seqstr(, args 3 .. nargs[ ]), ``), "):"))), (proc() close(a); read a; fremove(a) end proc )( ), _v63g58, unassign '( _v63g58') end proc |
Параметризация модулей. В отличие от процедур, программные модули не используют механизма формальных аргументов. Поэтому для использования ПМ в задачах параметрического программирования используется следующая общего характера конструкция:
Proc:= proc(Параметры {::Типы})
Module() export {Переменные};
<ТЕЛО модуля, содержащее Параметры>
end module end proc;
SveGal := proc(a::integer, b::integer) module() export Gal Sv, ; Gal := ( ) → `+` args( )/(a×nargs + b); Sv := ( ) → `*` args( )(a + b^nargs) end module end proc > R95_06:= SveGal(95, 99): R95_06:- Gal(59, 64, 39), R95_06:- Sv(39, 44, 10, 17); 27/64, 291720 |
Параметризация позволяет создавать модули, легко настраиваемые на конкретные условия применения, что обеспечивает их гибкость и мобильность при программировании задач из различных приложений. Вышеприведенный простой фрагмент иллюстрирует применение данной конструкции для параметризации конкретного программного модуля, определенного в процедуре SveGal. Процедура SveGal в качестве формальных аргументов a и b использует параметры вложенного в нее модуля, экспортирующего две функции Gal и Sv. Присвоение вызова данной процедуры с конкретными фактическими аргументами некоторой переменной генерирует поименованный первым способом программный модуль, параметры a и b которого получают конкретные значения. Следовательно, мы получаем программный модуль, настроенный на определенные значения его параметров. В дальнейшем такой модуль используется описанным выше способом. Описанный механизм позволяет производить параметризацию программных модулей, что обеспечивает решение разнообразных задач параметрического программирования в среде пакета Maple.
Представленные в книге [103] процедуры нашей Библиотеки представляют целый ряд весьма полезных средств для работы с программными и пакетными модулями, существенно дополняющими имеющиеся стандартные средства пакета. Вместе с тем, данные средства позволяют детализировать сами модульные Maple-объекты и использовать их особенности для программирования задач с использованием подобных объектов. Так, в главе 3 [41] представлена группа средств, расширяющих возможности пакета Maple релизов 6 – 10 при работе с процедурами и программными модулями. Данные средства поддерживают такие виды обработки как преобразование модулей в процедуры, проверка наличия в файлах некорректных модулей, проверка аргументов процедур и модулей, проверка активности (пригодности к непосредственному использованию) процедуры или модуля, проверка типа модульной таблицы, преобразование файлов входного формата Maple, содержащего модули, преобразование модуля второго типа в первый тип, преобразование файла входного формата Maple в файл внутреннего формата Maple, и наоборот, и т.д. Представленные инструментальные средства обеспечивают набор разнообразных полезных операций с процедурными и модульными объектами Maple. Эти инструментальные средства используются достаточно широко при расширенном программировании различных приложений в Maple и в целом ряде случаев весьма существенно упрощают программирование.
Maple-язык располагает средствами сохранения в файлах процедур и программных модулей с возможностью их последующего чтения как в текущем сеансе, так и после перезагрузки пакета. Для сохранения процедур и программных модулей в файле служит saveпредложение языка, имеющее следующие простые форматы кодирования:
save N1, N2, ..., Nk, <Файл> или save(N1, N2, ..., Nk, <Файл>)
где N1, N2, ..., Nk – последовательность имен сохраняемых объектов и Файл – имя файла или полный путь к нему типа {string, symbol}. Объекты Maple-языка сохраняются в файлах в одном из двух форматов (входном Maple-формате, в терминах DOS эквивалентном формату ASCII, и внутреннем m-формате). Вызов save(N1, N2, ..., Nk, <Файл>) либо выполнение предложения save N1, N2, ..., Nk, <Файл> пишет в заданный файл определения имен N1, N2, ..., Nk в виде последовательности предложений присвоения. При попытке сохранения неопределенного имени А в файл пишется предложение A:= A. Успешный вызов save(…) возвращает NULL-значение, т.е. ничего.
При этом, если в файле определено m-расширение имени, то файл сохраняется во внутреннем m-формате пакета, в противном случае используется входной формат Maple-языка, т. е. (ASCII-формат). Внутренний m-формат используется, чтобы сохранять процедуры, модули и другие объекты в более компактном, простом для чтения пакетом формате. Объекты, сохраненные во внутреннем формате, могут читаться быстрее (прежде всего, при файлах большого объема) чем объекты, сохраненные во входном Maple-формате языка. Следующий весьма простой пример иллюстрирует сохранение целочисленного L-списка из 1000 элементов в файлах обоих форматов, из которого следует, что файл “file” входного формата занимает 5024 байта, тогда как файл “file.m” внутреннего формата только 4026 байтов.
> L:= [k$k=1 .. 1000]: save(L, “C:\temp\file”); save(L, “C:\temp\file.m”);
При этом, для больших сохраняемых объектов эта разница может быть довольно существенной. В случае указания в save-предложении неопределенных Id-идентификаторов, они сохраняются в виде Id := 'Id' с выводом соответствующих предупреждающих сообщений, например:
> save(h, g, s, `D:\Academy\file`);
Warning, unassigned variable `h` in save statement
Warning, unassigned variable `g` in save statement
Warning, unassigned variable `s` in save statement
Именно внутренний m-формат (m-файлы) представляют Maple-объекты, сохраняемые в библиотеках пакета (т.н. Maple-библиотеках) и идентичных с ними по организации. Однако, здесь имеется и одно существенное «но». Файлы с Maple-объектами, сохраненными во входном Maple-формате посредством предложения save, мобильны относительно всех релизов пакета (хотя они и могут быть синтаксически зависимы от релиза), тогда как файлы внутреннего m-формата всегда релизо-зависимы. Попытка чтения m-файла (не соответствующего текущему релизу) посредством предложения read вызывает ошибочную ситуацию, тогда как чтение файла во входном Maple-формате корректно в любом релизе, если определения процедур и модулей, расположенных в нем, не содержат каких-либо релизо-зависимых синтаксических элементов. В нынешних реалиях m-файлы, созданные в среде пакета Maple 6, несовместимы с m-файлами, созданными в среде релизов 7 – 10, тогда как в рамках релизов 7 – 10 имеет место полная совместимость. Обусловлено это изменением соответствия между идентификационными номерами внутренних структур данных и их именами. В дальнейшем мы будем называть файлы, корректно читаемые предложением read, файлами пакета или Maple-файлами.
Следующий простой фрагмент иллюстрирует применение save-предложения для создания файлов обоих указанных форматов (входного и внутреннего):
> Sr:= () -> `+`(args)/nargs: save(Sr, "C:/Temp/Sr"); save(Sr, "C:/Temp/Sr.m");
Sr := proc () options operator, arrow; `+`(args)/nargs end proc; - входной формат
M7R0 - m-формат
I#Srf*6”F$6$%)operatorG%&arrowGF$*&-%”+G6#9”“““9#!”“F$F$F$F$
В данном фрагменте определяется простая Sr-процедура и по save-предложению сохраняется в Sr-файлах во входном ("Sr") и внутреннем ("Sr.m") форматах. Содержимое обеих файлов выводится. На небольшого объема файлах не ощущается преимущества от того либо иного формата, однако при возрастании объема предпочтение внутреннего m-формата становится все ощутимее, позволяя создавать более компактные и быстрочитаемые пакетом файлы данных.