> Grsu:= module() end module: > type(Grsu,`module`), typematch(Grsu,`module`), whattype(eval(Grsu)); true, true, module > type(module() end module, 'module'); Error, unexpected single forward quote > type(module() end module, `module`); ⇒ true > type(module() end module, 'moduledefinition'); ⇒ false > type('module() end module', 'moduledefinition'); ⇒ true |
При этом, четыре последние примера иллюстрируют возможность тестирования не только готового модульного объекта языка, но и его определения. Для этого определение модуля должно указываться невычисленным, в противном случае возвращается false-значение. Данная организация ПМ позволяет использовать их при программировании параметрических алгоритмов, создании пакетных модулей, а также предоставляет возможность использования в Maple-программах Pascal-подобных записей [1] или C+/C-подобных структур [12]. В среде Maple-языка ПМ могут использоваться несколькими способами, из которых следует отметить 4 наиболее широко используемых, а именно: (1) инкапсуляция, (2) создание модулей пакета, (3) моделирование объектов и (4) параметрическое программирование. Детально эти способы с рекомендациями по их применению рассмотрены в книгах [13-14,29-33,41].
Инкапсуляция в значительной степени гарантирует, что уровень абстрагирования процедур и данных ПМ строго соответствует определенному для него интерфейсу. Это позволяет пользователю программировать сложные мобильные и многократно используемые программные системы с хорошо определенными пользовательскими интерфейсами. Более того, обеспечиваются лучшие сопровождение и понимание исходных программных текстов, что является важной характеристикой сложных программных систем. Механизм инкапсуляции обеспечивает возможность определения процедур и данных, видимых извне модуля (т.е. экспортируемых модулем), а также тех, которые составляют внутреннюю сущность самого модуля и недоступны вне модуля, т.е. являются невидимыми вне его.
Пакетные модули (в отличие от ПМ) обеспечивают механизм совместного хранения тематически связанных Maple-процедур. Такие наборы процедур обеспечивают достаточно развитые функциональные средства, ориентированные на конкретные области приложений, например: linalg и LinearAlgebra (линейная алгебра), stats (статистический анализ данных), plots и plottools (графические средства и средства анимации) и др. Большое количество функциональных средств пакета находится именно в его модулях как внутренних, так и внешних. Основной организацией модулей пакета предыдущих релизов являлась табличная структура [12], в которой входами являлись имена процедур, а выходами их определения. ПМ обеспечивают иной механизм организации модулей пакета, который детально рассмотрен в [13,41]. В частности, пакетный модуль LinearAlgebra, обеспечивающий функции линейной алгебры, был имплантирован в среду пакета релизов 6 и выше именно как программный модуль.
Посредством ПМ легко программируются объекты. В программной среде под объектом понимается некоторая программная единица, определяемая как своим состоянием, так и поведением. Вычисления над такими объектами производятся передачей им некоторых управляющих сообщений, на которые они отвечают соответствующими действиями (вычисление, управление, изменение состояния и др.). Параметрические программы пишутся без знания того, как организованы объекты, обработку которых они производят. Параметрическая программа будет работать с любым объектом, имеющим с ней общий интерфейсный протокол, безотносительно того, как объект удовлетворяет этому протоколу. Перечисленные выше 4 аспекта, поддерживаемые механизмом ПМ, определяют самое непосредственное практическое применение Maple-технологии, которая широко иллюстрируется в [32] на ряде практически полезных примеров. Основы механизма ПМ и их использования в Maple-среде иллюстрируются соответствующими примерами.
Организация программного модуля (ПМ) весьма напоминает организацию Maple-процедуры, довольно детально рассмотренной в предыдущей главе. В общем случае организация (структура) программного модуля имеет следующий весьма прозрачный вид: module()
export {экспортируемые переменные} local {локальные переменные} global {глобальные переменные} options {опции модуля} description {описание модуля}
Т Е Л О М О Д У Л Я
end module {:|;}
Определение каждого ПМ начинается с ключевого слова module, за которым следуют круглые скобки "()", подобно тому, как это имеет место для определения процедуры без явно заданных формальных аргументов. Завершается определение модуля закрывающей скобкой `end module`. Все остальные компоненты структуры модуля, находящиеся между module() и `end module`, являются необязательными. При этом, взаимный порядок расположения указанных пяти деклараций в определении модуля несущественен, но все они должны предварять (при их наличии) тело модуля. В результате компиляции модуля устанавливается принятый языком выходной порядок его существующих деклараций, определяемый релизом пакета и вычислительной платформой.
Программный модуль является типом Maple-выражения, которое создается в процессе вычисления определения модуля. Данное определение создается программой синтаксического анализа языка на основе представленной выше структуры модуля. Подобно процедуре определение ПМ может включать ряд необязательных деклараций типа global, local, option и description, имеющих тот же смысл, что и в случае процедуры (при этом, специальные опции могут различаться). ПМ можно представлять себе как собрание тематически связанных переменных. При этом, некоторые из этих переменных доступны для текущего сеанса вне определения модуля после его вычисления. Такие переменные задаются в определении модуля как глобальные (global) или экспортируемые (export). Другие переменные определяются явно (local) или неявно локальными и доступны только в рамках определения модуля в период его реализации. Они используются только алгоритмом, реализуемым предложениями Maple-языка, составляющими тело модуля. Переменные такого типа рекомендуется определять явно посредством local-декларации, иначе это сделает сам Maple-язык на основе принятых неявных правил синтаксического и семантического анализа. Все другие переменные, встречающиеся в определении модуля, относятся к глобальным, параметрам либо к локальным в зависимости от возможных приложений. Глобальные переменные могут быть определены глобальными явно через global-декларации модуля или через неявные правила синтаксического и семантического анализа пакета в период упрощения определения модуля. При этом, следует иметь в виду, что множества глобальных, локальных и экспортируемых переменных модуля не должны попарно пересекаться.
Каждый ПМ имеет тело, которое может быть пустым либо содержать Maple-предложения и/или допустимые Maple-выражения. Данные конструкции тела обеспечивают как необходимые вычисления в процессе реализации модуля, так и определяют начальные значения для его экспортируемых переменных. Множество переменных тела модуля и их соотношений составляет полный лексический набор модуля. Это полностью аналогично определению тела процедуры; в обоих случаях используются одни и те же лексические и неявные правила синтаксического и семантического анализа. При этом, в определении модуля и процедуры допускается широкий произвол в их взаимной вложенности (вложенности типов процедура-процедура, процедура-модуль, модуль-процедура, модуль-модуль). Модуль, содержащийся в другом модуле, называется подмодулем относительно второго. Переменным модуля (локальным, глобальным или экспортируемым) могут присваиваться любые допустимые Maple-выражения, включая процедуры и модули. Простейший модульный объект имеет следующий вид: module() ... end module. Данный модуль не имеет особого смысла, т.к. не экспортирует переменных, не имеет ни локальных, ни глобальных переменных, ни даже тела, производящего какие-либо вычисления. В общем случае ПМ можно рассматривать как результат вызова процедуры, которая возвращает некоторые из своих локальных переменных, определенных в модуле как экспортируемые переменные (или экспорты).
Модули подобно процедурам могут быть как поименованными, так и непоименованными. Непоименованные модули используются, как правило, непосредственно в выражениях, тогда как поименованный модуль поддерживает существенно более развитый механизм работы с ним. Имя модулю можно присваивать двумя способами, а именно:
(1) присвоением определения модуля некоторой переменной; (2) указывая имя между ключевым словом module и "()"-скобками.
Однако, между обоими способами существуют серьезные различия. Поименованный первым способом модуль может многократно переименовываться, создавая свои копии. Тогда как поименованный вторым способом модуль не может быть переименован, а его имя имеет атрибут protected. Если при выполнении поименованного первым способом модуля возникает ошибочная ситуация, то имя модуля идентифицируется как неизвестное (unknown), тогда как для второго случая ошибка привязывается к имени модуля. Это достаточно разумное решение, т.к. модули могут быть непоименованными либо иметь разноименные копии, тогда как второй способ именования делает модуль с фиксированным именем. Следующий простой фрагмент иллюстрирует вышесказанное: