Главная      Учебники - Разные     Лекции (разные) - часть 35

 

Поиск            

 

Программирование и разработка приложений в Maple

 

             

Программирование и разработка приложений в Maple

Министерство образования Республики Беларусь

УЧРЕЖДЕНИЕ ОБРАЗОВАНИЯ

«ГРОДНЕНСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ

ИМЕНИ ЯНКИ КУПАЛЫ»

МЕЖДУНАРОДНАЯ АКАДЕМИЯ НООСФЕРЫ

Балтийское отделение

В.З. АЛАДЬЕВ, В.К. БОЙКО, Е.А. РОВБА

ПРОГРАММИРОВАНИЕ И РАЗРАБОТКА ПРИЛОЖЕНИЙ В MAPLE

М о н о г р а ф и я

Гродно – Таллинн 2007

Аладьев В.З. Программирование и разработка приложений в Maple: монография /

В.3. Аладьев, В.К. Бойко, Е.А. Ровба.– Гродно: ГрГУ; Таллинн: Межд. Акад. Ноосферы, Балт. отд.– 2007, 458 с., ISBN 978-985-417-891-2 (ГрГУ), ISBN 978-9985-9508-2-1 (МАНУР)

Монография вводит в программную среду известного математического пакета Maple, представляющего собой одну из наиболее развитых современных систем компьютерной алгебры. Данное исследование – достаточно детальное введение в среду встроенного Maple-языка программирования, позволяющего пользователю не только четко представить все возможности пакета, но и разрабатывать в его среде сложные приложения для многих разделов техники, математики, физики, химии и других естественнонаучных дисциплин, для решения которых пакет не имеет стандартных средств. При этом, язык Maple может оказаться весьма эффективным средством в системе преподавания указанных дисциплин. Именно в данном направлении он может получить свое не меньшее признание, чем у многочисленных исследователей естественнонаучных дисциплин, использующих математические методы.

Представленный в монографии материал покрывает практически все основные функциональные возможности Maple-языка с иллюстрацией целого ряда как их наиболее массовых приложений при решении широкого круга математических задач, так и наиболее интересных особенностей, позволяющих использовать их нестандартным образом, расширяя тем самым потенциал встроенного Maple-языка; целый ряд рассмотренных приемов может оказаться полезным при формировании эффективной концепции программирования в его среде.

Все это делает книгу полезным пособием по пакету Maple как для студентов, так и для профессионалов из различных фундаментальных и прикладных областей современного естествознания. В свете вышеизложенного Maple можно рассматривать в качестве достаточно хорошо сбалансированной интегрированной среды для выполнения разнообразных вычислений, работы с графическими объектами и для программирования на высокоуровневом процедурном языке, прежде всего, задач, носящих математический характер с акцентом на символьных (алгебраических) вычислениях.

Монография является одним из немногих изданий по программированию в среде пакета Maple, что и определяет ее место среди исследований по программным средствам для ПК, использующих операционную среду Windows. Вместе с тем мобильность пакета позволяет использовать его многими другими популярными платформами. Монография рассчитана на достаточно широкий круг специалистов, использующих в своей профессиональной деятельности ПК для решения задач математического характера, а также на студентов и учащихся, изучающих курс «Основы информатики и вычислительной техники» физико-математических и других естественнонаучных специальностей соответствующих университетов и колледжей.

Настоящая монография представляет собой авторский оригинал-макет мастер-классов по программированию в среде Maple, проведенных в ряде университетов Прибалтики и СНГ. Данное издание может быть полезно пользователям Maple, чьи задачи требуют непосредственного программирования приложений в среде пакета.

Рецензент: доктор физико-математических наук, профессор А.Н. Дудин

ISBN 978-985-417-891-2 (ГрГУ) © Аладьев В.З. (Таллинн)

ISBN 978-9985-9508-2-1 (МАНУР) Бойко В.К., Ровба Е.А. (Гродно), 2007

Maple V, Maple 6, Maple 7, Maple 8, Maple 9, Maple 10 – торговые марки MapleSoft Inc.

Содержание

Предисловие 5

Глава 1. Базовые сведения по Maple-языку пакета 21

1.1. Базовые элементы Maple-языка пакета 23

1.2. Идентификаторы, предложения присвоения и выделения Maple-языка 30

1.3. Средства Maple-языка для определения свойств переменных 42

1.4. Типы числовых и символьных данных Maple-языка пакета 45

1.5. Базовые типы структур данных Maple-языка 51

1.6. Средства тестирования типов данных, структур данных и выражений 80

1.7. Конвертация Maple-выражений из одного типа в другой 89

1.8. Функции математической логики и средства тестирования пакета 93

Глава 2. Базовые управляющие структуры Maple-языка пакета 106

2.1. Предварительные сведения общего характера 106

2.2. Управляющие структуры ветвления Maple-языка (if-предложение) 108

2.3. Циклические управляющие структуры Maple-языка (while_do-предложение) 111

2.4. Специальные типы циклических управляющих структур Maple-языка 115

Глава 3. Организация механизма процедур в Maple-языке пакета 118

3.1. Определения процедур в Maple-языке и их типы 119

3.2. Формальные и фактические аргументы Maple-процедуры 126

3.3. Локальные и глобальные переменные Maple-процедуры 131

3.4. Определяющие параметры и описания Maple-процедур 138

3.5. Механизмы возврата Maple-процедурой результата ее вызова 150

3.6. Средства обработки ошибочных и особых ситуаций в Maple-языке 156

3.7. Расширенные средства Maple-языка для работы с процедурами 167

3.8. Расширение функциональных средств Maple-языка пакета 176

3.9. Иллюстративные примеры оформления Maple-процедур 184

3.10. Элементы отладки Maple-процедур и функций 205

Глава 4. Организация программных модулей в Maple-языке 213

4.1. Вводная часть 213

4.2. Организация программных модулей Maple-языка 216

4.3. Сохранение процедур и программных модулей в файлах 227

Глава 5. Средства Maple-языка для работы с данными и структурами строчного, символьного, списочного, множественного и табличного типов 235

5.1. Средства работы Maple-языка с выражениями строчного и символьного

типов 235

5.2. Средства работы Maple-языка с множествами, списками и таблицами 249

5.3. Алгебраические правила подстановок для символьных вычислений 265

5.4. Средства Maple-языка для обработки алгебраических выражений 273

5.5. Управление форматом вывода результатов вычисления выражений 305

Глава 6. Средства ввода/вывода Maple-языка 311

6.1. Средства Maple-языка для работы с внутренними файлами пакета 311

6.2. Средства Maple-языка для работы с внешними файлами данных 321

6.2.1. Открытие, закрытие и удаление внешних файлов любого типа 327

6.2.2. Средства обработки особой ситуации «конец файла» доступа к файлам 331

6.2.3. Обработка особых и ошибочных ситуаций процедур доступа к файлам 333

6.3. Базовые средства Maple-языка для обеспечения доступа к внешним

файлам данных TEXT-типа 335

6.3.1. Базовые средства доступа к файлам данных на уровне Maple-выражений 340

6.4. Средства Maple-языка для обеспечения доступа к внешним файлам данных

BINARY-типа 343

6.5. Обеспечение форматированного доступа к внешним файлам данных 345

Глава 7. Графические средства Maple-языка пакета 352

7.1. Графическая интерпретация алгебраических выражений и уравнений

в среде Maple-языка 352

7.2. Двухмерное представление функциональных зависимостей и данных

в среде Maple-языка 354

7.3. Трехмерное представление функциональных зависимостей и данных

в среде Maple-языка 373

7.4. Создание графических объектов на основе базовых примитивов 383

Глава 8. Создание и работа с библиотеками пользователя 392

8.1. Классический способ создания Maple-библиотек 393

8.2. Специальные способы создания библиотек пользователя в среде Maple 415

8.3. Создание пакетных модулей пользователя 426

8.4. Статистический анализ Maple-библиотек 436

9. Средства Maple, не рассматриваемые в настоящей книге,

но полезные для приложений 444

Заключение 443

Литература 448

Справка по авторам 453

Предисловие

Системы компьютерной алгебры (СКА) находят все более широкое применение во многих областях науки таких как математика, физика, химия, информатика и т.д., техники, технологии, образовании и т.д. СКА типа Maple, Mathematica, MuPAD, Macsyma, Reduce, Axiom и Magma становятся все более популярными для решения задач преподавания математически ориентированных дисциплин, как в научных исследованиях, так и в промышленности. Данные системы являются мощными инструментами для ученых, инженеров и педагогов. Исследования на основе СКА-технологии, как правило, сочетают алгебраические методы с продвинутыми вычислительными методами. В этом смысле СКА – междисциплинарная область между математикой и информатикой, в которой исследования сосредотачиваются как на разработке алгоритмов для символьных (алгебраических) вычислений и обработки на компьютерах, так и на создании языков программирования и программной среды для реализации подобных алгоритмов и базирующихся на них проблем различного назначения.

В серии наших работ [1-20,22-33,39,41-46,47,49,50,91,103] довольно детально рассмотрены такие математические пакеты как Maple, Reduce, MathCAD, Mathematica. При этом, особое внимание нами уделелялось особенностям каждого из пакетов, его преимуществам, а также недостаткам, эффективным приемам и методам программирования в его среде, созданию набора средств, расширяющих его возможности, а также выработке системы предложений по его дальнейшему развитию. Наш опыт апробации и использования четырех математических пакетов Mathematica, Reduce, Maple, MathCAD в различных математических и физических приложениях позволяет нам рассматривать именно пакеты Maple и Mathematica в качестве бесспорных лидеров (на основе специального обобщенного индекса) среди всех известных на сегодня современных СКА. При этом, мы предпочитаем именно пакет Maple (несмотря на все его недостатки и недоработки) из-за целого ряда преимуществ, среди которых особо следует выделить такие как развитые графические средства, достаточно эффективные средства решения систем дифференциальных уравнений, средства создания графических интерфейсов пользователя, мощная библиотека математических функций, большой набор сопутствующих пакетных модулей для различных приложений, современный встроенный язык программирования (4GL) интерпретирующего типа, интерфейс с рядом других Windows-приложений, перспективная концептуальная поддержка.

Исследователи используют пакет Maple как важный инструмент при решении задач, связанных с их исследованиями. Пакет идеален (по нынешним понятиям) для формулировки, решения и исследования различных математических моделей. Его алгебраические средства существенно расширяют диапазон проблем, которые могут быть решены на качественном уровне. Педагоги в средних школах, колледжах и университетах обновляют традиционные учебные планы, вводя задачи и упражнения, которые используют диалоговую математику и физику Maple. Тогда как студенты могут сконцентрироваться на важных концепциях, а не на утомительных алгебраических вычислениях и преобразованиях. Наконец, инженеры и специалисты в промышленности используют Maple как эффективный инструмент, заменяющий много традиционных ресурсов типа справочников, калькуляторов, редакторов, крупноформатных таблиц и языков программирования. Эти пользователи легко решают весьма широкий диапазон математически ориентированных задач, разрабатывая проекты и объединяя результаты (как числовые, так и графические) их вычислений в профессиональные отчеты довольно высокого качества.

Между тем, наш эксплуатационный опыт в течение 1997–2006 г.г. с пакетом Maple релизов 4 – 10 позволил нам не только оценить его преимущества по сравнению с другими подобными пакетами, но также выявил ряд ошибок и недостатков, устраненных нами. Кроме того, пакет Maple не поддерживал ряд достаточно важных процедур обработки информации, алгебраических и численных вычислений, включая средства доступа к файлам данных. Ввиду сказанного, в процессе работы с пакетом Maple мы создали достаточно много эффективного программного обеспечения (процедуры и программные модули), целым рядом характеристик расширяющих базовые и по выбору возможности пакета. Данное программное обеспечение было организовано в виде Библиотеки, которая является структурно подобной главной библиотеке Maple и обеспечена развитой справочной системой, аналогичной подобной системе пакета Maple и весьма органично с ней связанной. Комментированное описание данной Библиотеки представлено в нашей книге [103]. К ней же прилагается данная Библиотека версии 2.1810, тогда как обновленную версию данной Библиотеки можно бесплатно загружать с любого из адресов, указанных в [109]. Демонстрационная же версия Библиотеки находится по адресу [108].

При этом, программные средства, составляющие Библиотеку, в своем большинстве имеют дело именно с базовой средой Maple, что пролонгирует их актуальность как на текущие релизы, начиная с шестого, так и на последующие релизы пакета. В этой связи здесь уместно обратить внимание на один весьма существенный момент. При достаточно частом объявлении о новой продукции MapleSoft, между тем, уделяет недостаточно внимания устранению имеющихся ошибок и дефектов, переходящих от релиза к релизу. Некоторые из них являются достаточно существенными. Мы отмечали данное обстоятельство в наших книгах неоднократно, этому вопросу посвящен целый ряд замечаний и членов MUG (Maple Users Group). Более того, расширению инструментальных средств основной среды пакета также уделяется недостаточное внимание, что особенно заметно в режиме продвинутого программирования в его среде. Представленная в [103] Библиотека содержит расширения инструментальных средств, прежде всего, базовой среды пакета, что пролонгирует их актуальность и на последующие релизы пакета, а также весьма существенно упрощает программирование целого ряда задач в его среде и обеспечивает более высокий уровень совместимости релизов 6 - 10. Выявленная нами несовместимость пакета как на уровне релизов, так и на уровне базовых операционных платформ – Windows 98SE и ниже, с одной стороны, и Windows ME/2000/XP и выше, с другой стороны, потребовала решения проблемы совместимости и для средств нашей Библиотеки относительно релизов 6 - 10.

В заключение данной преамбулы вкратце изложим (адресуясь, прежде всего, к нашим достаточно многочисленным читателям как настоящим, так и будущим) наше личное мнение по сравнительной оценке пакетов Maple и Mathematica. Как один, так и другой пакеты изобилуют многочисленными ошибками (в целом ряде случаев недопустимыми для систем подобного рода), устранению которых разработчиками как MapleSoft Inc., так и Wolfram Research уделяется сравнительно небольшое внимание. Из коммерческих соображений часто весьма необоснованно выпускаются новые релизы, сохраняющие старые ошибки и привнося в ряде случаев как новые ошибки, так и различного рода экзотические излишества. Данный вопрос неоднократно поднимался как в наших изданиях, так и перед разработчиками. Однако, если разработчики Maple в режиме открытого диалога с пользователями в какой-то мере пытаются решить данную проблему, то Wolfram Research достаточно болезненно воспринимает любую (в подавляющем большинстве обоснованную) критику в свой адрес. При этом, Wolfram Research ведет весьма агрессивную маркетинговую политику, не вполне адекватную качеству ее продукции. Именно это, прежде всего, объясняет ее временные количественные преимущества, которые достаточно быстро уменьшаются. Сравнивая отклики пользователей пакетов Maple и Mathematica, а также в свете нашего многолетнего опыта работы с обоими пакетами, можно вполне однозначно констатировать, что вторые при использовании пакета имеют значительно больше проблем.

Из нашего опыта достаточно глубокого использования и апробирования обоих пакетов отметим, что Maple – существенно более дружелюбная и открытая система, использующая достаточно развитый встроенный язык 4-го поколения интерпретирующего типа, что упрощает освоение пакета пользователю, имеющему опыт современного программирования. Тогда как пакет Mathematica имеет несколько архаичный и не столь изящный язык, в целом ряде отношений отличный от современных языков программирования. Наконец, Maple имеет по ряду показателей более развитые инструментальные средства (например, для решения диффуравнений в частных производных, предоставления используемого алгоритма решения задачи, настройки графического интерфейса пользователя на конкретные приложения и др.), а также весьма широкий спектр бесплатных приложений во многих фундаментальных и прикладных областях современного естествознания.

Пакет Maple воплощает новейшую технологию символьных вычислений, числовых вычислений с произвольной точностью, наличие инновационных Web-компонент, расширяемой технологии пользовательского интерфейса (Maplets), и весьма развитых математических алгоритмов для решения сложных математических задач. В настоящее время пакет использует более 5 миллионов студентов, ученых, исследователей и специалистов из различных областей. Практически каждый ведущий университет и научно-исследовательский институт в мире, включая такие, как MIT, Cambridge, Stanford, Oxford, Waterloo и др., используют пакет для учебных и исследовательских целей. В промышленных целях пакет используется такими ведущими корпорациями как Boeing, Bosch, Canon, Motorola, NASA, Toyota, Hewlett Packard, Sun, Microsystems, Ford, General Electric, Daimler-Chrysler и др.

Резюмируя сказанное (более детальный сравнительный анализ обоих пакетов можно найти в серии наших работ [1-20,22-33,39,41-46,47,49,50,91,103]), начинающему пользователю систем компьютерной алгебры рекомендуем все же пакет Maple, как наиболее перспективное средство в данной области компьютерной математики. Этому существенно способствует и творческий альянс MapleSoft с всемирно известным разработчиком математического ПО – NAG Ltd. И это при том, что последний имеет и свою довольно приличную СКА – AXIOM, являющуюся на сегодня лидером среди СКА на европейском уровне. При этом, Maple постоянно отвоевывает позиции у Mathematica и начинает доминировать в образовании, что весьма существенно с ориентацией на перспективу; используемая Mapleидеология занимает все более существенное место при создании различных электронных материалов математического характера.

Вместе с тем, современное развитие пакета Maple вызывает и ряд серьезных опасений, которые в общих чертах можно определить следующим образом. Качество любого программного обеспечения определяется в соответствии с большим количеством характеристик, среди которых можно отметить такие существенные как: (1) совместимость программных средств «снизу-вверх», (2) устойчивость функционирования относительно операционных платформ, наряду с качественной поддержкой и сопровождением, и т. д. Данным критериям последние релизы пакета Maple, начиная с 7-го, удовлетворяют все меньше и меньше, а именно.

Достаточно существенные ошибки и недоработки (многие из них неоднократно отражались в наших книгах и статьях, а также в ряде других источниках, включая многочисленные форумы по Maple) переходят от релиза к релизу. Отсутствует совместимость релизов пакета Maple «снизу-вверх». О несовместимости релизов Maple мы неоднократно отмечали в своих книгах и статьях. Кое-что для усовершенствования совместимости нами было сделано (в частности, посредством нашей Библиотеки, представленной в [103,109]), однако не все. Тем временем, для Maple релизов 9 и 10 была обнаружена несовместимость уже среди их клонов. Как хорошо известно, Maple 9 и 10 поддерживают два режима – классический (например, для Maple 9 ядро “cwMaple9.exe”и для Maple 10 ядро “cwMaple.exe”) и стандартный (например, для Maple 9 ядро `Maplew9.exe` и для Maple 10 ядро Maplew.exe). Оказывается, что данные клоны несовместимы даже на уровне встроенных функций.

В частности, если в классическом режиме встроенная функция system выполняется корректно, то в стандартном режиме, возвращая код завершения 0, она некорректно выполняет некоторые команды (программы) MS DOS. По этой причине процедуры нашей Библиотеки, использующие данную функцию и отлаженные в Maple релизов 8 и ниже, а также в классическом режиме Maple 9-10, в стандартном режиме Maple 9-10 выполняются некорректно, часто вызывая непредсказуемые ошибочные ситуации. В целях устранения подобных ситуаций нами была определена процедура System, заменяющая указанную стандартную функцию system и устраняющая ее основные недостатки [103]. Естественно, подобные нарушения требований к качественному программному обеспечению не допустимы для программных средств подобного типа и могут вести к нежелательным для Maple последствиям. Более того, нам кажется, что их действие уже начинает сказываться.

Ввиду сказанного, упомянутая наша Библиотека корректно работает с Maple релизов 6 - 8 и Maple 9 - 10 (классический режим), тогда как для Maple 9 - 10 (стандартный режим) некоторые библитечные средства, использующие стандартную функцию system, будут выпоняться некорректно или вызывать ошибочные ситуации, в ряде случаев непредсказуемые. В этой связи заинтересованный читатель в качестве достаточно полезного упражнения имеет хорошую возможность использовать процедуру System для обновления упомянутых процедур Библиотеки на предмет расширения сферы их применимости и на стандартный режим Maple. В целях большей информативности приведем краткую характеристику нашей Библиотеки [103,108,109].

Характеристика нашей библиотеки программных средств . Упоминаемая здесь Библиотека расширяет диапазон и эффективность использования пакета Maple на платформе Windows благодаря содержащимся в ней средствам в трех основных направлениях: (1) устранение ряда основных дефектов и недостатков, (2) расширение возможностей ряда стандартных средств, и (3) пополнение пакета новыми средствами, расширяющими возможности его программной среды, включая средства, повышающие уровень совместимости релизов 6 - 10 пакета, о которой говорилось выше. Основное внимание было уделено дополнительным средствам, созданным нами в процессе использования пакета Maple релизов 410, которые по целому ряду параметров существенно расширяют возможности пакета и облегчают работу с ним. Значительное внимание уделено также средствам, обеспечивающим повышение уровня совместимости пакета релизов 6-10. Большой и всесторонний опыт использования данного программного обеспечения подтвердил его высокие эксплуатационные характеристики при использовании пакета Maple в многочисленных приложениях, потребовавших не только стандартных средств, но и программирования своих собственных, ориентированных на конкретные приложения.

Со всей определенностью следует констатировать, что серия наших книг по Maple [29-33, 39,41-46,91,103], представляющая разработанные нами средства и содержащая предложения по дальнейшему развитию пакета, в значительной степени стимулировала появление таких полезных приложений как пакетные модули ListTools, FileTools, StringTools и LibraryTools. Между тем, и в свете данных приложений средства нашей Библиотеки существенно расширяют возможности пакета, во многих случаях перекрывая средства указанных пакетных модулей. Текущая версия Библиотеки содержит набор средств (более 730 процедур и программных модулей), ориентируемых на следующие основные виды обработки информации и вычисления [103,108,109]:

1. Программные средства общего назначения

2. Программные средства для работы с процедурными и модульными объектами

3. Программные средства для работы с числовыми выражениями

4. Программныее средства для работы со строчными и символьными выражениями

5. Программные средства для работы со списками, множествами и таблицами

6. Программное обеспечение поддержки структур данных специального типа

7. Программное обеспечение для по-битной обработки информации

8. Программные средства, расширяющие графические возможности пакета

9. Расширение и исправление стандартного программного обеспечения Maple

10. Программное обеспечение для работы с файлами данных

10.1. Программное обеспечение общего назначения

10.2. Программное обеспечение для работы с текстовыми файлами

10.3. Программное обеспечение для работы с бинарными файлами

10.4. Программное обеспечение для работы с файлами Maple

10.5. Специальное программное обеспечение для работы с файлами данных

11. Программное обеспечение для решения задач математического анализа

12. Программное обеспечение для решения задач линейной алгебры

12.1. Программное обеспечение общего назначения

12.2. Программное обеспечение для работы с rtable-объектами

13. Программное обеспечение для решения задач простой статистики

13.1. Программное обеспечение для решения задач описательной статистики

13.2. Программное обеспечение для решения задач регрессионного анализа

13.3. Программное обеспечение для проверки статистических гипотез

13.4. Элементы анализа временных (динамических) и вариационных рядов

14. Программное обеспечение для работы с библиотеками пользователя

Основные новации нашей Библиотеки с привязкой к вышеперечисленным разделам, тематически классифицирующим средства Библиотеки, кратко охарактеризованы в Предисловии к нашей книге [103] и на web-страницах www.aladjev.narod.ru/MapleBook.htm и www.exponenta.ru/educat/news/aladjev/book2.asp. Исходя же из нашего многолетнего опыта использования пакета Maple релизов 4 - 10 и опыта наших коллег из университетов и академических институтов России, Эстонии, Белоруссии, Литвы, Латвии, Украины, а также ряда других стран, следует отметить, что многие из средств (или их аналоги) нашей Библиотеки весьма целесообразно включить в стандартные поставки последующих релизов пакета Maple. Соответствующие предложения были нами представлены разработчикам пакета. При этом, можно констатировать, что ряд наших книг по Mapleпроблематике, которые представляют средства, разработанные нами, и содержат полезные рекомендации по дальнейшему развитию пакета, стимулировали появление модулей FileTools, ListTools, LibraryTools и StringTools. Однако, в этом отношении средства, представленные нами, существенно расширяют возможности пакета, во многих случаях превышая таковые из указанных пакетных модулей. В настоящее время они доступны пользователям Maple в виде предлагаемой Библиотеки, функционирующей на платформах Windows и поддерживающей релизы 6-10 пакета. Данная Библиотека прилагается к нашей книге [103], а также может быть получена по адресу [109]. Средства Библиотеки в целом ряде случаев позволяют существенно упрощать программирование различных прикладных задач в среде пакета Maple релизов 6 - 10. Настоящая Библиотека была отмечена в 2004 г. наградой Smart Award от Smart DownLoads Network.

Программные средства, предоставляемые данной Библиотекой, снимают целый ряд вопросов, возникших в дискуссиях членов группы пользователей Maple (MUG) на целом ряде форумов по Maple, и существенно расширяют функциональные возможности пакета, облегчая его использование и расширяя сферу приложений. Библиотека предназначена для достаточно широкой аудитории ученых, специалистов, преподавателей, аспирантов и студентов естественно научных специальностей, которые в своей профессиональной работе используют пакет Maple релизов 6 - 10 на платформе Windows. Библиотека содержит оптимально разработанное, интуитивное программное обеспечение (набор процедур и программных модулей), которое достаточно хорошо дополняет уже доступное программное обеспечение пакета с ориентацией на самый широкий круг пользователей, в целом ряде случаев расширяя сферу применения пакета и его эффективность.

Библиотека структурно подобна главной библиотеке Maple, снабжена развитой справочной системой по средствам, расположенным в ней, и логически связана с главной библиотекой пакета, обеспечивая доступ к средствам, содержащимся в ней, подобно стандартным средствам пакета. Простое руководство описывает установку Библиотеки при наличии на компьютере с платформой Windows инсталлированного пакета Maple релизов 6, 7, 8, 9, 9.5 и/или 10. Для полной установки данной Библиотеки требуется 17 МB свободного пространства на жестком диске. Сопутствующие материалы содержат немало дополнительной полезной информации, которая по тем либо иным причинам не была включена в основной текст книги.

Все исходные тексты средств, содержащихся в Библиотеке, доступны пользователю, что позволяет использовать их в качестве хорошего иллюстративного материала при освоении программирования в среде пакета. В них представлено использование различных полезных методов и приемов программирования, включая и нестандартные, которые во многих случаях позволяют существенно упрощать программирование задач в среде пакета Maple, делая их более прозрачными и изящными с математической точки зрения. При этом, следует отметить, что в ряде случаев тексты процедур оставляют достаточно широкое поле для их оптимизации (в нынешнем виде большинство из них по эффективности, практически, не уступает оптимальным), однако это было сделано вполне умышленно с целью иллюстрации ряда особенностей и возможностей языка программирования Maple. Это будет весьма полезно при освоении практического программирования в среде пакета Maple.

Следует отметить, что поставляемые с Библиотекой файлы “ProcUser.txt” (для Maple 6–9) и “ProcUser10.txt” (для Maple 9.5/10), содержащие исходные тексты программных средств, составляющих Библиотеку, а также полный набор mws-файлов с help-страницами, составляющими справочную базу Библиотеки, наряду с большим набором различного назначения примеров, позволяют достаточно легко адаптировать Библиотеку на базовые платформы, отличные от Windows-платформы. Более того, в виду наследования встроенными языками математических пакетов целого ряда общих черт, имеется хорошая возможность адаптации ряда процедур нашей Maple-библиотеки к программной среде других пакетов. В частности, целый ряд процедур Библиотеки достаточно легко был нами адаптирован к среде пакета Mathematica и некоторых других математических пакетов, тогда как предложенный нами метод “дисковых транзитов”, существенно расширяющий возможности программирования, эффективен не только для математических пакетов. При этом, следует иметь в виду, что исходные тексты программных средств, представленные в книге [103], и их представления в нашей Библиотеке (будучи функционально эквивалентными) могут в определенной степени различаться, что обусловлено широким использованием Библиотеки также и в учебных целях.

Наш и опыт наших коллег показывает, что использование Библиотеки в целом ряде случаев существенно расширяет возможности пакета Maple релизов 6 – 10 и последующих релизов, упрощая программирование различных прикладных задач в его среде. Данная Библиотека представит особый интерес прежде всего для тех, кто использует пакет Maple не только как высоко интеллектуальный калькулятор, но также и как среду программирования различных задач математического характера в своей профессиональной деятельности.

Библиотека в совокупности с главной Maple-библиотекой обладает полнотой в том отношении, что любое ее средство использует или средства главной библиотеки и/или средства самой Библиотеки. В этом плане она полностью самодостаточна. Ряд часто используемых процедур Библиотеки, ориентированных на массовое применение при программировании различных приложений, оптимизирован. Тогда как многие, обладая функциональной полнотой, на которую они и были ориентированы, между тем, в полной мере не оптимизированы, что предоставляет пользователю (прежде всего серьезно осваивающему программирование в Maple) достаточно широкое поле для его творчества как по оптимизиции процедуры, так и по созданию собственных аналогов, постоянно контролируя себя готовым, отлаженным и корректно функционирующим прообразом. Более того, используемые в процедурах полезные, эффективные (а в целом ряде случаев и нестандартные) приемы программирования позволяют более глубоко и за более короткий срок освоить программную среду пакета. Использование же во многих процедурах обработки особых и ошибочных ситуаций дает возможность акцентировать уже на ранней стадии внимание на таких важных компонентах создания программных средств, как их надежность, мобильность и ошибкоустойчивость. Наконец, работая с Библиотекой, пользователь не только имеет прекрасную возможность освоить многие из ее средств для своей текущей и последующей работы с пакетом, но и проникается концепцией эффективной организации своих собственных Maple-библиотек, содержащих средства, обеспечивающие его профессиональные интересы и потребности. Есть надежда, что и читатель книги найдет среди средств Библиотеки полезные для своего творчества. В предлагаемой книге рассматриваются принципы работы в программной среде Maple, основу которого составляет язык программирования Maple, что является непосредственным продолжением наших книг упомянутой серии [1-20,22-33,39,41-46,47,49,50,91,103], в которых обсуждаются ПС того же типа, что и рассматриваемое в настоящей книге. Придерживаясь выработанных методики и методологии подготовки указанных книг, наш подход делает основной акцент на изложении материала на основе развернутой апробации описываемой предметной области при решении задач как сугубо теоретических, так и прикладных. В предлагаемой книге представлены базовые сведения по языку программирования Maple – составляющему основу программной среды пакета, в которой пользователь имеет достаточно широкие возможности по разработке собственных Mapleприложений.

Пакет Maple способен решать большое число, прежде всего, математически ориентированных задач вообще без программирования в общепринятом смысле. Вполне можно ограничиться лишь описанием алгоритма решения своей задачи, разбитого на отдельные последовательные этапы, для которых Maple имеет уже готовые решения. При этом, пакет Maple располагает большим набором процедур и функций, непосредственно решающих совсем не тривиальные задачи как то интегрирование, дифференциальные уравнения и др. О многочисленных приложениях Maple в виде т.н. пакетов и говорить не приходится. Тем не менее, это вовсе не означает, что Maple не предполагает программирования. Имея собственный довольно развитый язык программирования (в дальнейшем Maple-язык), пакет позволяет программировать в своей среде самые разнообразные задачи из различных приложений. Несколько поясним данный аспект, которому в отечественной литературе уделяется недостаточно внимания.

Относительно проблематики, рассматриваемой в настоящей книге, вполне уместно сделать несколько существенных замечаний. К большому сожалению, у многих пользователей современных математических пакетов, включая и системы компьютерной алгебры – основной темы нашей книги – бытует достаточно распространенное мнение, что использование подобных средств не требует знания программирования, ибо все, что нужно для решения их задач, якобы уже имеется в этих средствах и задача сводится лишь к выбору нужного средства (процедуры, модуля, функции и т.д.). Подобный подход к данным средствам носит в значительной степени дилетантский характер, причины которого достаточно детально рассмотрены в нашей книге [103].

Двухуровневая лингвистическая поддержка пакета Maple обеспечивается такими языками программирования как С и Maple. В ряде публикаций встречается иная (не вполне обоснованная на наш взгляд) классификацию, когда выделялись три языка – реализации, входной и программирования. Суть же состоит в следующем. Действительно, ядро пакета Maple содержит набор высокоэффективных программ, написанных на С-языке. Более того, библиотека функций доступа к компонентам файловой системы компьютера непосредственно заимствована из соответствующей библиотеки С. По нашим оценкам доля программных средств пакета, написанных на С, не превышает 15%. Остальная масса программных средств пакета (функции, процедуры, модули), находящихся в различных библиотеках, написана на собственном Maple-языке. Уже ввиду сказанного весьма сомнительным выглядит утверждение, что С – язык реализации, а Maple – входной язык или язык программирования. Так как Maple-язык использован для реализации важнейших базовых средств пакета, то языками реализации являются и С, и Maple. При этом, с определенными допущениями можно говорить о входном Maple-языке и языке программирования Maple. В основе своей входной Maple-язык пакета базируется на встроенном языке программирования, являясь его подмножеством, обеспечивающим интерактивный режим работы с пакетом. Именно на входном Maple-языке в этом режиме пишутся и выполняются Maple-документы.

Входной язык ориентирован на решение математически ориентированных задач практически любой сложности в интерактивном режиме. Он обеспечивает диалог пользователя со своей вычислительной компонентой (вычислителем), принимая запросы пользователя на обработку данных с их последующей обработкой и возвратом результатов в символьном, числовом и/или графическом видах. Входной язык является языком интерпретирующего типа и идеологически подобен языкам этого типа. Он располагает большим числом математических и графических функций и процедур, и другими средствами из обширных библиотек пакета. Интерактивный характер языка позволяет легко реализовывать на его основе интуитивный принцип решения своих задач, при котором ход решения можно пошагово верифицировать, получая в конце концов требуемое решение. Уже введя первые предложения в текущий сеанс пакета, вы начинаете работать со входным Maple-языком. В настоящей же книге рассматривается наиболее полная лингвистическая компонента пакета – встроенный Maple-язык программирования (или просто Maple-язык). Вместе с тем, все примеры применения представленных в книге средств являются типичными предложениями входного Maple-языка пакета.

Среда программирования пакета обеспечивается встроенным Maple-языком, являющимся функционально полным процедурным языком программирования четвертого поколения (4GL). Он ориентирован, прежде всего, на эффективную реализацию как системных, так и задач пользователя из различных математически-ориентированных областей, расширение сферы приложений пакета, создание библиотек программных средств и т. д. Синтаксис Maple-языка наследует многие черты таких известных языков программирования как С, FORTRAN, BASIC и Pascal. Поэтому пользователю, в той либо иной мере знакомому как с этими языками, так и с программированием вообще, не составит особого труда освоить и Maple-язык.

Maple-язык имеет вполне традиционные стредства структурирования программ, включает в себя все команды и функции входного языка, ему доступны все специальные операторы и функции пакета. Многие из них являются достаточно серьезными программами, например, алгебраическое дифференцирование и интегрирование, задачи линейной алгебры, графика и анимация сложных трехмерных объектов и т. д. Являясь проблемно-ориентированным языком программирования, Maple-язык характеризуется довольно развитыми средствами для описания задач математического характера, возникающих в различных прикладных областях. В соответствии с языками данного класса структуры управляющей логики и данных Maple-языка в существенной мере отражают характеристику средств, прежде всего, именно для математических приложений. Наследуя многие черты С-языка, на котором написан компилятор интерпретирующего типа, Maple-язык позволяет обеспечивать как численные вычисления с любой точностью, так и символьные вычисления, поддерживая все основные операции традиционной математики. Однако, здесь следует привести одно весьма существенное пояснение.

Хорошо известно, что далеко не все задачи поддаются решению в аналитическом виде и приходится применять численные методы. Несмотря на то, что Maple-язык позволяет решать и такие задачи, его программы будут выполняться медленнее, чем созданные в среде языков компилирующего типа. Так что решение задач, требующих большого объема численных вычислений, в среде Maple может быть весьма неэффективно. Именно поэтому пакет предоставляет как средства перекодировки программ с Maple-языка на C, Java, FORTRAN, MatLab и VisualBasic, так и поддержку достаточно эффективного интерфейса с известным пакетом MatLab.

Средства Maple-языка позволяют пользователю работать в среде пакета в двух режимах: (1) на основе функциональных средств языка с использованием правил оформления и работы с Maple-документом предоставляется возможность на интерактивном уровне формировать и выполнять требуемый алгоритм вашей задачи без сколько-нибудь серьезного знания даже основ программирования, а подобно конструктору собирать из готовых функциональных компонент входного языка на основе его синтаксиса требуемый вам алгоритм, включая его выполнение, отображение результатов на экране (в обычном и/или графическом виде), в файле и в твердой копии, и (2) использовать всю мощь Mapleязыка для создания развитых систем конкретного назначения, так и средств, расширяющих собственно саму среду Maple, чьи возможности определяются только вашими собственными умением и навыками. При этом, первоначальное освоение языка не предполагает предварительного серьезного знакомства с основами программирования, хотя их знание и весьма предпочтительно.

Программирование в среде Maple-языка в большинстве случаев не требует какого-либо особого программистского навыка (хотя его наличие и весьма нелишне), т. к. в отличие от других языков универсального назначения и многих проблемно-ориентированных язык Maple включает большое число математически ориентированных функций и процедур, позволяя только одним вызовом решать достаточно сложные самостоятельные задачи, например: решать системы дифференциальных или алгебраических уравнений, находить минимакс выражения, вычислять производные и интегралы, выводить графики сложных функций и т.д. Интерактивность языка обеспечивает простоту его освоения и удобство редактирования и отладки прикладных Maple-документов и программ. Реальная же мощь Maple-языка обеспечивается не только его управляющими структурами и структурами данных, но и всем богатством его функциональных (встроенных, библиотечных, модульных) и прикладных (Maple-документов) сред-тв, созданных к настоящему времени пользователями из различных прикладных областей, прежде всего, математических. Важнейшим преимуществом Maple является открытость его архитектуры, что позволило в кратчайшие сроки создать широким кругом пользователей из многих областей науки, образования, техники и т.д. обширных наборов процедур и модулей, которые значительно расширили как его возможности, так и сферу приложений. К их числу можно с полным основанием отнести и представленную в [103] Библиотеку, содержащую более 730 средств, дополняющих средства пакета, устраняющих ряд его недоработок, расширяющих ряд его стандартных средств и повышающих уровень совместимости релизов пакета. Представленные в [103] средства используются достаточно широко как при работе с пакетом Maple в интерактивном режиме, так и при программировании различных задач в его среде. Они представляют несомненный интерес при программировании различных задач в среде Maple, как упрощая собственно сам процесс программирования, так и делая его более прозрачным с формальной точки зрения.

Таким образом, пакет Maple – не просто высоко интеллектуальный калькулятор, способный аналитически решать многие задачи, а легко обучаемая система, вклад в обучение которой вносят как сами разработчики пакета, так и его многочисленные пользователи. Очевидно, как бы ни была совершенна система, всегда найдется много специальных задач, которые оказались за пределами интересов ее разработчиков. Освоив относительно простой, но весьма эффективный Maple-язык, пользователь может изменять уже существующие процедуры либо расширять пакет новыми, ориентированными на решение нужных ему задач. Эти процедуры можно включать в одну или несколько пользовательских библиотек, снабдить справочной базой, логически сцепить с главной библиотекой пакета, так что их средства на логическом уровне будут неотличимы от стандартных средств пакета. Именно таким образом и организована наша Библиотека [103,108,109]. И последнее, Maple-язык – наименее подверженная изменениям компонента пакета, поэтому ее освоение позволит вам весьма существенно пролонгировать эффективное использование пакета для решения тех задач, которые прямо не поддерживаются пакетом.

Поскольку Maple-язык является одновременно и языком реализации пакета, то его освоение и практическое программирование в его среде позволят не только существенно повысить ваш уровень использования предоставляемых пакетом возможностей (уровень владения пакетом), но и глубже понять как идеологию, так и внутреннюю кухню самого пакета. Учитывая же ведущие позиции Maple в современной компьютерной алгебре и во многом распространенную его идеологию в этой области, вы получаете прекрасную и пролонгированную возможность весьма эффективного использования подобных ему средств для своей профессиональной деятельности, прежде всего, в математике, физике, информатике и др.

В настоящей книге рассматриваются основы Maple-языка программирования в предположении, что читатель в определенной мере имеет представление о работе в Windowsсреде и с самим пакетом в режиме его главного меню (GUI) в пределах, например, книг [8-14,29-33,39,42-46,54-62,103] и подобных им изданий. Представленные ниже сведения по Maple-языку в значительной мере обеспечат вас тем необходимым минимумом знаний, который позволит знакомиться со средствами главной библиотеки пакета и нашей Библиотеки [103]. Данная работа, в свою очередь, даст вам определенный навык программирования, а также обеспечит вас набором полезных приемов и методов программирования (включая и нестандартные) в среде Maple-языка наряду с практически полезными средствами, упрошающими решение многих прикладных задач, составляющих ваши приложения. Кратко охарактеризуем содержание предлагаемой книги.

Первая глава книги представляет базовые сведения по Maple-языку, включая рассмотрение таких вопросов, как базовые элементы языка, идентификаторы, предложения присвоения и выделения, средства языка для определения свойств переменных, типы числовых и символьных данных, базовые типы структур данных, средства тестирования типов данных, структур данных и выражений, конвертация выражений из одного типа в другой и др.

Вторая глава представляет базовые управляющие структуры Maple-языка: ветвления (ifпредложение), организации циклических вычислений (while_do-предложение), а также специальные типы циклических управляющих структур Maple-языка.

Третья глава достаточно детально рассматривает одну из ключевых образующих модульного программирования – процедуры и средства их организации в среде Maple-языка. В ней представлены как базовые так и расширенные средства наряду с вопросами расширения функциональных средств Maple-языка. Довольно подробно рассмотрены такие вопросы как определения процедур и их типы, формальные и фактические аргументы, локальные и глобальные переменные, определяющие параметры и описания процедур, механизмы возврата результата вызова процедуры, средства обработки ошибочных ситуаций, расширенные средства для работы с процедурами, расширение функциональных средств Maple-языка и др.

Четвертая глава рассматривает вопросы организация программных модулей Maple-языка и сохранения процедур и программных модулей в файлах входного и внутреннего форматов.

Пятая глава представляет средства Maple-языка для работы с данными и структурами символьного, строчного, списочного, множественного и табличного типов, в том числе и средства работы с выражениями строчного и символьного типов, множествами, списками и таблицами. Рассматриваются алгебраические правила подстановок для символьных вычислений и др.

Шестая глава представляет средства Maple-языка для обеспечения доступа к внешним файлам данных. Полнота изложения в значительной степени перекрывает как поставляемую с пакетом документацию, так и другие издания. С целью развития системы доступа к файловой системе компьютера нами был разработан целый ряд эффективных средств, представленных как в настоящей главе, так и в нашей Библиотеке [103,108,109].

Седьмая глава представляет основные средства Maple-языка для обеспечения работы с графическими объектами. Пакет располагает развитым набором функций различных уровней, обеспечивающих формирование графических структур данных и вывод соответствующих им графических объектов как в двух, так и в трех измерениях, а также в широком диапазоне систем координат. Здесь же рассматриваются и такие вопросы, как расширение функциональных возможностей графических средств пакета.

Восьмая заключительная глава знакомит с вопросами создания и ведения библиотек пользователя. Рассмотрены такие вопросы как классический способ создания Maple-библиотек, специальные способы создания библиотек в среде Maple, создание пакетных модулей и др.

Подобно другим ПС, ориентированным на решение задач математического характера, пакет Maple располагает достаточно развитыми средствами, обеспечивающими решение широкого круга задач математического анализа и линейной алгебры. Эти две дисциплины представляют собой основную базовую компоненту современного математического образования естественно-научных специальностей и особых пояснений не требуют. Вместе с тем, следует сразу же отметить, что в дальнейшем вопросы решения задач математического анализа, линейной алгебры и других математически-ориентированных дисциплин в среде Maple (учитывая специфику настоящей книги) не рассматриваются. В принципе, сложности при освоении средств данной группы особой возникать не должно и они достаточно хорошо изложены в многочисленной литературе как зарубежной, так и отечественной [8-14,54-62]. В книгах [41-43,103] мы представили средства, функционирующие в среде пакета релизов 6–10 и ориентированные, прежде всего, на решение задач математического анализа. Там же представлены средства, расширяющие возможности пакета при решении задач линейной алгебры. Данные средства классифицируются относительно стандартных средств двух основных пакетных модулей, ориентированных, прежде всего, на задачи линейной алгебры, а именно: традиционный Maple-модуль linalg и имплантированный модуль LinearAlgebra фирмы NAG. Представлены дополнительные средства, обеспечивающие решение ряда массовых задач линейной алгебры.

Более того, не рассматриваются детально здесь и такие важные средства пакета как графические и обеспечения доступа к файлам данных. Мотивировано данное решение тем, что эти средства довольно хорошо изложены в наших предыдущих книгах, книгах других авторов и в справочной системе по пакету. В частности, в книге [12] достаточно детально рассмотрена система ввода/вывода пакета, которая не претерпела каких-либо существенных изменений вот уже на протяжении 6 релизов. Основной акцент здесь сделан на тех аспектах средств доступа, которые не нашли отражения в имеющихся на сегодня изданиях по Maple-языку, включая и фирменную литературу, стандартно поставляемую с пакетом. В целом же система ввода/вывода пакета может быть охарактеризована следующим образом.

Будучи языком программирования в среде пакета компьютерной алгебры, ориентированного, прежде всего, на задачи алгебраических вычислений, Maple-язык располагает относительно ограниченными возможностями при работе с данными, которые расположены во внешней памяти компьютера. Более того, в этом отношении Maple-язык существенно уступает таким традиционным языкам программирования как C, Cobol, PL/1, FORTRAN, Pascal, Ada, Basic и т. д. В то же самое время Maple-язык, ориентированный, прежде всего, на решение задач математического характера, предоставляет тот набор средств для доступа к файлам данных, которые могут вполне удовлетворить довольно широкую аудиторию пользователей пакета и его физических и математических приложений. В наших книгах [41-42,103] представлен целый ряд дополнительных средств доступа к файлам данных, существенно расширяющих пакет в данном направлении. Многие из них упрощают программирование целого ряда задач, имеющих дело с доступом к файлам данных различной организации, содержания и назначения.

Со всей определенностью можно констатировать, что новые пакетные модули FileTools и LibraryTools были вдохновлены рядом наших книг по Maple-тематике [29-33,39,42-46], с которыми разработчики пакета были ознакомлены. Однако, наш набор подобных процедур является значительно более представительным и они сосредоточены на более широком практическом использовании при решении задач, имеющих дело с обработкой файлов данных. Более того, нам не известно более обстоятельное рассмотрение системы доступа к файлам, обеспечиваемой пакетом, чем в наших предыдущих книгах [8-14, 29-33,39,41-43,45,46,103]. Именно в этом отношении они рекомендуются читателю, имеющему дело с подобными задачами.

В определенной мере вышесказанное можно отнести и к графическим средствам пакета. Так, в вышеуказанных книгах представлены средства, расширяющие стандартный набор графического инструментария пакета Maple релизов 6 – 10. Предлагаемые средства являются довольно полезными процедурами, представляющими определенный прикладной интерес, что подтверждает эффективность их использования при решении целого ряда прикладных задач с использованием графических объектов. В частности, на сегодня, стандартные средства пакета, ориентированные на работу с анимируемыми графическими объектами, имеют целый ряд ограничений на динамическое обновление их характеристик. Между тем, многие задачи, имеющие дело с графическими анимируемыми объектами, предполагают возможность динамического обновления их характеристик. Представлен ряд средств в этом направлении. Заинтересованный читатель отсылается к книгам [8-14,32,78,84,86,88,55,59-62], а также к [91] с адресом сайта (Локальная копия сайта), с которого можно бесплатно загружать некоторые из наших предыдущих книг по данной тематике.

Настоящая книга носит вводный характер, представляя собой основы языка программирования Maple, что позволит читателю, не знакомому в достаточной мере с программной средой пакета, более осознанно воспринимать информацию по программным средствам пакета. По этой причине она лишь скелетно представляет Maple-язык пакета, не отвлекаясь на его тонкости, особенности и другие частности в свете основной цели настоящей книги. Следует отметить, что к сфере Maple-языка пакета мы отнесли не только синтаксис, семантику, структуры данных, управляющие структуры и т. д., но и функции и процедуры различных уровней, которые доступны собственно Maple-языку в процессе программирования в его среде программ и их последующего выполнения.

Читатель, имеющий опыт программирования в среде Maple, может вполне (быть может, за редким исключением) безболезненно опустить данный материал. Тогда как начинающий на этом поприще получит необходимый минимум сведений по Maple-языку, который позволит значительно проще совершенствоваться в этом направлении. Для более глубокого ознакомления с Maple-языком нами приведены полезные ссылки как на отечественные, так и на зарубежные издания. Более того, читатель, заинтересованный в освоении программной среды пакета Maple, может найти немало полезной информации как в справочной системе по пакету, литературе, поставляемой с пакетом, так и на ведущих Web-серверах и форумах, посвященных Maple-тематике. В век почти массовой компьютеризации и интернетизации лишь ленивый может аппелировать к недостатку нужной информации по какому бы то ни было разделу современного знания.

По ходу настоящей книги будет приведено немало ссылок на наши издания, что следует совершенно непредвзято трактовать и вот почему. Прежде всего, в настояшей книге рассматриваются вопросы программирования в среде пакета Maple, которое является основой разработки в его среде эффективного программного обеспечения, ориентированного на те или иные приложения. В отечественной же литературе других изданий, рассматривающих столь скрупулезно данную проблематику (как, впрочем, и в англоязычной), на мой взгляд, просто нет (и это не только мое субъективное мнение). Наконец, вполне это уместно и в том потребительском контексте, что основная масса цитируемых наших изданий по Maple-тематике может быть совершенно бесплатно получена с указанного в [91] Internet-адреса Гродненского университета. Конечно, там представлены издания, ориентированные на Maple 5 – 7, однако ввиду полной приемственности по основным аспектам программной среды пакета они (с некоторыми оговорками) вполне пригодны и для последующих релизов пакета.

Заключение представляет краткий экскурс в историю создания нашей Maple-библиотеки, которая будет неоднократно цитироваться в настоящей книге. Здесь лишь хочу предупредить, что во многом она создавалась еще в Maple релизов 5–7, когда нас не совсем устраивали как целый ряд базовых средств пакета, так и возможности пакета. Поэтому создавались средства как заполняющие подобный ваккуум, так и устраняющие замеченные недостатки пакета. С появлением новых релизов базовые средства пакета и его средства в целом расширялись и улучшались, поэтому ряд средств библиотеки могут, на первый взгляд, показаться устаревшими (obsolete), однако они все еще представляют интерес с учебной точки зрения, предлагая немало эффективных и полезных приемов программирования, не утративших своей актуальности и поныне. Многие же из библиотечных средств и на сегодня не имеют аналогов. В последние пару лет Maple-тематике в ее прямом смысле мы уделяли относительно немного внимания, используя, между тем, пакет в целом ряде физико-математических и инженерных приложений, а также в преподавании математических курсов для докторантов, различных курсов и мастер-классов по Maple. В будущем данная тематика не планируется в числе наших активных интересов, поэтому наши публикации в этом направлении будут носить в значительной мере спорадический характер. Вот и данное издание, посвященное вопросам сугубо программирования в Maple, также в значительной мере специально не планировалось, поэтому оно не было подвержено тщательному редактированию. Однако смеем надеяться, что и в таком виде оно представит определенный интерес как для начинающих, так и даже имеющих определенный опыт программирования в среде Maple, да и не только.

Еще на одном важном моменте следует акцентировать внимание. Разработка Maple-приложений базируется на программной среде пакета. Здесь можно выделить ряд уровней разработки, которые представляются нам под следующим углом, а именно:

(1) Maple-документы, решающие отдельные прикладные задачи (типичным примером такого подхода может служить набор Maple-документов, решающих типичные инженерно-физические задачи методом конечных элементов [11,13,14,43,110]);

(2) отдельные процедуры, программные модули либо их наборы, сохраненные в файлах и решающие конкретные прикладные задачи (типичными примерами служат средства, созданные многочисленными пользователями пакета, а также пакетные модули);

(3) библиотеки процедур и модулей, организованные аналогично главной библиотеке пакета и решающие достаточно широкий круг как задач из различных приложений, так и системных, расширяющих функциональную среду самого пакета (типичным примером служит наша Библиотека [41,103,108,109]).

Естественно, представленная классификация в значительной степени субъективна, однако при отсутствии иной и она может служить в качестве отправной точки. Средства первого уровня представляют собой законченные Maple-документы, т.е. {mws, mw}-файлы, решающие конкретные прикладные залачи из различных приложений. Они могут содержать описательную часть, постановку задачи, алгоритм, описанный входным Mapleязыком, результаты выполнения документа и другую необходимую информацию. Как правило, сложная задача либо комплекс задач реализуются набором {mws, mw}-файлов, сохраняемых в отдельном каталоге и доступных для выполнения путем загрузки в среду Maple соответствующего релиза либо поочередно, либо в требуемом порядке. Для создания таких документов вполне можно обходиться лишь входным Maple-языком, а не полным набором средств, предоставляемых его расширением – встроенным языком программирования. В целом, данный уровень допускает и диспетчирование документов, например, соедующим образом.

У традиционного Maple-документа после его отладки удаляются все Output-параграфы и он сохраняется в текстовом файле в рамках только своих Input-параграфов. Создается Maple-документ, обеспечивающий загрузку в нужном порядке в текущий сеанс по readпредложению созданные описанным образом txt-файлы, обеспечивая необходимое диспетчирование документов.

Второй уровень представляет отдельные процедуры либо программные модули, либо их наборы, решающие конкретные как прикладные, так и системные задачи. Данные средства могут сохраняться в файлах, как входного, так и внутреннего Maple-формата. Для них могут создаваться и библиотечные организации, например, архивного типа. В этом случае пользователь уже в значительной мере использует встроенный Maple-язык.

Наконец, третий уровень характеризуется использованием для разработки достаточно сложных приложений в полной мере средств встроенного Maple-языка, созданием библиотек, подобных главной библиотеке пакета, наряду с пакетными модулями, ориентированных как на относительно узкие области приложений, так и на самое массовое использование при разработке приложений в среде пакета. Типичным примером средств третьего уровня является и наша Библиотека, рассматриваемая в настоящей книге.

Литература содержит интересные и полезные как зарубежные, так и отечественные источники по Maple-тематике, которые являются вполне доступными. При этом многие из наших изданий (точнее их авторские оригинал-макеты) можно бесплатно получать с указанного в [91] Internet-адреса Гродненского государственного университета им. Я. Купалы. Там же можно найти и массу полезных примеров по Maple-тематике.

В дальнейшем изложении нами используются следующие основные соглашения и обозначения, а также основные используемые в тексте сокращения:

- ПС – программное средство;

- ПО – программное обеспечение;

- ПК – персональный компьютер;

- альтернативные элементы разделяются символом “|“ и заключаются в фигур- ные скобки, например, {A|B} – A или B;

- смысл конструкции указывается в угловых скобках “< ... >“;

- под выражением “по умолчанию” понимается значение той или иной характе- ристики операционной среды или пакета, которое используется, если пользова- телем не определено для нее иное значение;

- под «регистро-зависимым (независимым)» режимом будем понимать такой режим поиска, сравнения и т.д. символов и строк, когда принимаются (не принимаются) в расчет различия между одинаковыми буквами верхнего и нижнего регистра кла- виатуры. В частности, все идентификаторы в программной среде Maple регист- ро-зависимы;

- понятия “имя УВВ, имя файла, каталог, подкаталог, цепочка каталогов, путь” соот- ветствуют полностью соглашениям MS DOS; при кодировании пути к файлу в

качестве разделителя используется, как правило, сдвоенный обратный слэш (\\);

- СФ - спецификатор файла, определяющий полный путь к нему либо имя;

- СБО (Сlipboard) – системный буфер обмена;

- ГМП (GUI) - главное меню пакета; содержит группы функций его оболочки;

- hhh-файл - файл, имеющий “hhh” в качестве расширения его имени; - под Input-параграфом в дальнейшем понимается вводимая в интерактивном ре- жиме в текущий сеанс информация, слева идентифицируемая символом ввода «>». По умолчанию, ввод оформляется красным цветом. Тогда как под Output- параграфом понимается результат выполнения предшествующего ему Input-па- раграфа (выражения, тексты процедур и модулей, графические объекты и т.д.). Сле- дующий простой пример поясняет вышесказанное:

> A, V:= 2, 12: assign('G' = Int(1/x*sin(x)*cos(x), x)), G = value(G); plot(rhs(%), x = 4 .. V, thickness=A, color=green, axesfont=[TIMES, BOLD, V], labelfont=[TIMES, BOLD, V]);

В дальнейшем ради удобства и компактности там, где это возможно, блок из 2-х параграфов {Input, Output} будем представлять в строчном формате, а именно: > Input-параграф; ⇒ Output-параграф

Остальные необходимые обозначения, понятия, сокращения и соглашения будут нами вводится по мере надобности по тексту книги. Тогда как с нашими последующими разработками и изданиями по данной проблематике можно периодически знакомиться на Web-страницах:

www.aladjev.newmail.ru, www.aladjev.narod.ru, www.aladjev-maple.narod.ru

На этих же страницах находятся email-адреса, в которые можно отправлять все замечания, пожелания и предложения, относящиеся к материалу настоящей книги, а также в целом по Maple-тематике. Все они будут приняты с благодарностью и без нашего внимания не оставлены.

Гродно – Таллинн, февраль, 2007

Глава 1. Базовые сведения по Maple-языку пакета

Пакет Maple способен решать большое число, прежде всего, математически ориентированных задач вообще без программирования в общепринятом смысле. Вполне можно ограничиться лишь описанием алгоритма решения своей задачи, разбитого на отдельные последовательные этапы, для которых Maple имеет уже готовые решения. При этом, Maple располагает довольно большим набором процедур и функций, непосредственно решающих совсем не тривиальные задачи как то интегрирование, дифференциальные уравнения и др. О многочисленных приложениях Maple в виде т.н. пакетов и говорить не приходится. Тем не менее, это вовсе не означает, что Maple не предполагает программирования. Имея собственный достаточно развитый язык программирования (в дальнейшем просто Maple-язык), пакет позволяет программировать в своей среде самые разнообразные задачи из различных приложений. Обсуждение данного аспекта нашло отражение в нашей книге [103], а именно о дилетантском взгляде на вопрос программирования в Maple, да, впрочем, и в ряде других математических пакетов.

В среде пакета можно работать в двух основных режимах: интерактивном и автоматическом (программном). Интерактивный режим аналогичен работе с калькулятором, пусть и весьма высоко интеллектуальным – в Input-параграфе вводится требуемое Maple-выражение и в следующем за ним Output-параграфе получаем результат его вычисления. Так шаг за шагом можно решать в среде пакета достаточно сложные математические задачи, в процессе работы формируя текущий документ (ТД). Для такого режима требуется относительно небольшой объем знаний о самом пакете, а именно:

* знание общей структуры ТД и синтаксиса кодирования выражений;

* знание синтаксиса используемых функциональных конструкций;

* общие правила выполнения, редактирования и сохранения текущего документа.

Таким образом, средства Maple-языка позволяют пользователю работать в среде пакета без сколько-нибудь существенного знания даже основ программирования, а подобно конструктору собирать из готовых функциональных компонент языка на основе его синтаксиса требуемый вам алгоритм, включая его выполнение, отображение результатов на экране (в обычном и/или графическом виде), в файле и в твердой копии, и (2) использовать всю мощь языка как для создания развитых систем конкретного назначения, так и средств, расширяющих собственно саму программную среду пакета, возможности которых определяются только вашими собственными умением и навыками в сфере алгоритмизации и программирования.

Программирование в среде Maple-языка в большинстве случаев не требует особого программистского навыка (хотя его наличие и весьма нелишне), ибо в отличие от языков универсального назначения и многих проблемно-ориентированных Maple-язык включает большое число математически ориентированных функций, позволяя одним вызовом функции решать достаточно сложные самостоятельные задачи, например: вычислять минимакс выражения, решать системы дифуравнений или алгебраических уравнений, вычислять производные и интегралы, выводить графики сложных функций и др. Тогда как интерактивность языка обеспечивает не только простоту его освоения и удобство редактирования, но также в значительной мере предоставляет возможность эвристического программирования, когда методом «проб и ошибок» пользователь получает возможность запрограммировать свою задачу в полном соответствии со своими нуждами. Реальная же мощь языка обеспечивается не только его управляющими структурами и структурами данных, но и всем богатством его функциональных (встроенных, библиотечных, модульных) и прикладных (Maple-документов) средств, созданных к настоящему времени пользователями из различных прикладных областей, прежде всего физико-математических и технических, а в целом, естественно-научных.

Однако для более сложной работы (выполняющейся, как правило, в программном режиме) требуется знание встроенного Maple-языка программирования, который позволяет использовать всю вычислительную мощь пакета и создавать сложные ПС, не только решающие задачи пользователя, но и расширяющие средства самого пакета. Примеры такого типа представлены как в нашей книге [103], так и в настоящей книге. Именно поэтому, далее представим основные элементы программирования в среде пакета Maple.

Для лингвистического обеспечения решения задач пакет снабжен развитым встроенным проблемно-ориентированным Maple-языком, поддерживающим интерактивный режим. Являясь проблеммно-ориентированным языком программирования, Maple-язык характеризуется довольно развитыми средствами для описания задач математического характера, возникающих в различных прикладных областях. В соответствии с языками этого класса структуры управляющей логики и данных Maple-языка в существенной мере отражают специфику средств именно для математических приложений. Особую компоненту языка составляет его функциональная составляющая, поддерживаемая развитой библиотекой функций, покрывающих большую часть математических приложений. Наследуя многие черты С-языка, на котором был написан компилятор интерпретирующего типа, Maple-язык позволяет обеспечивать как численные вычисления с любой точностью, так и символьную обработку выражений и данных, поддерживая все основные операции традиционной математики [8-14].

Средства Maple-языка позволяют пользователю работать в среде пакета в двух режимах: (1) на основе функциональных средств языка с использованием правил оформления и работы с Maple-документом предоставляется возможность на интерактивном уровне формировать и выполнять требуемый алгоритм прикладной задачи без сколько-нибудь существенного знания даже основ программирования, а подобно конструктору собирать из готовых функциональных компонент языка на основе его синтаксиса требуемый вам алгоритм, включая его выполнение, отображение результатов на экране (в обычном или графическом виде), в файле и в твердой копии, и (2) использовать всю мощь языка как для создания развитых систем конкретного назначения, так и средств, расширяющих собственно саму среду пакета, возможности которых определяются только вашими собственными умением и навыками. При этом, первоначальное освоение языка не предполагает предварительного серьезного знакомства с основами программирования, хотя знание их и весьма предпочтительно. Тут же уместно отметить, что значительная часть функциональных средств самого пакета написана и скомпилирована именно на Maple-языке, позволившим создать довольно эффективные относительно основных ресурсов ЭВМ загрузочные модули. Анализ данных средств является весьма неплохим подспорьем при серьезном освоении Maple-языка. Наряду с этим, в архиве [109] представлены исходные тексты процедур и программных модулей нашей Библиотеки [41], которые также могут служить в качестве неплохого иллюстративного материала для осваивающих программную среду пакета.

В настоящей главе рассматриваются базовые элементы Maple-языка в предположении, что читатель в определенной мере имеет представление о работе в Windows-среде и с самим пакетом в режиме его графического меню (GUI) в пределах главы 3 [12] либо, например, книг [29-33,45,46,54-62,78-89,103,108,111-130] и подобных им изданий.

1.1. Базовые элементы Maple-языка пакета

Определение Maple-языка можно условно разбить на четыре базовые составляющие, а именно: алфавит, лексемы, синтаксис и семантику. Именно две последние составляющие и определяют суть того или иного языка. Синтаксис составляют правила образования корректных предложений из слов языка, которые должны строго соблюдаться. В случае обнаружения на входе Maple-предложения с синтаксической ошибкой выводится диагностическое сообщение, сопровождаемое “^”-указателем на место возможной ошибки или установкой в место ошибки |-курсора, как это иллюстрирует следующий фрагмент:

> A:= sqrt(x^2+y^2+z^2); V:=x*56 |Z:= sin(x)/(a+b); Error, missing operator or `;`

> A:= sqrt(x^2+y^2+z^2): V:= x**|*56: Z:= sin(x)/(a+b); Error, `*` unexpected

> read `C:\\ARM_Book\\Grodno\\Academy.99`: on line 0, syntax error, `*` unexpected:

VGS:=56***sin(x)/sqrt(Art^2+Kr^2)-98*F(x,y);

^

Error, while reading `C:\ARM_Book\Grodno\Academy.99`

При этом следует иметь в виду два обстоятельства: (1) идентифицируется только первая встреченная ошибка и (2) при наличии синтаксических ошибок (например, несогласования открывающих и закрывающих скобок) в сложных выражениях язык может затрудняться с точной их диагностикой, идентифицируя ошибочную ситуацию сообщением вида “ `;` unexpected”, носящим в целом ряде случаев весьма приблизительный характер. Mapleязык производит синтаксический контроль не только на входе конструкций в интерактивном режиме работы, но и в момент считывания их из файлов. В последнем случае синтаксическая ошибка инициирует вывод соответствующего диагностического сообщения указанного выше вида с указанием номера первой считанной строки, содержащей ошибку, и идентификацией спецификатора файла, как это иллюстрирует последний пример фрагмента. Ниже вопросы синтаксического анализа Maple-конструкций будут рассмотрены более детально, прежде всего, при рассмотрении parse-функции пакета.

В отличие от синтаксиса, определяющего правила составления корректных языковых конструкций, семантика языка определяет алгоритмы их обработки, т.е. определяет их понятийное назначение с точки зрения самого языка. Например, результатом обработки конструкции вида “W:= 57*sin(19.99);” является присвоение W-переменной результата произведения целого числа “57” и значения sin-функции в точке “19.99” ее вызова. При этом, определяется как собственно результат, так и его тип. В связи со сказанным наряду с синтаксическими, как правило, распознаваемыми языком, могут возникать и семантические ошибки, которые язык не распознает, если они при этом, не инициируют, в свою очередь, ошибок выполнения. Типичным примером семантических ошибок является конструкция вида “A/B*C”, трактуемая языком как “(A*C)/B”, а не как “A/(B*C)” на первый взгляд. Как правило, семантические ошибки выявляются на стадии выполнения Maple-программы или вычисления отдельных Maple-выражений и данная процедура относится к этапу отладки программ и процедур, рассматриваемому несколько ниже.

Синтаксис Maple-языка определяется выбранным набором базовых элементов и грамматикой, содержащей правила композиции корректных конструкций языка из базовых элементов. Рассмотрение базовых элементов начнем со входного алфавита языка, в качестве элементов которого используются следующие символы:

♦ заглавные и прописные буквы латинского алфавита (A .. ÷Z; a .. ÷z; 52);

♦ десятичные цифры (0 .. 9; 10);

♦ специальные символы (` ! @ # $ % ^ & * ( ) _ + { } : “ < > ? | - = [ ] ; ‘ , . / \; 32); ♦ заглавные и прописные буквы русского алфавита (кириллицы: A .. Я; a .. я; 64).

Синтаксис Maple-языка объединяет символы входного алфавита в лексемы, состоящие из ключевых (зарезервированных) слов, операторов, строк, натуральных чисел и знаков пунктуации. Рассмотрим несколько детальнее каждую из составляющих. В качестве ключевых Maple-язык использует слова, представленные в следующей табл. 1.

Таблица 1

Ключевые слова:

Смысловая нагрузка:

if, then, else, elif, fi

условное if-предложение языка

for, from, in, by, to, while, do, od

предложения циклических конструкций

proc, local, global, option, description, end

процедурные выражения языка

read, save

функции чтения и записи выражений

done, quit, stop

функции завершения работы

union, minus, intersect

операторы над множествами

and, or, not

логические операторы языка

mod

оператор вычисления по модулю

Так как ключевые слова несут определенную смысловую нагрузку, то они не могут использоваться в качестве переменных, в противном случае инициируется ошибка:

> local:= 64;

Error, reserved word `local` unexpected

В Maple-языке существует целый ряд других слов, имеющих специальный смысл, например идентификаторы (имена) функций и типов, однако пользователь может использовать их в программах в определенных контекстах. При этом, защита их от модификации обеспечивается совершенно иным механизмом (базирующемся на protected-механизме), рассматриваемым несколько ниже.

Операторы языка относятся к трем типам: бинарные, унарные и нульарные. Допускаемые языком унарные операторы представлены в следующей табл. 2.

Таблица 2

Унарный оператор:

Смысловая нагрузка оператора:

+

префиксный плюс

-

префиксный минус

{!|factorial}

факториал (постфиксный оператор)

$

префиксный оператор последовательности

.

десятичная точка (префиксный или постфиксный)

%<целое>

оператор метки

not

префиксный логический оператор отрицания

&<строка>

префиксный пользовательский оператор

Унарные операторы (+, -, not) и десятичная точка (.) имеют вполне прозрачный смысл и особых пояснений не требуют. Здесь мы кратко остановимся на операторе метки (%); при этом понятие метки в Maple-языке существенно иное, чем для традиционных языков программирования. Унарный оператор метки кодируется в виде %<целое> и служит для представления общих подвыражений большого выражения в целях более наглядного представления второго. Данный оператор используется Maple-языком для вывода выражений на экран и на принтер в удобном виде. После возвращения языком представленного в терминах %-оператора выражения к переменным %<целое> можно обращаться как к обычным определенным переменным. Для возможности использования представления выражений в терминах %-оператора используются две опции interface-переменной оболочки пакета: labelling и labelwidth, определяющих соответственно допустимость такого представления и длину меченных подвыражений. При этом, данная возможность допустима не для всех форматов Output-параграфа текущего документа.

Допустимые языком базовые бинарные операторы представлены в следующей табл. 3.

Таблица 3

Оператор

Смысловая нагрузка:

Оператор

Смысловая нагрузка:

+

сложения

<

меньше чем

-

вычитания

<=

меньше чем или равно

*

умножения

>

больше чем

/

деления

>=

больше чем или равно

{**|^}

степени

=

равно

:=

присваивания

<>

не равно

:-

выбора элемента модуля

,

разделитель выражений

$

последовательности

->

функциональный

@

композиции функций

mod

взятие модуля

@@

кратной композиции

union

объединение множеств

::

определения типа

minus

разность множеств

&<строка>

нейтральный инфексный

intersect

пересечение множеств

||

конкатенации строк

and

логическое И

..

ранжирования

or

логическое ИЛИ

С большинством из приведенных в табл. 3 унарных операторов читатель должен быть хорошо знаком, тогда как такие как (:=, $, @, @@, ..) в определенной мере специфичны для математически ориентированных языков и будут рассмотрены ниже. По остальным же пояснения будут даваться ниже по мере такой необходимости.

Наконец, три нульарных оператора %, %% и %%% (один, два и три знака процентов) представляют специальные идентификаторы Maple-языка, принимающие в качестве значений результат вычисления соответственно последнего, предпоследнего и предпредпоследнего предложения. Следующий фрагмент иллюстрирует применение нульарных операторов:

> AG:= 59: AV:= 64: AS:= 39: (%%+3*%+%%%-2); ⇒ 238

> P:=proc() args; nargs; %, %% end proc: P(59, 64, 39, 17, 10, 44); ⇒ 6, 59, 64, 39, 17, 10, 44

Как правило, нульарные операторы используются в интерактивном режиме работы с пакетом и выступают на уровне обычных переменных, позволяя обращаться к результатам предыдущих вычислений на глубину до трех. Однако использование их вполне допустимо и в теле процедур, где они выступают на уровне локальных переменных, как это иллюстрирует второй пример фрагмента. Вопросы организации процедур рассматриваются ниже. Наряду с возможностью использования {%|%%|%%%}-конструкции для доступа к результатам предыдущих вычислений в текущем сеансе работы языком предоставляется history-механизм обращения к любому полученному в рамках его истории вычислению. Детально данный механизм рассмотрен в нашей книге [12]. В частности, там же представлен и анализ данного механизма, говорящий о недостаточно продуманной и реализованной системе, обеспечива-ющей history-механизм; в целом весьма полезного средства, но при наличии существенно более широких возможностей, подобных, например, пакету Mathematica [6,7], где подобный механизм представляется нам намного более развитым.

Приоритетность операторов Maple-языка в порядке убывания определяется как:

|| :- :: % & ! {^, @@} { ., *, &*, /, @, intersect} {+, -, union, minus} mod subset

.. {<, <=, >, >=, =, <>, in} $ not and or xor implies -> , assuming :=

В качестве строк Maple-язык рассматривает любые последовательности символов, кодируемые в верхних двойных кавычках ("), например: "Академия Ноосферы". При этом, составлять строку могут любые допустимые синтаксисом языка символы. При необходимости поместить в строку верхнюю двойную кавычку ее следует дублировать либо вводить комбинацией вида (\"). Это же относится и к ряду других символов, вводимых посредством обратного слэша (\), как это иллюстрирует следующий весьма простой фрагмент:

> `Строка`:= "Международная Академия Ноосферы"; Строка:= "Международная Академия Ноосферы"

> `Строка 1`:= "Международная\nАкадемия\nНоосферы";

Строка 1 := "Международная

Академия

Ноосферы"

> `Строка 2`:= "Internatio\nal Academy of Noosphere"; Строка 2 := "Internatio nal Academy of Noosphere"

> `Строка 3`:= "Российская Эколо"гическая Академия";

Error, missing operator or `;`

Однако, как иллюстрируют примеры фрагмента, если наличие в строке одинарной двойной кавычки (помимо ограничивающих) вызывает синтаксическую ошибку, то для случая обратного слэша в общем случае могут возникать семантические ошибки. Максимальная длина строки зависит от используемой платформы: для 32-битных ЭВМ - 524271 символов, а для 64-битных - 34359738335 символов. О средствах работы со строчными структурами детальнее речь будет идти несколько ниже.

В качестве символов Maple-язык рассматривает любые последовательности символов, в общем кодируемые в верхних обратных кавычках (`), например: `Академия Ноосферы 64`. При этом, составлять символ могут любые допустимые синтаксисом языка символы. При необходимости поместить в символ верхнюю обратную кавычку ее следует дублировать либо вводить комбинацией вида (\`). Это же относится и к ряду других символов, вводимых посредством обратного слэша (\), как это иллюстрирует фрагмент:

> `Строка`:= `Международная Академия Ноосферы`; Строка:= Международная Академия Ноосферы

> `Строка 1`:= `Международная\nАкадемия\nНоосферы`;

Строка 1 := Международная

Академия Ноосферы

> `Строка 2`:= `Internatio\nal Academy of Noosphere`; Строка 2 := Internatio nal Academy of Noosphere

> `Строка 3`:= `Российская Эколо`гическая Академия`; Error, missing operator or `;`

Сразу же следует отметить одно принципиальное отличие строк от символов Maple-языка. Если символам можно присваивать значения, то строки такой процедуры не допус- кают, вызывая ошибочные ситуации, например:

> "Данные":= 2006;

Error, invalid left hand side of assignment

> Данные:= 2006: `Результат`:=350: Данные, Результат;

2006, 350

При этом, если символ не содержит специальных знаков, то его можно кодировать и без ограничивающих его обратных одинарных кавычек, как иллюстрирует второй пример.

В качестве натурального целого язык рассматривает любую последовательность из одной или более десятичных цифр, при этом все ведущие нули игнорируются, т.е. 005742 рассматривается как 5742. Длина таких чисел зависит от используемой пакетом платформы и на большинстве 32-битных ЭВМ составляет порядка 524280 десятичных цифр, тогда как на 64-битных она достигает уже 38654705646 цифры. В качестве целого рассматривается натуральное целое со знаком или без. Для работы с арифметикой целых чисел язык располагает достаточно развитыми средствами, рассматриваемыми ниже.

Наконец, знаки пунктуации Maple-языка представлены в следующей табл. 4.

Таблица 4

Знак пунктуации:

Смысловая нагрузка:

; и :

точка с запятой и двоеточие

( и )

левая и правая круглые скобки

< и >

левая и правая угловые скобки

{ и }

левая и правая фигурные скобки

[ и ]

левая и правая квадратные скобки

"

двойная верхняя кавычка

|

вертикальный разделитель

` ' , .

кавычка, апостроф, запятая и точка

Представим использование Maple-языком указанных в табл. 4 знаков пунктуации:

: и ; - служат для идентификации конца предложений Maple-языка; различия между ними обсуждаются ниже;

( ) - для группировки термов в выражениях, а также формальных или фактических аргументов при определениях или вызовах функций/процедур;

<> - для определенной пользователем группировки выражений;

{} - для определения структур данных типа множество;

[] - для определения структур данных типа список, а также для образования индек- сированных переменных и оператора выбора элемента из индексированных вы- ражений;

` ' , - соответственно для определения идентификаторов, невычисляемых выражений и структур типа последовательность; при этом, следует четко различать при коди- ровании конструкций Maple-языка символы верхней обратной кавычки (96) и апо- строфа (39), в скобках даны их десятичные коды по внутренней кодовой таблице; " - верхние двойные кавычки служат для определения строчных структур данных.

Для разделения лексемов синтаксис языка допускает использование как знаков пунктуации, так и пробелов, понимаемых в расширенном смысле, т.е. в качестве пробелов допускается ис-ользование символов: собственно пробела (space), табуляции (tab), перевода строки и возврата каретки. При этом, сами символы пробела не могут входить в состав лексемов, являясь их разделителями. Между тем, space-символ может входить в состав строк и символов, образованных двойными или одинарными верхними кавычками. Использование же его в составе лексема, как правило, инициирует ошибочную ситуацию, например: > AGN: = sqrt(x^2 + y^2 + z^2 + 19.47);

Error, `=` unexpected

Под программной строкой в Maple-языке понимается строка символов, завершающаяся символами перевода строки и возврата каретки (16-ричные коды 0D0A); при этом, сами эти символы строке не принадлежат. Пустые программные строки образуются простым нажатием Enter-клавиши. Завершение программной строки по Enter-клавише в интерактивном режиме вызывает немедленное вычисление всех содержащихся в ней выражений и предложений языка. Если в программной строке содержится #-символ, то язык рассматривает всю последующую за ним информацию в качестве комментария и обработки ее не производит. Этот прием можно использовать для комментирования текстов Mapleпрограмм, тогда как в интерактивном режиме особого смысла он не имеет. Вывод длинных строк производится в несколько строк экрана, идентифицируя символом обратного слэша (\) продолжение строки. При этом символ обратного слэша несет более широкую смысловую нагрузку, а именно: наряду с функцией продолжения он может выступать в качестве пассивного оператора и средства ввода управляющих символов.

В первом качестве он используется, как правило, для разбиения на группы строчных структур или цифровых последовательностей в целях более удобного их восприятия. Тогда как во втором случае он позволяет вводить управляющие символы, производящие те или иные действия. В этом случае кодируется конструкция следующего вида: ”\<управляющий символ>”, где управляющий символ является одним из следующих восьми {a, b, e, f, n, r, t, v}; например, по конструкции “\n” производится перевод строки, а по “\a” – звонок, с другими детально можно ознакомиться по книге [10] либо по Help-системе пакета посредством предложения вида: > ?backslash. Следующий фрагмент иллюстрирует использование символа обратного слэша в указанных выше контекстах:

> A:= `aaaaaaaaaaa\nbbbbbbbbbbbb\nccccccccccccc`; # Перевод строки A := aaaaaaaaaaa bbbbbbbbbbbb ccccccccccccc

> A:= `aaaaaaaaaa\a\nbbbbbbbbbbb\a\ncccccccccccc`; # Звонки с переводом строки A := aaaaaaaaaa• bbbbbbbbbbb• cccccccccccc

> 1942.1949\1959\1962\1966\1972\1995\1999\2006;

1942.19491959196219661972199519992006

Употребление обратного слэша без следующего за ним одного из указанных управляющих символов в середине лексема приводит к его игнорированию, т.е. он выступает в качестве пустого оператора. Для действительного ввода в строку обратного слэша его нужно кодировать сдвоенным. При этом, следует иметь в виду, что в случае кодирования спецификаторов файлов в функциях доступа в качестве разделителей подкаталогов можно использовать сдвоенные обратные слэши (\\) или одинарные прямые слэши (/), в противном случае возможно возникновение семантических ошибок. По конструкции вида kernelopts(dirsep) возвращается стандартный разделитель подкаталогов, принимаемый по умолчанию, однако его переопределение невозможно.

После успешной загрузки пакета Maple производится выход на его главное окно, структура и назначение компонент которого детально рассмотрены, например, в [9-14]. В области текущего документа в первой строке устанавливается >-метка ввода, идентифицирующая запрос на ввод информации, как правило, завершающийся {;|:}-разделителем с последующим нажатием Enter-клавиши либо активацией !-кнопки 4-й строки главного окна. Результатом данной процедуры является передача пакету программной строки, содержащей отдельное Maple-выражение, вызов функции либо несколько предложений языка. Под Maple-программой (документом) понимается последовательность предложений языка, описывающая алгоритм решаемой задачи, и выполняемая, если не указано противного, в естественном порядке следования своих предложений.

В интерактивном режиме ввод программной строки производится непосредственно за >-меткой ввода (Input-параграф текущего документа) и завершается по клавише Enter, инициирующей синтаксический контроль введенной информации с последующим вычислением (если не обнаружено синтаксических ошибок) всех входящих в строку Maple-предложений. В дальнейшем мы ради удобства представления иллюстративных примеров программную строку и результат ее вычисления будем иногда представлять в следующем достаточно естественном формате:

> Maple-предложение {:|;} ⇒ Результат вычисления

При этом, в зависимости от завершения Maple-предложения {;|:}-разделителем результат его вычисления соответственно {выводится|не выводится} в текущий документ, т. е. {формируется|не формируется} Output-параграф. Однако следует иметь в виду, что «не формируется» вовсе не означает «не возвращается» – результат выполнения Input-параграфа всегда возвращается и его можно получать, например, по нульарному %-оператору, как это иллюстрирует следующий весьма простой фрагмент:

> Tallinn:= 2006; ⇒ Tallinn := 2006

> %; ⇒ 2006

> Tallinn:= Grodno: %; ⇒ Grodno

Однако здесь следует иметь в виду, что может возвращаться и NULL-значение, т.е ничего. В этом случае %-оператор возвращает последний результат, отличный от NULL, как это иллюстрирует следующий простой пример:

> x:= 5: assign('y' = 64); assign('z' = 59); assign('s' = 39); %; ⇒ 5

Использование (:)-разделителя служит, главным образом, для того, чтобы избавиться в Maple-документе от ненужного вывода промежуточной и несущественной информации. Структурная организация Maple-документов достаточно детально была рассмотрена в [9-14,78-89]. Здесь мы лишь отметим особые типы программных строк, начинающихся с двух специальных управляющих {?, !}-символов Maple-языка пакета.

Использование в самом начале программной строки в качестве первого, отличного от пробела, ?-символа рассматривается Maple-языком как инструкция о том, что следующая за ним информация - фактические аргументы (последовательность которых разделяется запятой “,” или прямым слэшем “/”) для help-процедуры, обеспечивающей вывод справочной информации по указанным аргументами средствам пакета. Например, по конструкции формата “> ?integer“ выводится справка integer-раздела Help-системы пакета.

Использование в самом начале программной строки в качестве первого, отличного от пробела, !-символа рассматривается Maple-языком как инструкция о том, что следующая за ним информация предназначена в качестве команды для ведущей ЭВМ. Однако данная возможность поддерживается не всеми операционными платформами, например, для Windows-платформы в данном случае идентифицируется синтаксическая ошибка. Рассмотрев базовые элементы Maple-языка, переходим к более сложным его конструкциям.

1.2. Идентификаторы, предложения присвоения и выделения Maple-языка

Основной базовой единицей языка является предложение, представляющее собой любое допустимое Maple-выражение, завершающееся {;|:}-разделителем. Более того, не нарушая синтаксиса Maple-языка, под предложением будем понимать любую допустимую Maple-конструкцию, завершающуюся {;|:}-разделителем; при этом, предложение может завершаться и по Enter-клавише (символы перевод строки и возврат каретки), если оно содержит единственное выражение. В ряде случаев допустимо использование в качестве разделителя Maple-предложений даже запятой, если они находятся внутри программной строки, что позволяет выводить результаты вычислений в строчном (разделенном запятой) формате. Однако это требует весьма внимательного подхода, как к нетипичному приему. Следующий фрагмент иллюстрирует все перечисленные способы кодирования Maple-предложений:

> V:= 64: G:= 59: S:= 39: Art:= 17: Kr:= 10: V ,G, S, Art, Kr;

64, 59, 39, 17, 10

> ?HelpGuide # вывод справки по HelpGuide

> R := evalf( Art2 + Kr2 + V2 + G2 + S2 , 6) Z := Art 2 + Kr 2 + V 2 + G 2 + S 2

R := 97.4012 Z := 9487

if 17 ≤ evalf( Art2 + Kr2 ) then evalf  Art 89  else evalf  96 Kr  end if

> 

5.235294118

> V:= 64: G:= 59: Art:= 17: Kr:= 10: V, G, S, Art, H:= 2006: Kr; Error, cannot split rhs for multiple assignment

10

> assign('V', 42), assign('G', 47), assign('Art', 89), assign('Kr', 96), V, G, Art; 42, 47, 89

Как следует из двух последних примеров фрагмента, запятую в качестве разделителя предложений можно использовать лишь для разделения выражений. Тогда как третий и четвертый примеры иллюстрируют ввод предложений в стандартной математической нотации.

Согласно сказанному Maple-язык оперирует предложениями различных типов, определяемых типом конструкции, завершающейся рассмотренным выше способом. Ниже данный вопрос получит свое дальнейшее развитие. Однако, прежде всего нам необходимо определить такую конструкцию как выражение, состоящее из ряда более простых понятий. В первую очередь, рассмотрим понятие идентификатора, играющего одну из ключевых ролей в организации вычислительного процесса в среде Maple-языка аналогично случаю других современных языков программирования.

Идентификаторы . В терминологии пакета под символами (symbol) понимаются как собственно цепочки символов, так и идентификаторы, удовлетворяющие соглашениям пакета, т.е. имена для всех конструкций Maple-языка пакета. Идентификаторы служат для установления связей между различными компонентами вычислительного процесса как логических, так и информационных, а также для образования выражений и других вычислительных конструкций. В качестве идентификатора в Maple-языке выступает цепочка из не более, чем 524271 символов для 32-битной платформы и не более 34359738335 символов для 64-битной платформы, начинающаяся с буквы либо символа подчеркивания (_). Идентификаторы регистро-зависимы, т.е. одинаковые буквы на верхнем и нижнем регистрах клавиатуры полагаются различными. Данное обстоятельство может служить, на первых порах, источником синтаксических и семантических ошибок, ибо в большинстве современных ПС идентификаторы, как правило, регистро-независимы. В качестве примеров простых идентификаторов можно привести следующие:

AVZ, Agn, Vs_A_K, Ar_10, KrV, Tall_Est, Salcombe_Eesti_Ltd_99, Vasco_97

Для возможности определения русскоязычных идентификаторов либо идентификаторов, содержащих специальные символы, включая пробелы, их следует кодировать в верхних кавычках, как это иллюстрирует следующий простой пример:

> `ТРГ=TRG_9`:= 64: `Значение 1`:= 1942: evalf(`Значение 1`/`ТРГ=TRG_9`, 12);

30.3437500000

> Таллинн:= 56: Гродно:= 84: Вильнюс:= 100: Таллинн + Гродно + Вильнюс;

240

В принципе, простые русскоязычные идентификаторы могут кодироваться и без кавычек, однако во избежание возможных недоразумений рекомендуется для них использовать кавычки. В этом случае в качестве идентификатора может выступать произвольная цепочка символов, что существенно расширяет выразительные возможности Maple-языка, позволяя в ряде случаев переносить функции комментария на идентификаторы конструкций языка.

Наряду с такими простыми конструкциями идентификаторов Maple-язык пакета допускает и более сложные, определяемые несколькими путями. Прежде всего, идентификаторы, начинающиеся с комбинации символов (_Env) полагаются ядром сеансовыми, т. е. их действие распространяется на весь текущий сеанс работы с пакетом. Так как они служат, прежде всего, для изменения пакетных предопределенных переменных, то их использованию следует уделять особое внимание. По конструкции anames('environment') можно получать в текущем сеансе все активные пакетные переменные: > anames('environment');

Testzero, UseHardwareFloats, Rounding, %, %%%, Digits, index/newtable, mod, %%, Order, printlevel, Normalizer, NumericEventHandlers

Ниже данный тип переменных будет нами рассматриваться несколько детальнее. Для обозначения определенного типа числовых значений Maple-язык использует целый ряд специальных идентификаторов таких, как: _N, _NN, _NNp, _Z∼, _Zp∼, _NN∼ и др. Например, _Zp∼ и _NN∼ используются для обозначения целых и целых неотрицательных чисел, тогда как _Cp-переменные используются для обозначения постоянных интегрирования и т.д.

Пакетные переменные такие как Digits, Order, printlevel имеют приписанные им по умолчанию значения соответственно [10, 6, 1], которые можно переопределять в любой момент времени. Однако, если после выполнения restart-предложения, восстанавливающего исходное состояние ядра пакета, сеансовые переменные становятся неопределенными, то пакетные переменные восстанавливают свои значения по умолчанию. Более сложные виды идентификаторов, включающие целые фразы как на английском, так и на национальных языках, можно определять, кодируя их в верхних обратных кавычках; при этом, внутри ограничивающих кавычек могут кодироваться любые символы (при использовании верхних кавычек они дублируются). В случае простого R-идентификатора конструкции R и `R` являются эквивалентными, а при использовании ключевых слов Maple-языка (указанных в 1.1) в качестве идентификаторов они должны кодироваться в верхних обратных кавычках.

Пустой символ, кодируемый в виде ``, также может использоваться в качестве идентификатора, например:

> ``:= 64: ``; ⇒ 64

однако по целому ряду соображений этого делать не следует, то же относится и к символам ` `, ` `, ` ` и т.д. В противном случае могут возникать ошибочные и непредсказуемые ситуации. Вместе с тем, пустой символ (как и строка) отличен от значения глобальной NULL-переменной пакета, определяющей отсутствие выражение, т.е. ничего.

В последующем мы все более активно будем использовать понятие функции, поэтому здесь на содержательном уровне определим его. В Maple-языке функция реализует определенный алгоритм обработки либо вычислений и возвращает результат данной работы в точку своего вызова. Каждая функция идентифицируется уникальным именем (идентификатором) и ее вызов производится по конструкции следующего формата:

Имя(Последовательность фактических аргументов)

где передаваемые ей фактические аргументы определяют исходные данные для выполнения алгоритма функции и возвращаемый ею результат. Детально вопросы организации и механизма функциональных средств Maple-языка рассматриваются ниже.

Вторым способом определения сложных идентификаторов является кодирование объединяющих их составные части ||-оператора конкатенации и/или встроенной cat-функции конкатенации строк, имеющей простой формат кодирования cat(x, y, z, t, ...) и возвращающей объединенную строку (символ) xyzt... , где: x, y, z, t … - строки, символы или

||-операторы (при этом, ||-оператор не может быть первым или последним), как это иллюстрирует следующий весьма простой фрагмент:

> cat(111, aaa||bbb, ccc), cat("111", aaa||bbb, ccc||ddd), `111`||cat(aaa, bbb)||"222";

111aaabbbccc, "111aaabbbcccddd", (111cat(aaa, bbb)) || "222"

> `11111`||22222, "11111"||22222, cat(`11111`, 22222), cat("11111", 22222);

1111122222, "1111122222", 1111122222, "1111122222"

> 11111||22222; Error, `||` unexpected

> cat(11111, 22222); ⇒ 1111122222

При этом, как показывает второй пример фрагмента, тип возвращаемого в результате конкатенации нескольких аргументов значения определяется типом первого аргумента, а именно: если первый аргумент является строкой (символом), то и результат конкатенации будет строкой (символом). Это справедливо как для cat-функции, так и для ||-оператора. Вообще говоря, имеет место следующее определяющее соотношение для обоих методов конкатенации, а именно:

cat(x1, x2, ..., xj, ..., xn) = x1||x2|| ... ||xj|| ... xn

Результат конкатенации должен использоваться в кавычках, если составляющие его компоненты включали специальные символы. Полученные в результате конкатенации символы используются в дальнейшем как единые идентификаторы или просто символьные (строчные) значения. В принципе, допуская конкатенацию произвольных Maple-выражений, функция cat и ||-оператор имеют ряд ограничений, в частности, при использовании их в качестве аргументов и операндов функциональных конструкций, которые могут инициировать некорректные (а в ряде случаев и непредсказуемые) результаты:

> cat(ln(x), sin(x), tan(x)); ⇒ || (ln(x)) || (sin(x)) || (tan(x))

> ` `||F(x)||G(x), " "||F(x)||G(x); ⇒ ( F(x)) || G(x), (" F"(x)) || G(x)

> cat(F(x), G(x)), H||evalf(Pi); ⇒ || (F(x)) || (G(x)), Hevalf(Pi)

> H||(evalf(Pi)); ⇒ H || (3.141592654)

> cat(`sin(x)`, "cos(x)"); ⇒ sin(x)cos(x)

> [A||max(59, 64), A||(max(59, 64))]; ⇒ [Amax(59, 64), A64]

При этом, как следует из последних примеров предпоследнего фрагмента, cat-функция является более универсальным средством, чем ||-оператор. В любом случае данные средства рекомендуется использовать, как правило, относительно строчных либо символьных структур, апробируя допустимость их в других более общих случаях. Это связано и с тем обстоятельством, что ||-оператор конкатенации имеет максимальный приоритет, поэтому второй операнд может потребовать круглых скобок, как показано выше. С другой стороны, это может способствовать расширению в качестве операндов типов. Детальнее вопрос использования средств конкатенации рассматривается ниже.

Идентификаторы ключевых слов Maple-языка кодируются в верхних кавычках при использовании их в качестве аргументов функций либо обычных переменных, однако по целому ряду соображений последнего делать не рекомендуется. Не взирая на возможность использования в качестве идентификаторов произвольных символов, использовать ее рекомендуется только в случае необходимости контекстного характера, ибо в противном случае выражения с ними становятся мало обозримыми. В качестве идентификаторов допускается использование и русских имен и выражений, однако их следует как при определении, так и при использовании кодировать в верхних обратных кавычкам.

Наряду с рассмотренными типами Maple-язык допускает использование индексированных идентификаторов, имеющих следующий формат кодирования:

Идентификатор[Последовательность индексов]

при этом, в отличие от традиционных языков программирования, индексированный идентификатор не означает принадлежности его к массиву, обозначая просто индексированную переменную, как это иллюстрирует следующий простой фрагмент:

> A:= 64*V[1, 1] + 59*V[1, 2] - 10*V[2, 1] - 17*V[2, 2];

A := 64 V1, 1 + 59 V1, 2 − 10 V2, 1 − 17 V2, 2

> AG[47][59]:= 10*Kr[k][96] + 17*Art[j][89];

AG47 := 10 Krk + 17 Artj

59 96 89

Однако, при условии V-массива V[k, h]-переменная идентифицирует его (k, h)-элемент. И так как индексированность сохраняет свойство быть идентификатором, то его можно последовательно индексировать, как это иллюстрирует последний пример фрагмента.

На основе индексированной переменной базируется и предложение выделения, кодируемое подобно первой в следующем простом формате:

Идентификатор[Последовательность индексов] {;|:}

возвращающее элемент структуры с указанным идентификатором и заданными значениями индексов. Данное предложение имеет смысл только для структур, в которых можно выделить составляющие их элементы (списки, множества, массивы, матрицы и др.). Следующий простой пример иллюстрирует применение предложения выделения:

> L:= [59, 64, 39, 10, 17]: S:={V, G, Sv, Art, Kr}: L[3], S[5], S[3], L[4];

39, Sv, Art, 10

Наконец, идентификатор функции/процедуры кодируется в следующем формате: Идентификатор(Последовательность фактических аргументов)

определяя вызов функции с заданным именем с передачей ей заданных фактических аргументов, например: sin(20.06); ⇒ 0.9357726776.

Для идентификаторов любых конструкций Maple-языка допускается использование алиасов (дополнительных имен), позволяющих обращаться к конструкциям как по их основным именам, так и по дополнительным. Данный механизм языка обеспечивается встроенной alias-функцией, кодируемой в следующем формате: alias(alias_1=Id, alias_2=Id, ... , alias_n=Id)

где Id - основное имя, а alias_j - присваиваемые ему алиасы (j=1..n), например:

> G:= `AVZ`: alias(year = 'G', Grodno = 'G', `ГрГУ` = 'G'); ⇒ year, Grodno, ГрГУ

> [G, year, Grodno, `ГрГУ`]; ⇒ [AVZ, AVZ, AVZ, AVZ]

> alias(e = exp(1)); ⇒ year, Grodno, ГрГУ, e

> evalf(e); ⇒ 2.718281828

В приведенном фрагменте проиллюстрировано, в частности, определение более привычного e-алиаса для основания натурального логарифма, вместо принятого (на наш взгляд не совсем удачного) в пакете exp(1)-обозначения. Алиасы не допускаются только для числовых констант; при этом, в качестве Id-параметров alias-функции должен использоваться невычисленный идентификатор (т.е. кодируемый в апострофах), а не его значение. Если вызов alias-функции завершается (;)-разделителем, то возвращается последовательность всех на текущий момент присвоенных алиасов сеанса работы с ядром пакета. Механизм алиасов имеет немало интересных приложений, детальнее рассматриваемых в наших книгах [10-12,91].

Близкой по назначению к alias-функции является и функция macro формата: macro(X1 = Y1, ...., Xn = Yn)

возвращающая NULL-значение, т.е. ничего, и устанавливающая на период сеанса работы с ядром пакета односторонние соотношения X1 ⇒ Y1, ..., Xn ⇒ Yn. Точнее, любое вхождение Xj-конструкции в Input-параграфе либо при чтении ее из файла замещается на приписанную ей по macro-функции Yj-конструкцию. Исключением является вхождение Xj-конструкций в качестве формальных аргументов и локальных переменных процедур. В данном отношении macro-функция отличается от традиционного понятия макроса и более соответствует однонаправленному алиасу. Функция macro может быть определена для любой Maple-конструкции, исключая числовые константы. Более того, фактические аргументы macro-функции не вычисляются и не обрабатываются другими macro-функциями, не допуская рекурсивных macro-определений. Для изменения определения macro вполне достаточно выполнить новый вызов macro-функции, в которой правые части уравнений имеют другое содержимое. Тогда как для отмены macro-определения достаточно произвести соответствующий вызов функции macro(Xp = Yp). Следующий простой фрагмент иллюстрирует вышесказанное:

> restart; macro(x = x*sin(x), y = a*b+c, z = 56):

> HS:= proc(x) local y; y:= 42: y*x^3 end proc:

> macro(HS=AGN, x = x, y = 47, z = 99): map(HS, [x, y, z]);

[AGN(x), AGN(47), AGN(99)] > HS:= proc(x) local y; y:= 42: y*10^3 end proc:

> map(HS, [x, y, z]); ⇒ [42000, 42000, 42000]

> macro(HS=AGN, x = x, y = 47, z = 99): map(HS, [x, y, z]);

[AGN(x), AGN(47), AGN(99)]

> restart: HS:= proc(x) local y; y:= 42: y*x^3 end proc: HS(1999); 335496251958

> macro(HS=AGN): [AGN(1999), HS(1999)];

[AGN(1999), AGN(1999)]

> alias(AGN=HS): [AGN(1999), HS(1999)];

Warning, alias or macro HS defined in terms of AGN

[335496251958, AGN(1999)]

Из примеров фрагмента, в частности, следует вывод о необходимости достаточно внимательного использования macro-функции для идентификаторов процедур, ибо их переопределение приводит к неопределенности нового идентификатора со всеми отсюда вытекающими последствиями. При этом, в целом, ситуацию не исправляет и последующее использование alias-функции. На это следует обратить особое внимание.

Определение переменной в текущем документе (ТД) является глобальным, т.е. доступным любому другому ТД в течение текущего сеанса работы с ядром пакета. Сказанное не относится к режиму параллельного сервера, когда все загруженные документы являются независимыми. Режим параллельного сервера детально рассмотрен в [9-12]. При этом, определение считается сделанным только после его реального вычисления. После перезагрузки пакета все определения переменных и пользовательских функций/процедур (отсутствующих в библиотеках, логически сцепленных с главной библиотекой пакета) теряются, требуя нового переопределения. Без перезагрузки пакета этого можно добиться по предложению restart, приводящему все установки ядра пакета в исходное состояние (очистка РОП, отмена всех сделанных ранее определений, выгрузка всех загруженных модулей и т.д.), либо присвоением идентификаторам переменных невычисленных значений вида Id:='Id'. Следующий простой фрагмент иллюстрирует вышесказанное:

> x:= 19.42: y:= 30.175: Grodno:= sin(x) + cos(y); ⇒ Grodno := 0.8639257079

> restart; Grodno:= sin(x) + cos(y); ⇒ Grodno := sin(x) + cos(y)

> G:= proc() nargs end proc: G(42, 47, 67, 62, 89, 96); ⇒ 6

> G:= 'G': G(42, 47, 67, 62, 89, 96); ⇒ G(42, 47, 67, 62, 89, 96)

Из фрагмента хорошо видно, что ранее сделанное определение Grodno-переменной отменяется после выполнения restart-предложения. Выполнение restart-предложения следует лишь на внешнем уровне ТД и не рекомендуется внутри ряда его конструкций (функции, процедуры и др.) во избежание возникновения особых и аварийных ситуаций, включая “зависание” пакета, требующее перезагрузки ПК. При этом, следует иметь в виду, что освобождаемая в этом случае память не возвращается операционной среде, а присоединяется к собственному пулу свободной памяти пакета. Поэтому при необходимости получения максимально возможной памяти для решения больших задач пользователю рекомендуется все же производить перезагрузку пакета в Windows-среде. Тогда как второй способ отмены определенности для переменных более универсален. В книге [103] и в прилагаемой к ней Библиотеке представлены средства, обеспечивающие возможность восстановления из процедур исходного состояния объектов. Например, вызов процедуры prestart() очищает все переменные, определенные в текущем сеансе, исключая пакетные переменные.

Предложение присвоения . Идентификатору может быть присвоено любое допустимое Maple-выражение, делающее его определенным; в противном случае идентификатор называется неопределенным, результатом вычисления которого является символьное представление самого идентификатора, что весьма прозрачно иллюстрирует следующий простой пример:

> macro(Salcombe = Vasco): Eesti:= 19.95: Vasco:= Noosphere: Tallinn:= 20.06:

> TRG:= sqrt(Lasnamae*(Eesti + Tallinn)/(Salcombe + Vasco)) + `Baltic Branch`;

Lasnamae

TRG := 4.472694937 + Baltic Branch

Noosphere

Присвоение идентификатору определенного или неопределенного значения производится посредством традиционного (:=)-оператора присвоения вида А:= В, присваивающего левой А-части В-значение. При этом, в качестве левой А-части могут выступать простой идентификатор, индексированный идентификатор или идентификатор функции с аргументами. Точнее, присвоение А-части В-значения корректно, если A имеет assignable-тип, т.е. type(A, 'assignable'); ⇒ true. Вычисленное (или упрощенное) значение В-части присваивается идентификатору А-части.

Оператор присваивания допускает возможность множественного присваивания и определяется конструкциями следующего простого вида:

Id1, Id2, …, Idn := <Выражение_1>, <Выражение_2>, …, <Выражение_n>

При этом, при равном количестве компонент правой и левой частей присвоения производятся на основе взаимно-однозначного соответствия. В противном случае инициируются ошибочные ситуации "Error, ambiguous multiple assignment" либо "Error, cannot split rhs for multiple assignment". Следующий фрагмент иллюстрирует случаи множественного присваивания:

> A, B, C:= 64, 59:

Error, ambiguous multiple assignment

> V, G, S, Art, Kr, Arn:= 64, 59, 39, 17, 10, 44: [V, G, S, Art, Kr, Arn];

[64, 59, 39, 17, 10, 44]

> x, y, z, t, h:= 2006: [x, y, z, t, h];

Error, cannot split rhs for multiple assignment

[x, y, z, t, h]

Примеры фрагмента достаточно прозрачны и особых пояснений не требуют. Сам принцип реализации множественного присваивания также достаточно прост. Вместе с тем, языком не поддерживаются уже достаточно простые конструкции множественного присваивания, что иллюстрируют первый и последний примеры фрагмента.

Между тем, в ряде случаев возникает необходимость назначения того же самого выражения достаточно длинной последовательности имен или запросов функций. Данная проблема решается оператором `&ma`, который имеет идентичный с оператором `:=` приоритет. Оператор `&ma` имеет два формата кодирования, а именно: процедурный и операторный форматы:

&ma('x', 'y', 'z', ..., <rhs>) и ('x', 'y', 'z', ...) &ma (<rhs>)

В общем случае, в обоих случаях в конструкции lhs &ma rhs элементы lhs должны быть закодированы в невычисленном формате, т.е. в апострофах ('). Исключение составляет лишь первый случай присвоения. Кроме того, в операторном формате, левая часть lhs должна быть закодирована в скобках. Кроме того, если правая часть rhs удовлетворяет условию type(rhs, {'..', '<', ` <= `, '.', '*', '^', '+', '='}) = true, то правая часть должна также кодироваться в скобках. Наконец, если необходимо присвоить NULL-значение (т. е. ничего) элементам левой части lhs, то в качестве правой части rhs кодируется _NULL-значение. Успешный вызов процедуры `&ma` или применения оператора &ma возвращает NULL-значение, т. е. ничего с выполнением указанных присвоений. В целом ряде приложений оператор/процедура &ma представляются достаточно полезными [103]. Ниже приведен ряд примеров на применение оператора &ma:

Процедурный формат оператора:

> &ma(h(x), g(y), v(z), r(g), w(h), (a + b)/(c - d)); h(x), g(y), v(z), r(g), w(h);

a + b a + b a + b a + b a + b c − d, c − d, c − d, c − d, c − d

> &ma('x', 'y', 'z', 'g', 'h', "(a + b)/(c - d)"); x, y, z, g, h;

"(a+b)/(c-d)", "(a+b)/(c-d)", "(a+b)/(c-d)", "(a+b)/(c-d)", "(a+b)/(c-d)"

> &ma('x', 'y', 'z', 'g', 'h', _NULL); x, y, z, g, h;

> &ma'x', 'y', 'z', 'g', 'h', 2006); x, y, z, g, h; ⇒ 2006, 2006, 2006, 2006, 2006 > &ma('x', 'y', 'z', 'g', 'h', 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 _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

Для проверки идентификатора на предмет его определенности используется встроенная функция assigned языка, кодируемая в виде assigned(Идентификатор) и возвращающая значение true в случае определенности идентификатора (простого, индексированного или вызова функции/процедуры), и false-значение в противном случае. При этом, следует иметь в виду, что определенным идентификатор полагается тогда, когда он был использован в качестве левой части (:=)-оператора присвоения, даже если его правая часть являлась неопределенной. Или он получил присвоение по аssign-процедуре. Следующий простой фрагмент иллюстрирует вышесказанное:

> agn:=1947: avz:=grodno: assign(vsv=1967, art=kr): seq(assigned(k), k=[agn, avz, vsv, art]); true, true, true, true

> map(type,[agn, avz, vsv, art],'assignable'); ⇒ [false, true, false, true]

> map(assigned, [agn, avz, vsv, art]);

Error, illegal use of an object as a name

С другой стороны, по конструкции type(Id, 'assignable') мы можем тестировать допустимость присвоения Id–переменной (простой, индексированной или вызова функции/процедуры) выражения: возврат true-значения говорит о такой возможности, false – нет. Следует обратить внимание на последний пример фрагмента, иллюстрирующий некорректность использования встроенной функции map при попытке организации простого цикла.

Вызов функции indets(W {, <Тип>}) возвращает множество всех идентификаторов заданного его первым фактическим W-аргументом Maple-выражения. При этом, W-выражение рассматривается функцией рациональным, т.е. образованным посредством {+, -, *, /}-операций. Поэтому в качестве результата могут возвращаться не только простые идентификаторы W-выражения, но и неопределенные его подвыражения. В случае кодирования второго необязательного аргумента, он определяет тип возвращаемых идентификаторов, являясь своего рода фильтром. В качестве второго фактического аргумента могут выступать как отдельный тип, так и их множество в соответствии с типами, распознаваемыми тестирующей type-функцией языка, рассматриваемой детально ниже. Следующий фрагмент иллюстрирует применение indets-функции для выделения идентификаторов переменных:

> indets(x^3 + 57*y - 99*x*y + (z + sin(t))/(a + b + c) = G);

{sin(t), x, y, z, t, a, b, c, G}

> indets(x^3 + 57*y - 99*x*y + (z + sin(t))/(a + b + c) = G, function); ⇒ {sin(t)} > indets(x^3 + z/y - 99*x*y + sin(t), {integer, name});

{-99, -1, 3, x, y, z, t}

> indets(x^3 + z/y - 99*x*y + sin(t), {integer, name, `*`, `+`});

{-99 -1 3, , , , , , ,x z y t , x3 + − z 99 x y + sin( )t , −99 x y} z

y y

> indets(x^3 + z/y - 99*x*y + sin(t), {algnum, trig});

{-99, -1, 3, sin(t)}

Из приведенного фрагмента, в частности, следует, что по indets-функции можно получать не только неопределенные идентификаторы, но и числовые компоненты тестируемого выражения, а также совокупности комбинаций составляющих его компонент. Таким образом, indets-функция несет существенно более развитую смысловую нагрузку, чем определение простых идентификаторов. По своим возможностям она представляется достаточно эффективным средством при решении задач символьных обработки и анализа выражений, а также в целом ряде других важных задач.

Для возможности использования произвольного А-выражения в качестве объекта, которому могут присваиваться значения, Maple-язык располагает evaln-функцией, кодируемой в виде evaln(A) и возвращающей имя выражения. В качестве А-выражения допускаются: простой идентификатор, индексированный идентификатор, вызов функции/ процедуры или конкатенация символов. В результате применения к А-выражению функции evaln оно становится доступным для присвоения ему значений, однако если ему не было сделано реального присвоения, то применение к нему assigned-функции возвращает значение false, т.е. А-выражение остается неопределенным. Таким образом, по конструкции Id:= evaln(Id) производится отмена присвоенного Id-идентификатору значения, делая его неопределенным, как это иллюстрирует следующий простой фрагмент:

> Asv:= 32: assigned(Asv); Asv:= evaln(Asv): Asv, assigned(Asv); true

Asv, false

> Asv:= 67: assigned(Asv); Asv:= 'Asv': Asv, assigned(Asv); true

Asv, false

Из приведенного фрагмента непосредственно следует, что первоначальное присвоение Asv-идентификатору значения делает его определенным, на что указывает и результат вызова assigned-функции. Тогда как последующее выполнение Asv:=evaln(Asv) предложения делает Asv-идентификатор вновь неопределенным. Вторым способом приведения идентификатора к неопределенному состоянию является использование конструкции вида Id:= 'Id', как это иллюстрирует второй пример предыдущего фрагмента.

Для выполнения присвоений можно воспользоваться и assign-процедурой, допускающей в общем случае три формата кодирования, а именно: assign({A, B|A = B|C}), где А – некоторый идентификатор, В - любое допустимое выражение и С - список, множество либо последовательность уравнений. В первых двух случаях применение assign-процедуры эквивалентно применению (:=)-оператора присвоения, тогда как третий случай применяется для обеспечения присвоения левой части каждого элемента списка, множества или последовательности уравнений. Простой фрагмент иллюстрирует вышесказанное:

> assign(AGn, 59); assign(AVz=64); assign([xx=1, yy=2, zz=3, h=cos(y)*tan(z)]);

> assign({xx1=4, yy1=5, zz1=6, y=x*sin(x)}); [AGn, AVz, xx, yy, zz, xx1, yy1, zz1, y, h];

[59, 64, 1, 2, 3, 4, 5, 6, x sin(x), cos(x sin(x)) tan(z)]

> assign('x', assign('y', assign('z', 64))); [x, y, z]; ⇒ [64] # Maple 7 и выше

> assign('x', assign('y', assign('z', 64))); [x, y, z]; # Maple 6 и ниже Error, (in assign) invalid arguments

> `if`(type(AV, 'symbol') = true, assign(AV, 64), false); AV; ⇒ 64

> GS:= op([57*sin(19.95), assign('VG', 52)]) + VG*cos(19.99); ⇒ GS := 72.50422598

Успешное выполнение assign-процедуры производит указанные присвоения и возвращает NULL-значение, в противном случае инициируется соответствующая ошибочная ситуация. Данная ситуация, в частности, возникает при попытке рекурсивного вызова assign-процедуры для релизов 6 и ниже, тогда как в более старших релизах подобного ограничения нет. По отношению к процедуре assign релизы пакета характеризуются весьма существенной несовместимостью, что стимулировало нас к созданию аналога стандартной процедуры, который не только устраняет указанную несовместимость, но и расширяет функциональные возможности [31,39,41-43,45,46,103]. Это и другие наши средства рассмотрены детально в книге [103] и представлены в прилагаемой к ней Библиотеке программных средств.

При этом, следует отметить, что в целом ряде случаев assign-процедура является единственно возможным способом присвоения значений, например, внутри выражений, как это иллюстрирует последний пример фрагмента, который содержит структуры и функции, рассматриваемые ниже. Механизм assign-процедуры достаточно эффективен в различных вычислительных конструкциях, многочисленные примеры применения которого приводятся ниже при рассмотрении различных аспектов Maple-языка, а также в нашей Библиотеке [103].

Обратной к assign-процедуре является unassign-процедура с форматом кодирования: unassign(<Идентификатор_1>, <Идентификатор_2>, ...)

отменяющая определения для указанных последовательностью ее фактических аргументов идентификаторов. Успешный вызов процедуры unassign выполняет отмену назначений, возвращая NULL-значение. Однако, процедура не действует на идентификаторы с protected-атрибутом, инициируя ошибочную ситуацию с выводом соответствующей диагностики. Приведем простые примеры на использование unassign-процедуры.

> AS:= 39: AV:= 64: AG:= 59: Kr:= 10: Art:= 17: AS, AV, AG, Kr, Art;

39, 64, 59, 10, 17

> unassign(AS, AV, AG, Kr, Art);

Error, (in unassign) cannot unassign '39' (argument must be assignable) > unassign('AS', 'AV', 'AG', 'Kr', 'Art'); AS, AV, AG, Kr, Art;

AS, AV, AG, Kr, Art

> `if`(type(AV, 'symbol') = true, assign(AV, 64), unassign('AV')); AV; ⇒ 64

В приведенном фрагменте пяти переменным присваиваются целочисленные значения, а затем по unassign-процедуре делается попытка отменить сделанные назначения. Попытка вызывает ошибочную ситуацию, обусловленную тем, что в точке вызова процедуре unassign передаются не сами идентификаторы, а их значения (кстати, именно данная ситуация одна из наиболее типичных при ошибочных вызовах assign-процедуры). Для устранения ее идентификаторы следует кодировать в невычисленном формате (кодируя в апострофах), что иллюстрирует повторный вызов unassign-процедуры. Последний пример иллюстрирует применение процедур assign и unassign в условном if-предложении языка, по которому AV-переменной присваивается целочисленное значение, если она была неопределенной, и отменяется ее определение в противном случае.

Для отмены значений имен, имеющих protected-атрибут, может оказаться достаточно полезной и процедура Unassign('n1', 'n2', ...), возвращающая NULL-значение и отменяющая назначения для имен {'n1', 'n2', ...}, закодированных в невычисленном формате. Ее исходный текст представляется однострочным экстракодом следующего вида: Unassign := ( ) → `if`(nargs = 0, NULL, op([unprotect(args), unassign(args)]))

Приведем примеры применения процедур стандартной unassign и нашей Unassign:

> x, y, z, t, h:= 42, 47, 67, 89, 95: protect('x','y','z', 't', 'h'); > unassign('x','y','z', 't', 'h');

Error, (in assign) attempting to assign to `x` which is protected

> x, y, z, t, h; ⇒ 42, 47, 67, 89, 95

> Unassign('x','y','z', 't', 'h');

> x, y, z, t, h; ⇒ x, y, z, t, h

> Unassign();

> unassign();

Error, (in unassign) argument expected

Как следует из приведенного фрагмента, наша процедура Unassign в отличие от стандартной корректно обрабатывает особую ситуацию «отсутствие фактических аргументов», возвращая и в этом случае NULL-значение.

В целях защиты идентификаторов от возможных модификаций их определений (назначений) им присваивается protected-атрибут, делающий невозможной какую-либо модификацию указанного типа. Большинство пакетных идентификаторов имеют protectedатрибут, в чем легко убедиться, применяя к ним attributes-функцию, кодируемую в следующем формате: attributes(<Идентификатор>)

и возвращающую значения атрибутов заданного идентификатора, в частности атрибута protected. Если идентификатору не приписано каких-либо атрибутов, то вызов на нем attributes-функции возвращает NULL-значение, т.е. ничего. Для защиты от модификации либо снятия защиты используются процедуры protect и unprotect Maple-языка соответственно, кодируемые в следующем простом формате:

{protect|unprotect}(<Идентификатор_1>, <Идентификатор_2>, ...)

Следующий весьма простой фрагмент Maple-документа иллюстрирует вышесказанное:

> protect(AV_42); attributes(AV_42); ⇒ protected

> unassign(AV_42);

Error, (in assign) attempting to assign to `AV_42` which is protected > AV_42:= 64:

Error, attempting to assign to `AV_42` which is protected

> unprotect(AV_42); attributes(AV_42); AV_42:= 64; ⇒ AV_42 := 64

Следует при этом отметить, что действие protect-процедуры не распространяется на глобальные предопределенные переменные Maple, значения которых можно модифицировать согласно условиям пользователя. Такая попытка вызывает ошибочную ситуацию:

> map(attributes, [Digits, Order, printlevel]); protect('Digits'); protect('Order'); protect('printlevel'); ⇒ []

Error, (in protect) an Environment variable cannot be protected

Error, (in protect) an Environment variable cannot be protected

Error, (in protect) an Environment variable cannot be protected

Хотя по unprotect-процедуре отменяется protected-атрибут любого идентификатора, однако для пакетных идентификаторов этого (по целому ряду причин, здесь не рассматриваемых) делать не рекомендуется.

В целом ряде случаев в качестве весьма полезных средств могут выступать две встроенные функции со следующими форматами кодирования: unames() и anames({ |<Тип>}), возвращающие последовательности соответственно неопределенных и определенных идентификаторов (как пользовательских, так и пакетных), приписанных текущему Maple-сеансу. При этом, для случая anames-функции можно получать подборку определенных идентификаторов, значения которых имеют указанный Тип. Следующий фрагмент иллюстрирует результат вызова указанных выше функций:

> restart; unames();

identical, anyfunc, equation, positive, Integer, restart, radical, And, gamma, neg_infinity, none, default, nonposint, relation, odd, infolevel, indexable, algebraic, SFloat, RootOf, TABLE, float,

real_to_complex, embedded_real, vector, _syslib, realcons, name, assign, INTERFACE_GET, ...

> restart: SV:= 39: GS:= 82: Art:= sin(17): Kr:= sqrt(10): AV:= 64: anames(); sqrt/primes, type/interfaceargs, GS, sqrt, AV, csgn, interface, type/SymbolicInfinity, Art, sin, SV, Kr

> anames('integer'); # Maple 8 sqrt/primes, GS, Digits, printlevel, Order, AV, SV

> anames('environment');

Testzero, UseHardwareFloats, Rounding, %, %%%, Digits, index/newtable, mod, %%, Order, printlevel, Normalizer, NumericEventHandlers

> SV:= 39: GS:= 82: `Art/Kr`:= sin(17): Kr:= sqrt(10): _AV:= 64: anames('user'); # Maple 10

Kr, SV, GS

> anames('alluser'); ⇒ Kr, SV, GS, _AV, Art/Kr

При этом, unames-функция была вызвана в самом начале сеанса работы с пакетом и возвращаемый ею результат представлен только начальным отрезком достаточно длинной последовательности пакетных идентификаторов. Что касается anames-функции, то она в качестве второго необязательного аргумента допускает тип, идентификаторы с которым будут ею возвращаться. При этом, дополнительно к типу и в зависимости от релиза в качестве второго аргумента допускается использование таких параметров как user, environment, alluser, назначение которых можно вкратце охарактеризовать следующим образом. Параметр environment определяет возврат имен предопределенных переменных текущего сеанса пакета, параметр user (введенный, начиная с Maple 9) определяет возврат имен, определенных пользователем, тогда как параметр alluser (введенный, начиная с релиза 10) аналогичен по назначению предыдущему параметру user, но без фильтрации имен пользователя, содержащих прямой слэш «/» и префикс «_» (подчеркивание).

В ряде случаев требуется на основе определенного выражения определить имя (имена), которым в текущем сеансе было присвоено данное выражение. Данная задача решается нашей процедурой nvalue, базирующейся на стандартной процедуре anames и использующей некоторые особенности пакета. Ниже эта процедура будет представлена более детально, здесь же мы приведем лишь фрагмент ее применения, а именно:

> a:= 63: b:= 63: c:= "ransian63": c9:= "ransian63": d:= table([1=63, 2=58, 3=38]): L:= [x, y, z]: assign('h'=`svegal`): t47:= 19.42: t42:= 19.42: R:= a+b*I: B:= a+b*I: Z:= (a1+b1)/(c1+d1): f:=cos(x): g:=proc() `+`(args)/nargs end proc: r:=x..y: m:=x..y: n:= x..y: Lasnamae:= NULL: > Tallinn:=NULL: Grodno:= NULL: Vilnius:= NULL: Moscow:= NULL: map(nvalue, [63, "ransian63", table([1=63,2=58,3=38]), 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}, {c9, c}, {d}, {h}, {t47,t42}, {L}, {R,B}, {Z}, {f}, {g}, {m, r, n}, {Tallinn, Grodno, Vilnius, Moscow, Lasnamae}

1.3. Средства Maple-языка для определения свойств переменных

Важным средством управления вычислениями и преобразованиями в среде Maple-языка является assume-процедура и ряд сопутствующих ей средств. Процедура имеет следующие три формата кодирования:

assume(x1, p1, x2, p2, ...) assume(x1::p1, x2::p2, ...) assume(x1p1, x2p2, ...)

и позволяет наделять идентификаторы (переменные) или допустимые Maple-выражения xj заданными свойствами pj, т.е. устанавливать определенные свойства и соотношения между ними. Третий формат процедуры определяет соотношения, налагающие свойство pj на выражение xj. Например, простейшие, но весьма часто используемые вызовы assume(x >= 0) процедуры, определяют для некоторой х-переменной свойство быть неотрицательной действительной константой. Наделяемое по assume-процедуре свойство не является пассивным и соответствующим образом обрабатывается Maple-языком пакета при выполнении вычислений либо преобразований выражений, содержащих переменные, наделенные подобными свойствами. Тестировать наличие приписанного х-переменной свойства можно по вызову процедуры about(x), тогда как наделять х-переменную дополнительными свойствами можно по вызову процедуры additionally(x, Свойство). Следующий фрагмент иллюстрирует вышесказанное:

> assume(x >= 0): map(about, [x, y, z]); ⇒ [ ]

Originally x, renamed x~: is assumed to be: RealRange(0, infinity) y: nothing known about this object z: nothing known about this object

> assume(a >= 0): A:= sqrt(-a): assume(a <= 0): B:= sqrt(-a): [A, B]; ⇒ [ a~ I,

> simplify(map(sqrt, [x^2*a, y^2*a, z^2*a])); ⇒ [x~ −a~ I, y2 a~, z2 a~ ]

> additionally(x, 'odd'): about(x);

Originally x, renamed x~: is assumed to be:

AndProp(RealRange(0, infinity), LinearProp(2, integer, 1))

> map(is, [x, y, z], 'odd', 'posint'); ⇒ [true, false, false]

> assume(y, 'natural'): map(is, [x, y], 'nonnegative'); ⇒ [true, true]

> unassign('x'): about(x);

x: nothing known about this object

> assume(y, {y >= 0, y < 64, 'odd'}): about(y);

Originally y, renamed y~: is assumed to be: {LinearProp(2,integer,1), 0 <= y, y < 64}

> hasassumptions(y); ⇒ true

−a~ ]

Из примеров данного фрагмента, в частности, следует, что переменная с приписанным ей свойством выводится помеченной символом тильды (∼), а по about-процедуре выводится информация о всех приписанных переменной свойствах либо об их отсутствии. Один из примеров фрагмента иллюстрирует влияние наличия свойства положительной определенности переменной на результат упрощения содержащего ее выражения. В другом примере фрагмента иллюстрируется возможность определения для переменной множественности свойств, определяемых как поддерживаемыми языком стандартными свойствами, так и допустимыми отношениями для переменной. При этом, вызов процедуры hasassumptions(x) возвращает true, если на x-выражение было наложено какое-либо соотношение, и false в противном случае.

Режим идентификации assume-переменных определяется showassumed-параметром процедуры interface, принимающим значение {0|1 (по умолчанию)|2}: 0 - отсутствует идентификация, 1 - переменные сопровождаются знаком тильды и 2 - все такие переменные перечисляются в конце выражений, как это иллюстрирует следующий фрагмент:

> assume(V >= 64): assume(G >= 59): S:= V+G; interface(showassumed=0); V + G;

S := V~ + G~

V + G

> assume(Art >= 17, Kr >= 10): interface(showassumed=2): S^2 + (Art + Kr)^2;

S2 + (Art + Kr)2

with assumptions on Art and Kr

> assume(x, 'odd', y::integer, z >= 0), is(x + y, 'integer'); ⇒ true

> x:= 'x': unassign('y'), map(is, [x, y], 'integer'), is(z, 'nonnegative'); ⇒ [false, false], true

До 7-го релиза включительно оперативно переопределять режим идентификации переменных assume можно было переключателями функции Assumed Variables группы Options GUI.

По тестирующей процедуре is(x, Свойство) возвращается true-значение, если х-переменная обладает указанным вторым фактическим аргументом свойством, и значение false в противном случае. При невозможности идентифицировать для х-переменной свойство (например, если она по assume-процедуре свойствами не наделялась) возвращается FAIL-значение. Наконец, отменять приписанные х-переменной свойства можно посредством выполнения простой конструкции x:= 'x' либо вызовом процедуры unassign('x'); сказанное иллюстрируют последние примеры предыдущего фрагмента. При этом, проверять можно как конкретную отдельную переменную, так и выражение по нескольким ведущим переменным и набору искомых свойств.

Maple-язык поддерживает работу со свойствами шести основных групп, а именно:

1) имя свойства, например, continuous, unary;

2) большинство имен типов, например, integer, float, odd, even;

3) числовые диапазоны, например, RealRange(a, b), RealRange(-infinity, b),

RealRange( a, infinity), где a и b могут быть или числовыми значениями или Open(a), где a – числовое значение

4) AndProp(a, b, ...) – and-выражение свойств <a and b and ...>, где a, b, ... – свойства, оп- ределенные выше

5) OrProp(a, b, ...) – or-выражение свойств, где объект может удовлетворять любому из a, b, ... свойств

6) диапазон свойств p1 .. p2, где p1 и p2 - свойства. Данное свойство означает, что объект удовлетворяет по меньшей мере p2, но не более, чем p1; например, integer .. rational удовлетворяется integers/2.

За более детальной информацией по поддерживаемым Maple-языком свойствам остальных групп можно обращаться или к справке по пакету, или к книгам [8-14,78-86,88,105].

Механизм свойств, определяемый процедурой assume и сопутствующей ей группой процедур coulditbe, additionally, is, about, hasassumptions и addproperty, использует специальную глобальную _EnvTry-переменную для определения режима как идентификации у переменных приписанных им свойств, так и их обработки. При этом, данная переменная допускает только два значения: normal (по умолчанию) и hard, из которых указание второго значения может потребовать при вычислениях существенных временных затрат. В текущих реализациях пакета значение глобальной _EnvTry-переменной, определяющей режим обработки переменных, наделенных assume-свойствами, не определено, что иллюстрирует следующий достаточно прозрачный фрагмент:

> _EnvTry, about(_EnvTry); ⇒ _EnvTry

_EnvTry: nothing known about this object

> assume(V >= 64): about(V);

Originally V, renamed V~: is assumed to be: RealRange(64, infinity)

> `if`(is(V, RealRange(64, infinity)), ln(V) + 42, sqrt(Art + Kr)); ⇒ ln(V~) + 42 > assume(47 <= G, G <= 59): about(G);

Originally G, renamed G~: is assumed to be: RealRange(47, 59)

> `if`(is(G, RealRange(47, 59)), [10, 17, Sv, Art, Kr], Family(x, y, z)); ⇒ [10, 17, Sv, Art, Kr]

> assume(x >= 0), simplify(sqrt(x^2)), simplify(sqrt(y^2)); ⇒ x~, csgn(y) y

> sqrt(a*b), sqrt(a^2), assume(a >= 0, b <= 0), sqrt(a*b), sqrt(a^2); ⇒ a b, a2 , −a~ b~ I a~,

Механизм приписанных свойств является достаточно развитым и мощным средством как числовых вычислений, так и символьных вычислений и преобразований. Использование его оказывается весьма эффективным при программировании целого ряда важных задач во многих приложениях. Последние примеры предыдущего фрагмента иллюстрируют некоторые простые элементы его использования в конкретном программировании. Тогда как конкретный assume-механизм базируется на алгоритмах Т. Вейбеля и Г. Гоннета. С интересным обсуждением принципов его применения, реализации и ограничений можно довольно детально ознакомиться в интересных работах указанных авторов, цитируемых в конце справки по пакету (см. ?assume), и цитируемых в них многочисленных источниках различного назначения.

В ряде случаев требуется выполнить обмен значениями между переменными. Например, переменные х, у и z, имеющие значения 64, 59 и 39 должны получить значения 39, 59 и 64 соответственно. Следующая процедура varsort решает данную задачу.

varsort := proc() local a b c d k, , , , ;

`if`(nargs = 0, RETURN( ), assign(c = [ ])); d := proc(x y, )

try if evalf( )x < evalf( )y then true else false end if catch : `if`(map(type, {x, y}, 'symbol') = {true}, lexorder(x, y), true) end try

end proc ;

a := [seq `if` type(( ( args[ ]k , 'boolproc'), assign(' 'b = args[ ]k ),

`if`(type(args[ ]k , 'symbol'), op(args[ ]k , assign(' 'c = [op( )c , k])), NULL)), k = 1 .. nargs)];

a := sort(a, `if`(type(b, 'boolproc'), ,b d)); for k to nops(a) do assign(args[c k[ ]] = a k[ ]) end do

end proc

> x:=64: y:=59: z:=39: m:=vsv: n:=avz: p:=agn: varsort('x', 'z', 'y', 'm', 'p', 'n'), x, y, z, m, n, p;

39, 64, 59, agn, vsv, avz

> x:=64: y:=59: z:=39: m:=vsv: n:=avz: p:=agn: varsort('x', 'y', 'z', 'm', 'n', 'n'), x, y, z, m, n, p;

39, 59, 64, avz, vsv, agn

Вызов varsort('x', 'y', 'z', …) возвращает NULL-значение, обеспечивая обмен значениями между переменными x, y, z, … в порядке возрастания. Подробнее с возможностями данной процедуры можно ознакомиться в [41-43,103,109].

1.4. Типы числовых и символьных данных Mapleязыка пакета

Средства Maple-языка поддерживают работу как с простыми, так и сложными типами данных числового или символьного (алгебраического) характера. В первую очередь рассмотрим типы простых данных числового характера, предварив краткой информацией по очень важным встроенным функциям nops и op, непосредственно связанных со структурной организацией Maple-выражений. Первая функция возвращает число операндов выражения, заданного ее единственным фактическим аргументом. Тогда как вторая имеет более сложный формат кодирования следующего вида: op({ |n,|n..m,|<Список>,} <Выражение>)

где первый необязательный фактический аргумент определяет возврат соответственно: n-го операнда, с n-го по m-й операнды или операнды согласно Списка их позиций в порядке возрастания уровней вложенности Выражения. При этом, при n = 0 возвращается тип самого выражения, а в случае отсутствия указанного первым аргументом операнда инициируется ошибочная ситуация. Для случая n<0 выполняется соотношение op(n,V) ≡ op(nops(V) + n + 1, V), где V – выражение, а для неопределенного Id-идентификатора имеют место соотношения: op(0, Id) ⇒ symbol и op(1, Id) ⇒ Id. Отсутствие первого аргумента op-функции аналогично вызову вида op(1 .. nops(V), V). Для вывода структурной организации произвольного выражения может оказаться полезной конструкция вида: op('k', <Выражение>) $'k'=0 .. nops(<Выражение>)

вычисление которой возвращает последовательность типа и всех операндов первого уровня вложенности указанного выражения, как это иллюстрирует следующий пример:

> Art:= 3*sin(x) + 10*cos(y)/AV + sqrt(AG^2 + AS^2)*TRG: op('k', Art)$'k'=0 .. nops(Art);

10 cos(y) 2 + AS2 TRG

+, 3 sin( )x , , AG

AV

Ниже будет рассмотрено достаточно средств Maple-языка, ориентированных на задачи символьной обработки выражений, включая и те, которые базируются на их структурном ана-изе. Целый ряд средств для решения подобных задач предоставляет и наша Библиотека [103]. Нам же для дальнейшего будет пока вполне достаточно информации по встроенным функциям nops и op пакета.

Целые (integer); представляют собой цепочки из одной или более цифр, максимальная длина которых определяется используемой платформой ЭВМ: так для 32-битной она не превышает 524280 цифр, а для 64-битной – 38654705646 цифр. Целые могут быть со знаком и без: 1999, -57, 140642. На числах данного типа функции op и nops возвращают соответственно значение числа и значение 1, тогда как функция type идентифицирует их тип как integer, например:

> op(429957123456789), nops(429957123456789); ⇒ 429957123456789, 1

> type(429957123456789, 'integer'); ⇒ true

Действительные (float) с плавающей точкой; представляют собой цепочки из десятичных цифр с десятичной точкой в {начале|середине|конце} цепочки; числа данного типа допускают следующие два основных формата кодирования:

[<знак>]{<целое>.<целое>|.<целое>|<целое>.}

Float([<знак>]<мантисса>, [<знак>]<экспонента>) ≡

[<знак>]<мантисса>.{E|e}[<знак>]<экспонента>

В качестве мантиссы и экспоненты используются целые со знаком или без; при этом, мантисса может иметь любую длину, но экспонента ограничивается длиной машинного слова: для 32-битной платформы значение экспоненты не превышает целого 2147483647, а для 64-битной платформы – целого значения 9223372036854775807. Тогда как максимальное допустимое число цифр мантиссы аналогично максимальному допустимому числу цифр целого (integer) числа. Число цифр мантиссы, участвующих в операциях арифметики с плавающей точкой, определяется предопределенной Digits-переменной ядра пакета, имеющей по умолчанию значение 10. В книге [103] представлен ряд полезных средств, позволяющих оформлять числовые значения в принятых для документирования и печати форматах.

Второй способ кодирования действительных чисел применяется, как правило, при работе с очень большими или очень малыми значениями. Для конвертации значений в действительный тип используется evalf-функция, возвращающая значение float-типа. В вышеприведенных примерах применение данной функции уже иллюстрировалось; функция имеет простой формат кодирования evalf(<Выражение> [, n]) и возвращает результат float-типа вычисления выражения (действительного или комплексного) с заданной n-точностью (если она определена вторым фактическим аргументом). Maple испытывает затруднения при вычислениях уже целого ряда простых радикалов с рациональными степенями от отрицательных значений, если знаменатель экспоненты – нечетное число. В этом случае даже стандартная функция evalf оказывается бессильной совместно с использованием пакетного модуля RealDomain, что очень хорошо иллюстрируют довольно простые примеры, а именно:

> R:=(58 + (-243)^(1/5) + (-8)^(2/3))*(10 + (-1331)^(2/3) + (-8)^(2/3))/((63 +(-32)^(4/5) - (-27)^

(2/3))* (17 + (343)^(2/3) + (-32)^(3/5))); with(RealDomain): evalf(R), Evalf(R);

R := (58 + (-243)((14//55)) + (-8)(2(/32)/3))(10 + (-1331)(2/3()2/3) + (-8)(3(/52)/3))

(63 + (-32) − (-27) ) (17 + 343 + (-32) )

-0.7722517003 + 1.867646862 I, 1.961822660

Тогда как процедура Evalf, находящаяся в упомянутой Библиотеке [103], вполне успешно решает данную задачу, что и иллюстрируе данный пример в среде Maple 10.

Практически все встроенные функции пакета возвращают результаты float-типа, если хоть один из их аргументов получает значение данного типа. Автоматически результат операции возвращается float-типа, если один из операндов имеет данный тип. По умолчанию число цифр выводимого действительного числа равно 10, но в любое время может переопределяться в глобальной Digits-переменной ядра пакета. Примеры: 1.42, -.57, 17., 8.9, 2.9E-3, -5.6e+4, -5.4E-2. При этом, конструкции типа <целое>.{E|e}<целое> вызывают синтаксическую ошибку с диагностикой "missing operator or `;`".

Третьим способом определения действительных чисел является функция Float(<мантисса>, <экспонента>), возвращающая число float-типа с указанными мантиссой и экспонентой, например: Float(2006, -10); ⇒ 0.2006e-6. Иногда Float-функцию называют конструктором float-чисел.

Действительное число имеет два операнда: мантиссу и экспоненту, поэтому вызов функции op({1|2}, <число>) соответственно возвращает {мантиссу|экспоненту} указанного ее вторым аргументом числа, тогда как вызов функции nops(<число>) возвращает значение 2 по числу операндов; при этом, функция type идентифицирует тип таких чисел как float, например:

> op(2, 19.95e-10), op(1, 19.95e-10), nops(19.95e-10); ⇒ -12, 1995, 2 > type(19.95e-10, 'float'); ⇒ true

Вопросы арифметики с числами float-типа детально рассматриваются, например, в книгах [12,13] и в ряде других изданий, поэтому ввиду наших целей здесь они не детализируются. Впрочем, данный тип числовых выражений и так достаточно прозрачен.

Рациональные (rational); представляют собой числа (дроби), кодируемые в форме вида [<знак>] a/b, где a и b - целые числа; в частности, целые числа также рассматриваются пакетом в качестве частного случая рациональных, имеющих единичный знаменатель. Для перевода рациональных чисел в числа float-типа используется упомянутая выше evalf-функция, например: -2, 64, 59/47, -2006/9, evalf(59/47)=1.255319149. На числах данного типа функции op и nops возвращают соответственно значение числителя, знаменателя и значение 2 по числу операндов, тогда как функция type идентифицирует тип таких чисел как fraction или rational, например:

> op(350/2006), nops(350/2006); ⇒ 175, 1003, 2

> type(350/2006, 'fraction'), type(350/2006, 'rational'); ⇒ true, true

Для работы с рациональными числами Maple-язык располагает целым рядом функциональных средств, достаточно детально рассматриваемых ниже.

Комплексные (complex); представляют собой числа вида a+b*I (b ≠ 0), где a и b – числа рассмотренных выше трех типов, а I – комплексная единица (I = √-1). Части a и b комплексного числа называются соответственно действительной и мнимой; отсутствие второй делает число действительным. Примеры: -19.42 + 64*I, 88.9*I, 57*I/42. Для комплексных чисел различаются целые, действительные, рациональные и числовые в зависимости от того, какого типа их действительная и мнимая части. Например, число 64 - 42*I полагается комплексным целочисленным, тогда как числовое комплексное предполагает числовыми действительную и мнимую части. На числах комплексного типа функции op и nops возвращают соответственно значения действительной и мнимой частей, и число 2 операндов, тогда как функция type идентифицирует тип таких чисел как complex (может указываться и подтип), например:

> op(64 - 42*I), nops(64 - 42*I); ⇒ 64, -42, 2

> type(64 - 42*I, 'complex'('integer')); ⇒ true

Следует отметить, что Maple-языком некорректно распознается тип вычисления ряда комплексных выражений, ориентируясь только на наличие в вычисляемом выражении комплексной I-единицы. Следующий простой пример иллюстрирует вышесказанное:

> type(I*I, ‘complex’), type(I^2, ‘complex’), type(I^4, ‘complex’), type(52 + b*I, ‘complex’({‘symbol’, ‘integer’})), type(a + 57*I, ‘complex’({‘symbol’, ‘integer’})); true, true, true, true, true

> type(I*I, ‘complex1’), type(I^2, ‘complex1’), type(I^4, ‘complex1’), type(52 + b*I, ‘complex1’({‘symbol’, ‘integer’})), type(a + 57*I, ‘complex1’({‘symbol’, ‘integer’})); false, false, false, true, true

Согласно соглашениям пакета вызов функции type(x, complex) возвращает true, если x – выражение формы a+b*I, где a (при наличии) и b (при наличии) конечны, имея тип realcons. В принципе, с формальной точки зрения все нормально. Однако в целом ряде случаев необходимо точно идентифицировать комплексный тип, имеющий форму a + b*I при b ≠ 0. С этой целью нами был дополнительно определен тип complex1 [103], решающий данную задачу. В предыдущем фрагменте можно сравнить результаты тестирования на типы complex и complex1.

Булевские (boolean); представляют собой логические значения true (истина), false (ложь) и FAIL (неопределенная истинность). Третье значение используется в случае, когда истинность какого-либо выражения неизвестна. Функция type идентифицирует тип таких значений как boolean, например:

> map(type, [true, false, FAIL], ‘boolean’); ⇒ [true, true, true]

Язык Maple использует трехзначную логику для выполнения операций булевой алгебры. Булевы выражения образуются на основе базовых логических операторов {and, or, not} и операторов отношения {<, <=, >, >=, =, <> (не равно)}. Наша Библиотека [103] определяет ряд достаточно полезных средств, расширяющих стандартные средства пакета для работы с булевой алгеброй.

Константы (constant); представляют собой постоянные значения любого из вышерассмотренных пяти типов. Данные конструкции весьма прозрачны и особых пояснений, так же как и иллюстрирующих их примеров, не требуют.

Таким образом, в процессе организации вычислений в среде пакета пользователь имеет доступ к следующим четырем основным типам числовых данных:

(1) целые числа со знаком (1942; -324; 34567; -43567654326543; 786543278);

(2) действительные со знаком (19.95; -345.84; 7864.87643; -63776.2334643);

(3) рациональные со знаком (18/95; -6/28; -4536786/65932; 765987/123897);

(4) комплексные числа (48+53*I; 28.3-4.45*I; ½+5/6*I; -4543.87604+53/48*I)

Каждый из перечисленных типов числовых данных идентифицируется специальным идентификатором: integer – целые; float – действительные с плавающей точкой; rational, fraction – рациональные (дроби вида m/n; m, n – целые) и complex – комплексные числа. Каждый из этих идентификаторов может быть использован для тестирования типа переменных и выражений посредством type-функции, уже упоминаемой выше, но детально рассматриваемой ниже.

Для обеспечения работы с числовыми значениями Maple-язык располагает как функциями общего назначения, так и специальными, ориентированными на конкретный числовой тип. Например, по функциям ifactor, igcd, iperflow возвращается соответственно: разложение на целочисленные множители целого числа, наибольший общий делитель целых чисел и результат проверки целого числа на возможность представления его в виде np , где n и p – оба целые числа, например: [ifactor(64), iperfpow(625, 't'), igcd(42, 7)], t; [(2)6 , 25, 7], 2. Детально как с общими, так и специальными функциями работы с числовыми выражениями можно ознакомиться в книгах [12,103], в других изданиях, но наиболее полно в справке по пакету.

Наряду с десятичными числовыми значениями пакет поддерживает работу с бинарными, 8- и 16-ричными, а также произвольными q-ричными числами (q - основание системы счисления). Для преобразования чисел из одной системы счисления в другую служит функция convert языка, рассматриваемая ниже. Наряду с числовыми данными, пакет поддерживает работу с нечисловыми (символьными, алгебраическими) выражениями, характеризуемыми тем, что им не приписаны какие-либо числовые значения. С символьными данными и их обработкой познакомимся детальнее несколько позднее. Здесь лишь отметим базовый тип символьных данных – данные типов string и symbol.

Строка (string); любая конечная последовательность символов, взятая в верхние двойные кавычки; данная последовательность может содержать и специальные символы, как это иллюстрирует следующий простой пример:

“Dfr@t4#\78578”; “A_V_Z; A+G-N; “” 574%!@#$%”; “_Vasco&Salcombe_2006”

На строках функции op и nops возвращают соответственно саму строку и количество 1 операндов, тогда как функция type идентифицирует тип таких выражений как string, например:

> op(“123456”), nops(“123456”), type(“123456”, ‘string’); ⇒ “123456”, 1, true

Символ (symbol, name); любая конечная последовательность символов, взятая в верхние обратные кавычки; данная последовательность может содержать и специальные символы, как это иллюстрирует следующий простой пример:

`Dfr@t4#\78578`; `A_V_Z; A+G-N; “” 574%!@#$%`; `_Vasco&Salcombe_2006`; AVZ

Между тем, в отличие от строк, требующих обязательного ограничивания их двойными кавычками, для символов ограничения верхними обратными кавычками требуется только в том случае, когда они содержат специальные символы, например, пробелы. На символах функции op и nops возвращают соответственно сам символ и число 1 операндов, тогда как type-функция идентифицирует тип таких выражений как symbol либо name, например:

> op(`123456`), nops(`12345`), map2(type,`123456`, [‘symbol’, ‘name’]); ⇒ 12345, 1, [true, true]

В отличие от 4-го релиза последующие релизы Maple четко различает понятия символа и строки. Символьный тип играет основополагающую роль в символьных (алгебраических) вычислениях и обработке информации. Строчные данные, наряду с символьными, играют основную роль при работе с символьной информацией и Maple-язык располагает для работы с ними довольно развитыми средствами, которые с той или иной степенью полноты рассматриваются нами ниже. Немало дополнительных средств для работы с выражениями типов {symbol, name, string} представлено и нашей Библиотекой [103]. Многие из них позволяют весьма существенно упростить программирование целого ряда приложений в среде пакета Maple релизов 8 – 10.

Например, процедура _ON(expr) обеспечивает конвертацию алгебраического выражения expr в стилизованный формат, в котором каждое float-число представляется в виде n.m.

_ON := proc(expr) local a b c d p k, , , , , ; assign(a = cat " ",( convert(expr, 'string'), " " ,) b = {seq(convert(k, 'string'), k = 0 .. 9)}), assign(d = Search2(a, {"."}));

d := [seq `if`( (a[d k[ ] − 1] = " " and a[d k[ ] + 1] = "." or a[d[k] − 1] = "." and a[d[k] + 1] = " " or type(a[d[k] + 1], 'letter') or type(a[d k[ ] − 1], 'letter') or

member(a[d k[ ] + 1], b) and member(a[d k[ ] − 1], b), NULL, d k[ ]), k = 1 .. nops( )d )];

p := [1 $ (k = 1 .. nops(d))];

for k to nops(d) do if not member(a[d k[ ] − 1], b) then a := cat(a[1 .. d k[ ] − 1], "0", a[d k[ ] .. -1]); d := p + d

elif not member(a[d k[ ] + 1], b) then a := cat(a[1 .. d k[ ] − 1], a[d k[ ] + 1 .. -1]); d := d − p

end if

end do; came( )a

end proc

> R:= 8.*a + 47*I+(( .5 + 8.*a +c . d) + cos(6.*gamma + x-7.) - 3.*7^(-2.*a) + int(f(x), x=1. .. .5))/

(( 8.^a.b - .8*Pi )*sqrt( a - 3. ) + exp(8.*a - 87.*Pi + 16.*I) + array(1..2, 1..2, [[1., .2], [3., 4.]]));

0.5

0.5 + 8. a + (c d . ) + cos(6. γ + − x 7.) − 3. 7( 2. a) + ⌠ f( )x dx

1.

R := 8. a + 47 I +

(((8. )a . )b − 0.8 π) a − 3. + e(8. a − 87. π + 16. I) + 1.3. 0.24.  

> _ON(R);

0.5

0.5 + 8 a + (c d . ) + cos(6 γ + − x 7) − 3 7( 2 a) + ⌠ f( )x dx

1

8 a + 47 I +

(((8 )a . )b − 0.8 π) a − 3 + e(8 a − 87 π + 16 I) + 13 0.24 

При этом, результат, возвращаемый процедурой _ON, пригоден для непосредственных вычислений. Между тем, данная процедура носит описательный характер, не изменяя самой сути вычисления выражений.

Тогда как процедура Evalf(x) совместно с процедурой ffp устраняет затруднения Maple при вычислениях ряда простых радикалов х с рациональными степенями от отрицательных значений, если знаменатель их показателя – нечетное число [41-43,103,109].

Evalf := proc(E::algebraic) local a b c d h g k, , , , , , , ψ; assign(a = convert(E, 'string'), g = [ ]), assign(b = Search2(a, {")^("})); ψ := x → `if`(type(x, 'integer') or x = 0, parse(cat(convert(x, 'string'), ".")), x); if b = [ ] then nulldel(evalf(E, `if`(nargs = 2 and type(args[2], 'posint'), args[2], NULL)))

;

ψ(%)

else

for k in b do

c := a[nexts(a, k, "(", 1)[-1] .. nexts(a, k, ")")[-1]]; try d := map(convert, map(parse, SLD(c, "^")), 'fraction') catch : next end try ;

if type(d[1], 'negative') and type(d[2], 'fraction') and

type(denom(d[2]), 'odd') then if type(numer(d[2]), 'even') then g := [op(g), c = cat "(", [( c 3 .. -1])]

else g := [op(g), c = cat "(-1)*(", [( c 3 .. -1])] end if

end if

end do;

nulldel evalf parse(( ( SUB_S(g a, )),

`if`(nargs = 2 and type(args 2[ ], 'posint'), args 2[ ], NULL))); ψ (%)

end if

end proc

1.5. Базовые типы структур данных Maple-языка

Наряду с простыми данными Maple-язык поддерживает работу с наиболее распространенными структурами данных такими как последовательности, списки, множества, массивы и таблицы. Вкратце остановимся на каждой из перечисленных структур.

Последовательность (sequence) – последовательная структура, широко используемая пакетом для организации как ряда других типов структур данных, так и для разнообразных вычислительных конструкций, представляет собой базовую структуру данных и определяется конструкциями следующего весьма простого вида:

Sequence := B1, B2, B3, ..., Bn; Bj (j=1..n) – любое допустимое выражение языка

Последовательность не является ни списком, ни множеством, но она лежит в основе определения этих типов структур данных пакета. Пример: GLS:= 47, Gr, -64*L, `99n+57`, F. Структура типа последовательность выражений или просто последовательность (exprseq), как уже отмечалось, образуется (,)-оператором разделителя выражений (запятая) и представляет интерес не только в качестве самостоятельного объекта в среде Maple-языка, но и в качестве основы таких важных структур как: функциональная, список, множество и индексированная. Важным свойством данного типа структуры данных является то, что если ее элементы также последовательности, то результатом является раскрытая единая последовательность, как это иллюстрирует следующий простой пример:

> AV:= a, b, c, d, e: GS:= x, y, z: Sv:=2, 9: AG:= 1, 2, 3, AV, 4, Sv, 5, 6, GS; AG := 1, 2, 3, a, b, c, d, e, 4, 2, 9, 5, 6, x, y, z

Длина произвольной SQ-последовательности (число ее элементов) вычисляется по конструкции nops([SQ]), тогда как ее k-й элемент получаем посредством оператора выделения SQ[k]. Оператор выделения имеет весьма простой вид: Id[<Выражение>], где Id – идентификатор структуры типа массив, список, множество или последовательность; значение выражения Id[k] определяет искомый элемент структуры. Если Id не определен, то он возвращается индексированным, например:

> SQ:= V, G, S, Art, Kr, Arne: {SQ[4], nops([SQ]), R[S]}; ⇒ {6, R[S], Art}

> SQ:= [SQ]: SQ[6]:= Aarne: SQ:= op(SQ): SQ; ⇒ V, G, S, Art, Kr, Aarne

> Z:= SQ: whattype(SQ); ⇒ exprseq

> type(Z, 'exprseq');

Error, wrong number (or type) of parameters in function type > hastype(Z, 'exprseq');

Error, wrong number (or type) of parameters in function hastype

Так как по конструкции вида SQ[k]:=<Выражение> недопустимо присвоение заданного выражения k-му элементу SQ-последовательности, то для этих целей можно воспользоваться цепочкой Maple-предложений вида: SQ:=[SQ]:SQ[k]:= <Выражение>: SQ:=op(SQ):, как это иллюстрирует второй пример последнего фрагмента. При этом, следует иметь в виду, что тип последовательности тестируется только whattype-процедурой языка, т. к. при передаче последовательности в качестве аргумента другим тестирующим функциям она рассматривается как последовательность фактических аргументов. Последние три примера предыдущего фрагмента иллюстрируют сказанное. Для прямого тестирования структур типа `expressions sequence` нами была определена процедура typeseq, описанная в книге [103] и включенная в прилагаемую к ней Библиотеку [109]. Ниже дано несколько примеров ее применения, а именно:

> typeseq("Kr",Art,6,14,"RANS",IAN,Tallinn, 'seqn'(integer, string, symbol)); ⇒ true

> typeseq(a, b, 10, 17, 'seqn'), typeseq("Kr", Art, 10, 17, "RANS", IAN, Tallinn, Vilnius, 'seqn'), typeseq(G, [a], {b}, 61, 10/17, 'seqn'('integer', 'symbol', 'list', 'set', 'fraction'));

true, true, true

В определении различного назначения последовательностей важную роль играют так называемые ранжированные конструкции, образованные (..)-оператором ранжирования и имеющие следующий простой формат кодирования:

<Выражение_1> .. <Выражение_n>

где вычисляемые выражения определяют соответственно нижнюю и верхнюю границы ран-жирования с шагом 1. В зависимости от места использования ранжированной конструкции для значений выражений допускаются значения типов {float, integer}. Особо самостоятельного значения ранжированное выражение не имеет и используется в качестве ранжирующей компоненты во многих стандартных Maple-конструкциях (последовательностные структуры, суммирование, произведение, интегрирование и др.). В качестве самостоятельного применения оператор ранжирования можно использовать, например, с ||оператором конкатенации для создания последовательностей, например:

> n:= 1: m:= 9: SL:= x||(n .. m); ⇒ SL := x1, x2, x3, x4, x5, x6, x7, x8, x9

> A||("g" .. "s"); ⇒ Ag, Ah, Ai, Aj, Ak, Al, Am, An, Ao, Ap, Aq, Ar, As

> A||("м" .. "э"); ⇒ Ам, Ан, Ао, Ап, Ар, Ас, Ат, Ау, Аф, Ах, Ац, Ач, Аш, Ащ, Аъ, Аы, Аь, Аэ

> GS(x||(1 .. 5)):= (x1*x2*x5 + sin(x4 + x5))*(x1^2 + x2^2 + x3^2 + x4^2);

GS(x1, x2, x3, x4, x5) := (x1 x2 x5 + sin(x4 + x5)) (x1 + x2 + x3 + x4)

> A||(1 .. 3)||(4 .. 7); ⇒ A14, A15, A16, A17, A24, A25, A26, A27, A34, A35, A36, A37

Из приведенного фрагмента четко прослеживается одно существенное отличие бинарного ||-оператора конкатенации от других бинарных операторов Maple-языка. Если стандартным в языке является порядок вычисления выражений «слева направо», то для ||-оператора конкатенации используется обратный ему порядок вычислений. В последнем примере вычисляется сначала правый операнд, а затем левый. Более того, из второго и третьего примеров явствует, что наряду с числовыми значениями для ранжированной конструкции допускаются и буквы английского и национальных алфавитов, закодированные в строчном формате. На ранжированных выражениях функции op и nops возвращают соответственно последовательность их левых и правых частей, и число 2 операндов, тогда как функция type идентифицирует тип таких выражений как range, например:

> op(a .. b), nops(a .. b); ⇒ a, b, 2

> type("a" .. "z", 'range'), type(1 .. 64, 'range'), type(-1 .. -6.4, 'range'); ⇒ true, true, true

Детальнее структуры типа последовательность и оператор ранжирования будут рассматриваться ниже в различных контекстах, включая иллюстративные фрагменты. Здесь же мы лишь упомянем два функциональных средства Maple-языка, преднаначенных для определения последовательностных структур данных, а именно: $-оператор и seq-функция, которые имеют соответственно следующие наиболее общего вида форматы:

V(k) $ k = p .. n; ⇒ V(p), V(p + 1), ... ,V(n) seq(V[k], k = p .. n); ⇒ V(p), V(p + 1), ... ,V(n)

где V – допустимое Maple-выражение, в общем случае зависящее от к-переменной ранжирования. Следующий простой фрагмент иллюстрирует примеры структур типа последовательности, образованные $-оператором и seq-функцией Maple-языка:

> G(h) $ h = 9.42 .. 14.99; ⇒ G(9.42), G(10.42), G(11.42), G(12.42), G(13.42), G(14.42)

> seq(G(x), x = 9.42 .. 14.99); ⇒ G(9.42), G(10.42), G(11.42), G(12.42), G(13.42), G(14.42)

> 67 $ 15; ⇒ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67

> Gs $ h = -0.25 .. 7.99; ⇒ Gs, Gs, Gs, Gs, Gs, Gs, Gs, Gs, Gs

> cat(seq(x, x = "a" .. "z")); ⇒ "abcdefghijklmnopqrstuvwxyz"

> cat(seq(x, x = "A" .. "Z")); ⇒ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

> H:= [x, y, z, h, r, t, k, p, w, z, d]: H[s] $ s=1 .. 11; ⇒ x, y, z, h, r, t, k, p, w, z, d

> seq(H[s], s = 1 .. 11); ⇒ x, y, z, h, r, t, k, p, w, z, d

С учетом сказанного приведенный фрагмент весьма прозрачен и особых пояснений не требует. Более детально вопросы, связанные с $-оператором и seq-функцией, будут рассмотрены в связи с обсуждением функциональных средств языка. Здесь лишь отметим, что при всей близости обоих средств между ними существует различие, выражающееся в более универсальном характере seq-функции в плане ее применимости для генерации последовательностей, что будет проиллюстрировано ниже на примерах и детально рассмотрено в [12,41-43,103].

Список (list) – списочная структура, широко используемая пакетом для организации вычислений и обработки разнообразной информации, образуется посредством помещения последовательности выражений в квадратные скобки, формируя конструкцию следующего общего вида:

List := [B1, B2, B3, ..., Bn], где Bj (j=1 .. n) – любое допустимое выражение Maple-языка

Длина списка определяется числом входящих в него элементов, а идентичными полагаются списки, имеющие одинаковую длину и одинаковые значения соответствующих элементов. По конструкции вида List[n] можно получать значение n-го элемента списка с List-идентификатором, а на основе вызова функции nops(List) – число его элементов. При этом, по вызову функции op(List) можно конвертировать списочную List-структуру в последовательность. Замена к-го элемента списка L выполняется по конструкции L[k] := <Выражение>, тогда как для его удаления используется конструкция subsop(k = NULL, L). Функция type идентифицирует тип списочных структур как list. Следующий простой пример иллюстрируют вышесказанное:

> L:=[sqrt(25),September,2,2006,GS]: [L[4],nops(L)],op(L); ⇒ [2006,5], 5,September,2, 2006,GS

> L[2]:= October: L; L:= subsop(3 = NULL, L): L, type(L, 'list'); ⇒ [5, October, 2, 2006, GS] [5, October, 2006, GS], true

Между тем, для удаления к-го элемента списка L с последующим обновлением списка «на месте» можно использовать конструкцию вида L[k] := 'NULL', весьма удобную в целом ряде приложений. Однако данный подход, корректно работая в одиночных вычислениях, не дает искомого результата в циклических. Поэтому во втором случае после каждого вычисления вида L[k]:= 'NULL' следует использовать операцию присвоения L:= L. Следующий весьма простой фрагмент иллюстрирует вышесказанное:

> restart; L:= [q, w, e, r, t, y, u, j, o, p, a, d, g, h, k, z, x]: L[10]:= 'NULL': L;

[q, w, e, r, t, y, u, j, o, a, d, g, h, k, z, x]

> L:=[q,w,e,r,t,y,u,j,o,p,a,d,g,h,k,z,x]: while L <> [] do L[1]:= 'NULL': L:= L end do: L ⇒ []

На основе предложенного приема можно предложить несложную процедуру mlist, обновляющую список на месте. Ее исходный текст и примеры применения даются ниже.

> L:= [q,w,e,r,t,y,u,j,o,p,d,a,s,k,x,z,y]: subsop(5 = VSV, L), subsop(10 = NULL, L), L;

[q, w, e, r, VSV, y, u, j, o, p, d, a, s, k, x, z, y], [q, w, e, r, t, y, u, j, o, d, a, s, k, x, z, y],

[q, w, e, r, t, y, u, j, o, p, d, a, s, k, x, z, y]

> mlist:= proc(L::uneval, a::posint, b::anything) if type(eval(L), 'list') then if belong(a, 1 ..

nops(eval(L))) then L[a]:= b; L:=eval(L); NULL else error "in list <%1> does not exist

element with number %2", L, a end if else error "1st argument should be a list but had received <%1>", whattype(eval(L)) end if end proc;

mlist := proc(L::uneval, a::posint, b::anything) if type(eval(L), 'list') then if belong(a, 1 .. nops(eval(L))) then L a[ ] := b; L := eval(L); NULL

else error "in list <%1> does not exist element with number %2", L a,

end if

else error

"1st argument should be a list but had received <%1>"whattype(, eval(L)) end if

end proc

> mlist(L, 10, 'NULL'), L; ⇒ [q, w, e, r, t, y, u, j, o, d, a, s, k, x, z, y]

> mlist(L, 16, 'NULL'), L; ⇒ [q, w, e, r, t, y, u, j, o, d, a, s, k, x, z]

> mlist(L, 3, AVZ), L; ⇒ [q, w, AVZ, r, t, y, u, j, o, d, a, s, k, x, z]

> L:= [q,w,e,r,t,y,u,j,o,p,d,a,s,k,x,z,y]: while L <> [] do mlist(L,1,'NULL') end do: L; ⇒ [] > mlist(L, 64, AGN), L;

Error, (in mlist) in list <L> does not exist element with number 59 > mlist(B, 64, AGN), L;

Error, (in mlist) 1st argument should be a list but had received <symbol>

Вызов процедуры mlist(L, a, b) возвращает NULL-значение, заменяя a-й элемент списка L на Maple-выражение b; при этом, обновление списка производится «на месте». Тогда как вызов процедуры mlist(L, a, 'NULL') также возвращает NULL-значение, удаляя a-й элемент из списка L и обновляя список «на месте». В ряде приложений процедура mlist представляется достаточно полезной.

Списки могут иметь различный уровень вложенности, определяя различного рода структуры данных и конструкции, как это иллюстрирует следующий простой пример:

> Avz:= [[[1, 2, 3], [5, 6]], [6, G], V, S]: op(Avz), nops(Avz); ⇒ [[1, 2, 3], [5, 6]], [6, G], V, S, 4

Для вложенных списков, чьи элементы имеют одинаковую длину, Maple определяет тип listlist, играющий весьма важную роль при организации индексированных структур (матрица, массив и др.). Нами дополнительно определен тип nestlist [103], характеризующий более общий тип вложенности списков, например:

> Avz:= [[[1, 2, 3], [5, 6]], [6, G], V, S]: type(Avz, 'listlist'), type(Avz, 'nestlist'); ⇒ false, true > Agn:= [[1, 2, 3], [a, b, c], [x, y, z]]: type(Agn, 'listlist'), type(Agn, 'nestlist'); ⇒ true, true Пустой список обозначается как L0:=[] и nops(L0); ⇒ 0, op(L0); ⇒ NULL. Maple-язык располагает весьма обширным набором функциональных средств для работы со списочными структурами, которые будут рассматриваться нами довольно детально ниже. Пакетный модуль ListTools содержит набор средств для работы со списками. В свою очередь, наша Библиотека [103] также содержит ряд полезных средств для такого типа структур.

Списочные структуры являются весьма широко используемыми пакетом объектами (исходные и выходные данные, управление порядком вычислений, представление массивов, матриц и тензоров и др.). При этом, в достаточно широких пределах допускается сочетание их с другими типами данных и структур данных.

Множество (set) – структура данных, весьма широко используемая пакетом, в первую очередь, для организации вычислений в традиционном теоретико-множественном смысле, образуется посредством помещения последовательности в фигурные скобки, формируя конструкцию следующего простого вида:

Set := {B1, B2, B3, ..., Bn}, где Bj (j=1 .. n) – любая допустимая конструкция Maple-языка

Мощность множества определяется числом входящих в него элементов, а идентичными полагаются множества, имеющие одинаковый набор вычисленных значений элементов без учета их кратности. По конструкции вида Set[n] можно получать значение n-го элемента множества с Set-идентификатором, а на основе вызова функции nops(Set) - число его элементов. Более того, по вызову функции op(Set) можно конвертировать Set-множество в последовательность. При этом, функция type идентифицирует тип множественных структур как set. Следующий пример иллюстрируют вышесказанное:

> Set1:= {q,6/3,e,r,5,t,w}: Set2:= {q,w,e,r,2,t,w,sqrt(25),r,q,q}: [op(Set1)], [op(Set2)],

[nops(Set1)], [nops(Set2)], Set2[5]; ⇒ [2, 5, r, q, e, t, w], [2, 5, r, q, e, t, w], [7], [7], e

Из приведенного примера можно заметить ряд особенностей применения функций op и nops к структуре данных set-типа. Прежде всего, производится вычисление элементов множества и их упорядочивание согласно соглашениям пакета, поэтому порядки элементов исходного множества и результата его вычисления могут не совпадать. Это же относится и к мощности множества - в результате вычисления его элементов одинаковые результаты сводятся к одному, например: S := {56/2, 7*4, 28, 7*2^2}: nops(S); ⇒ 1. Пустое множество обозначается как S0:= {} и nops(S0); ⇒ 0, op(S0); ⇒ NULL.

Maple-язык поддерживает ряд операций над множествами, аналогичных классическим теоретико-множественным операциям, включая базовые операции объединения (union), разности (minus) и пересечения (intersect) множеств, например:

> S1:= {q,r,64,t,w,x,h,z}: S2:= {h,n,v,x,59,z,k,s}: S1 union S2, S1 intersect S2, S2 minus S1;

{59, 64, x, h, s, z, n, r, v, q, t, w, k}, {x, h, z}, {59, s, n, v, k}

Множества могут иметь различный уровень вложенности, определяя различного рода структуры данных и конструкции. Нами дополнительно определен тип setset [103], аналогичный стандартному типу listlist для случая списков, например:

> map(type, [{{}}, {{},{}}, {{{7}}}, {{a, b}, {c, d}}, {{a, b}, {c}}, {{10, 17}, {64, 59}, {39, 44}}], 'setset');

[true, true, true, true, false, true]

Ввиду принципиально различных принципов упорядочения элементов списка и множества для замены элементов множества можно использовать два способа, а именно: (1) по номеру элемента и (2) по его значению, как это иллюстрирует следующий фрагмент:

(1) S := S minus {S[k]} union {<Выражение>} (2) S := subs(Sk = <Выражение>, S)

где S – произвольное множество и S[k], Sk – его к-й элемент и значение к-го элемента соответственно. Конкретные примеры иллюстрируют применение обоих способов:

> S:= {q, r, 64, t, w, x, h, z}; ⇒ S := {64, x, h, z, r, q, t, w}

> S:= S minus {S[4]} union {Avz}; ⇒ S := {64, x, h, r, q, t, w, Avz}

> S:= subs(Avz = Agn, S); ⇒ S := {64, x, h, r, q, t, w, Agn}

> S:= subs(Agn = NULL, S); ⇒ S := {64, x, r, h, q, t, w}

Одной из наиболее полезных и часто используемых над объектами типов set и list является map-функция Maple-языка, имеющая в общем виде формат кодирования:

map(F, f(x, y, z, ...), a, b, c, …) ⇒ f(F(x, a, b, c, …), F(y, a, b, c, …), F(z, a, b, c, …), ...)

где F – некоторая функция или процедура, f – выражение от переменных x, y, z, ... и a, b, c, … – некоторые выражения, и возвращающая результат применения функции или процеду-ры F к каждому аргументу f(x, y, z, ...)-конструкции (а в более общей постановке, к каждому операнду выражения), как это иллюстрирует следующий простой фрагмент:

> map(F, [x, y, z], a, b, c, d); ⇒ [F(x, a, b, c, d), F(y, a, b, c, d), F(z, a, b, c, d)]

> map(F, {x, y, z}, a, b, c, d); ⇒ {F(x, a, b, c, d), F(y, a, b, c, d), F(z, a, b, c, d)}

> map(F, f(x, y, z), a, b, c, d); ⇒ f(F(x, a, b, c, d), F(y, a, b, c, d), F(z, a, b, c, d))

> Seq:= x, y, z: map(F, Seq, a, b, c, d); ⇒ F(x, y, z, a, b, c, d)

Между тем, если по map(F, V, a1, ..., an)-функции применяется определенная ее первым фактическим F-аргументом функция к каждому операнду V-выражения с передачей ей дополнительных фактических aj-аргументов (j=1..n), то по ее модификации – функции map2 вида map2(F, a1, V, a2, ..., an) производится применение к V-выражению F-функции со специфическим a1-аргументом и возможностью передачи ей дополнительных aj-аргументов (j=2 .. n), что является существенным расширением map-функции. Следующий пример иллюстрирует принцип формального выполнения map2-функции:

> map2(F, a1, [x,y,z], a, b, c, d, e); ⇒ [F(a1, x, a, b, c, d, e),F(a1, y, a, b, c, d, e),F(a1, z, a, b, c, d, e)]

> map2(S, x, G(a, b, c), y, z, h, t); ⇒ G(S(x, a, y, z, h, t), S(x, b, y, z, h, t), S(x, c, y, z, h, t))

> map(F, W, x, y, z, t, h), map2(F, W, x, y, z, t, h); ⇒ F(W, x, y, z, t, h), F(W, x, y, z, t, h)

Из данного фрагмента несложно усматривается не только общий принцип выполнения функции map2, но и ее эквивалентность map-функции на определенных наборах аргументов. И выше, и в дальнейшем обе эти функции достаточно часто используются в иллюстративных примерах, лучше раскрывая припцип своего выполнения. Нами был определен ряд новых процедур map3, map4, map5, map6, mapN, mapLS, mapTab (расширяющих возможности функций map и map2), описанных в нашей книге [103] и включенных в состав прилагаемой к ней Библиотеке [109]. Maple-язык располагает обширным набором средств для работы с множествами и списками, которые будут рассматриваться нами детальнее ниже в различных контекстах. Более того, данный набор дополнен нашими средствами работы со списочными структурами [103,109].

Массив (array) – структура данных, весьма широко используемая Maple, в первую очередь, при работе с матричными и векторными объектами. Массив представляет собой определенное обобщение понятия списочной структуры, когда ее элементам приписываются индексы, идентифицирующие местоположение элементов в структуре. При этом, размерность массива может быть более единицы, а сами индексы элементов могут принимать как положительные, так и отрицательные значения, например:

> M:= array(-10 .. 16, [k$k = 1 .. 27]): M[-7], M[-1], M[0], M[16]; ⇒ 4, 10, 11, 27

Характерной чертой массива является возможность переопределения его элементов, не затрагивая всей структуры в целом. В общем случае М-массив определяется конструкцией следующего достаточно простого вида:

M := array(<Индексная функция>, <Размерность>, <Начальные значения>) где Индексная функция определяет специальный индексный атрибут (например, symmetric атрибут определяет соотношение M[n, m]=M[m, n]). Размерность задается в виде интервалов “а .. в” по каждому из индексов массива и Начальные значения задают исходные значения для элементов массива. При этом, следует иметь в виду, что Maple-язык автоматически не определяет элементы массива, поэтому сразу же после создания массива его элементы являются неопределенными, например: a:= array(1..3): print(a); ⇒ [a1, a2, a3]. Обращение к массиву производится по его идентификатору, а к его элементам в виде индексированного идентификатора, например: print(M); M[5, 6, 2]. Вывод массива на печать производится по print-функции, тогда как по функциям eval и evalm не только предоставляется возможность вывода содержимого массива, но и возврата его в качестве результата для возможности последующего использования в вычислениях. Применение данных функций проиллюстрировано ниже. Ни один из трех аргументов функции array не является обязательным, однако должен присутствовать по меньшей мере один из двух последних для возможности построения массива. Наиболее простой формат array-конструкции имеет следующий вид:

M := array(J11 .. J1n, J21 .. J2n, ..., Jm1 . .Jmn {, []| })

определяя пустой (неопределенный) М-массив mxn-размерности. Переопределение элементов M-массива производится по конструкции вида M[p, k, h, ...]:=<Выражение>, которая остается действительной и для списочных структур. В целом ряде случаев при определении массива целесообразно сразу же задавать начальные значения для его элементов полностью или частично, что иллюстрирует следующий простой фрагмент:

> G:= array(1..3, 1..3, []): G[1,1]:= 42: G[1,2]:= 47: G[1,3]:= 67: G[2,1]:= 89: G[2,2]:= 96: G[2,3]:= 99: G[3,1]:= 95: G[3,2]:= 59: G[3,3]:= 62: print(G);

42 8995 47 9659 67 9962 

> assign(MMM = [eval(G), evalm(G)]), MMM, sum(MMM[k], k = 1..2);

  42 9589 47 5996 67 9962 ,  42 8995 47 9659 67 9962   , 2 _addmultmp

> eval(_addmultmp), map(sqrt, _addmultmp);

 428995 479659 679962 ,   

> S:= array(1..3, 1..3, [[a, b, c], [d, e, f], [g, h, k]]): map([eval, evala, evalm], S);

[[[ad d dg g g,, ,, ,a, a]]] [[[bh h he e e,,, ,b,, b]]] [[[ck k kf f f,, ,, ,c, c]]]

> Art:=array(symmetric, 1..4, 1..4): Art[1, 1]:= 42: Art[1, 2]:= 47: Art[1, 3]:= 67: Art[1, 4]:= 62:

Art[2, 2]:= 57: Art[2, 3]:= 89: Art[2, 4]:= 96: Art[3, 3]:= 52: Art[3, 4]:= 99: Art[4, 4]:= 9:

> whattype(eval(Art)), type(Art, 'array'); ⇒ array, true

> [op(0, eval(Art))], [op(1, eval(Art))], [op(2, eval(Art))], [op(3, eval(Art))];

[array], [symmetric], [1 .. 4, 1 .. 4], [[(1, 1) = 42, (2, 2) = 57, (4, 4) = 9, (1, 2) = 47, (3, 3) = 52, (2, 4) = 96, (2, 3) = 89, (1, 4) = 62, (1, 3) = 67, (3, 4) = 99]]

Как следует из приведенного фрагмента в первом примере определяется пустой G-массив с последующим прямым определением его элементов путем присваивания; в качестве разделителей предложений выбрано двоеточие, чтобы не загромождать документ выводом промежуточных результатов. Затем над полученным G-массивом производятся простые операции, о которых говорилось выше. Во втором примере определяется S-массив той же (3х3)-размерности с одновременным заданием начальных значений для всех его элементов. Результаты определения массива используются для выполнения нестандартной процедуры, использующей рассмотренные выше функции и которая может оказаться полезной в практической работе с объектами типа array. Там же иллюстрируется применение map-функции для поэлементной обработки массива. При определения начальных значений элементов непосредственно в array-конструкции они представляются в виде вложенного списка listlist-типа, структура которого должна строго соответствовать структуре определяемого массива, как это иллюстрируется фрагментом. В качестве встроенных индексных атрибутов пакет использует: symmetric, antisymmetric, sparse, diagonal, identity и ряд определяемых пакетными модулями, которые детально обсуждаются при рассмотрении матричных объектов, например, в нашей книге [12].

При этом, массив одновременно выводится и возвращается по функциям evalm и eval, тогда как по print-функции производится только вывод массива на экран. Предыдущий фрагмент иллюстрирует сказанное. Наряду с этим, примеры фрагмента иллюстрируют использование функции op для получения типа Art-объекта, индексной функции (первый аргумент array-функции), размерности массива (второй аргумент) и содержимого всех начальных значений для входов массива (третий аргумент). Иллюстрируется и применение map-функции для поэлементной обработки массива.

Ниже обсуждение структур данных array-типа будет детализироваться и рассматриваться на протяжении последующих глав книги. При этом, следует иметь в виду то обстоятельство, что частным случаем понятия массива являются векторы (одномерный массив) и матрицы (двухмерный прямоугольный массив), для обеспечения работы с которыми Maple-язык располагает достаточно развитым набором средств, прежде всего, определяемых пакетным модулем linalg, ориентированным на решение задач линейной алгебры [11-14,80-89]. Ниже этот вопрос будет рассмотрен несколько детальнее.

Массив hfarray-типа . Для обеспечения численных вычислений на основе машинной арифметики с плавающей точкой (МАПТ) Maple-язык поддерживает новый тип структуры данных – массивы hfarray-типа, определяемые hfarray-функцией формата вида:

Mhf := hfarray({<Размерность>}{, <Начальные значения>})

где назначение формальных аргументов функции полностью аналогично случаю функции array; при этом, оба аргумента необязательны. Размерность определяется одним либо несколькими ранжированными выражениями, при ее отсутствии используется установка по умолчанию. Пределы изменения индексов по каждому измерению массива определяются используемой платформой и для 32-битной платформы находятся в диапазоне от -2147483648 до 2147483647 и для 64-битной платформы от -9223372036854775808 до 9223372036854775807.

Данные массивы составляют специальный hfarray-тип, распознаваемый тестирующими функцией type и процедурой whattype. Массивы данного типа в качестве значений своих элементов содержат числа с плавающей точкой двойной точности стандарта IEEE-754. При этом, поддерживаются три специальные значения данного стандарта: +Infinity, Infinity и Quiet NaN, последнее из которых определяется как `undefined`. Для hfarrayфункции в качестве начальных значений могут выступать любые допустимые Mapleвыражения, результатом вычисления которых являются числа float-типа, или значения undefined, infinity, -infinity. Полностью массивы hfarray-типа возвращаются и выводятся на экран соответственно по функциям eval и print. Следующий фрагмент иллюстрирует вышесказанное:

> МАПТ:= hfarray([evalf([sqrt(10), sqrt(17)]), evalf([sqrt(64), sqrt(59)])]);

3.16227766000000000 4.12310562600000008

МАПТ :=  

 8. 7.68114574799999960

> МАПТ:= hfarray([[sqrt(10), sqrt(17)], [sqrt(64), sqrt(59)]]); Error, unable to store '10^(1/2)' when datatype=float[8]

> type(МАПТ,'hfarray'), type(МАПТ,'array'), whattype(eval(МАПТ)); ⇒ true, false, hfarray

> eval(МАПТ), print(МАПТ);

3.162277660000000008. 4.123105626000000087.68114574799999960  

3.162277660000000008. 4.123105626000000087.68114574799999960  

> evalf(array([[sqrt(10), sqrt(17)], [sqrt(64), sqrt(59)]]));

3.1622776608. 4.1231056267.681145748  

Структуры данных hfarray-типа широко используются с evalhf-функцией, поддерживающей МАПТ, и достаточно детально рассматриваются, например, в [11,12]. При этом, следует иметь в виду, что работа с такого типа массивами вне рамок МАПТ не эффективна, ибо требуется выполнение соглашений между МАПТ и Maple-арифметикой с плавающей точкой. Последний пример фрагмента иллюстрирует различия между массивами типов array и hfarray при одной и той же установке предопределенной Digits-переменной пакета. Тогда как второй пример фрагмента иллюстрирует недопустимость определения элементов hfarray-массива типами данных, отличными от extended_numericтипа (обобщение типов numeric, undefined либо infinity, -infinity). Для устранения данного недостатка используется evalf-функция.

Таблица (table) – структура данных, весьма широко используемая Maple, прежде всего, при работе с различного рода табличными объектами. Таблица представляет собой определенное обобщение понятия двумерного массива, когда в качестве значений индексов массива могут использоваться не только целочисленные значения, но и произвольные выражения, в первую очередь, символьные и строчные значения в качестве названия строк и столбцов. Характерной чертой таблицы является возможность работы со структурами данных, включающими естественные нотации (фамилии, имена, названия и т.д.). Итак, в общем случае Т-таблица определяется конструкцией следующего простого формата:

T := table(<Индексная функция>, <Список/множество начальных значений>) где Индексная функция определяет специальный индексный атрибут (например, symmetricатрибут), аналогичный случаю массива, и второй аргумент определяет исходные значения для элементов таблицы (ее входы и выходы). Пустая таблица определяется по конструкции вида T:= table(); при этом, первый аргумент table-функции необязателен.

Второй аргумент table-функции определяется, как правило, в виде списка или множества, элементами которого могут быть как отдельные допустимые Maple-выражения, так и уравнения, т.е. конструкции вида “A=B”. Левые А-части уравнений выступают в качестве идентификаторов строк таблицы и определяют ее входы, а правые – выходы, т. е. по конструкции формата T[<Вход>] возвращается строка-выход, отвечающая указанному в качестве значения аргумента Входу. Поэтому, если T:=table([X1=Y1, X2=Y2, ...]), то T[Xk]; ⇒ Yk. Если в списке/множестве начальных значений хоть один из элементов не является уравнением, то Maple в качестве входов в таблицу использует целые неотрицательные числа (1 .. <число строк>), располагая при этом строки таблицы в определенном порядке, отличном от естественного. Это одна из причин, почему в качестве элементов второго аргумента функции table следует кодировать уравнения, т.е. это не тот случай, когда решение вопроса стоит отдавать на откуп пакету. В качестве правых В-частей уравнений списка/множества функции могут выступать произвольные Maple объекты, позволяя создавать достаточно сложные табличные структуры данных, примером чего может служить следующий весьма простой фрагмент:

> T:= table([A=[AV, 42, 64, 350], B=[42, 64, array([[RANS, IAN], [REA, RAC]])],

C=array([[{G, S}, {Kr, Ar}], [{Vasco}, {Salcombe}]])]): eval(T);

table([C = {{VascoS, G}} {Salcombe{Kr, Ar} }, B = 42 64, , RANSREA

A = [AV, 42 64 350, , ]

])

IAN,

RAC

При этом, более простым способом создания таблицы является непосредственное присвоение индексированной переменной значений, как это иллюстрирует следующий весьма простой фрагмент:

> Tab[Grodno]:= 1962: Tab[Tartu]:= 1966: Tab[Tallinn]:= 1999: Tab[Gomel]:= 1995:

Tab[Moscow]:= 1994: eval(Tab), print(Tab);

table([Grodno = 1962, Tartu = 1966, Tallinn = 1999, Gomel = 1995, Moscow = 2006]) table([Grodno = 1962, Tartu = 1966, Tallinn = 1999, Gomel = 1995, Moscow = 2006])

> (Tab[Grodno]-Tab[Tallinn]-Tab[Moscow])/(Tab[Tartu]-Tab[Gomel]-350); ⇒ 2043/379 > Tab[Grodno], Tab[Tartu], Tab[Tallinn], Tab[Gomel], Tab[Moscow];

1962, 1966, 1999, 1995, 2006

> whattype(Tab), whattype(eval(Tab)), type(Tab, 'table'), type(eval(Tab), 'table'), map2(type, Tab, ['matrix', 'array']); ⇒ symbol, table, true, true, [false, false]

> op(0, eval(Tab)), [op(1, eval(Tab))], op(2, eval(Tab)); table, [], [Grodno = 1962, Tartu = 1966, Tallinn = 1999, Gomel = 1995, Moscow = 2006]

Обращение к таблице производится по ее идентификатору, а к ее элементам в форме индексированного идентификатора, например: Т[С]. Вывод таблицы на печать производится по функции print (применение которой проиллюстрировано выше); это же относится и к выводу ее отдельных выходов, если они не являются конструкциями базовых типов {число, строка, список, множество}. При этом, подобно массиву таблица одновременно выводится и возвращается по функции eval, тогда как к ней не применимы функции evalm и evala. Приведенный выше фрагмент иллюстрирует сказанное. Наряду с этим, примеры фрагмента иллюстрируют использование op-функции для получения типа Tab-объекта, индексной функции (первый аргумент table-функции) и содержимого всех входов таблицы. По причине отсутствия индексной функции вызов op(1, eval(Tab)) возвращает NULL-значение.

Наиболее простой формат table-функции имеет следующий вид:

T := table([X1, X2, X3, ..., Xn]) или T := table({X1, X2, X3, ..., Xn})

определяя Т-таблицу из n строк, идентифицируемых входами-числами. Переопределение элементов Т-таблицы производится по конструкциям вида: Т[<вход>]:=<Выражение>, которые работают и со списочными структурами. Для удаления из Т-таблицы выхода (соответствующего заданному входу) выполняется следующая конструкция: Т[вход]:=NULL, тогда как для удаления всех выходов произвольной Т-таблицы достаточно выполнить следующее простое Maple-предложение цикла:

for k in [<Список входов>] do T[k]:= NULL end do:

либо эквивалентное ему выражение следующего вида:

eval(parse(convert(subs(F = null, mapTab(F, T, 1)), 'string')))

где null и mapTab – процедуры из Библиотеки, описание которых находится в [103]:

> Tab[Grodno]:= 1962: Tab[Tartu]:= 1966: Tab[Tallinn]:= 1999: Tab[Gomel]:= 1995: Tab[Moscow]:= 2006: eval(parse(convert(subs(F = null, mapTab(F, Tab, 1)), 'string'))); table([Tartu = (), Tallinn = (), Gomel = (), Moscow = (), Grodno = ()])

При работе с массивами и таблицами наиболее часто используемыми являются две функции indices и entries, имеющие следующий простой формат кодирования; {indices|entries}(T), где Т – массив или таблица

и возвращающие индексы/входы и соответствующие им элементы/выходы массива/таблицы Т соответственно, как это иллюстрирует следующий простой фрагмент:

> Tab[Grodno]:= 1962: Tab[Tartu]:= 1966: Tab[Tallinn]:= 1999: Tab[Gomel]:= 1995:

Tab[Moscow]:= 2006: indices(Tab), entries(Tab);

[Tartu], [Tallinn], [Gomel], [Moscow], [Grodno], [1966], [1999], [1995], [2006], [1962]

> A:= array([[a, b, h], [c, d, k], [x, y, z]]): indices(A), entries(A);

[1, 1], [2, 2], [2, 3], [2, 1], [3, 1], [3, 2], [1, 2], [1, 3], [3, 3], [a], [d], [k], [c], [x], [y], [b], [h], [z]

> with(Tab); eval(%);

[Gomel, Grodno, Moscow, Tallinn, Tartu]

[1995, 1962, 2006, 1999, 1966]

> Tab1[1942]:= 1962: Tab1[2006]:= 1966: type(Tab1, 'table'); with(Tab1); ⇒ true Error, (in pacman:-pexports) invalid arguments to sort

> map(op, [indices(Tab)]), map(op, [entries(Tab)]);

[Tartu, Tallinn, Gomel, Moscow, Grodno], [1966, 1999, 1995, 2006, 1962]

В частности, последний пример фрагмента представляет конструкции, полезные при работе, прежде всего, с таблицами, и позволяющие представлять их входы и выходы в виде списка. Во фрагменте (пример 3) проиллюстрировано также использование процедуры with (применяемой, как правило, лишь с пакетными и программными модулями) для получения списка входов таблицы. При этом, следует учитывать, что если в качестве входов Т-таблицы выступают значения {symbol|name}-типа, то по выхову with(T) возвращается их отсортированный лексикографически список, в противном случае возникает ошибочная ситуация, как это иллюстрирует пример 4 предыдущего фрагмента.

Имея многочисленные приложения, табличные структуры используются и для организации пакетных и/или библиотечных модулей. Такой подход особенно широко использовался в ранних релизах пакета и об этом детальнее будет сказано ниже. Представим здесь один простой пример такого подхода. В этом случае входами таблицы Т являются имена процедур, а ее выходами – соответствующие им определения. Тогда вызов таким образом погруженных в табличную структуру процедур принимают следующий простой вид, а именно: Т[имя](Аргументы). Следующий фрагмент иллюстрирует сказанное:

> T:= table([sr = (() -> `+`(args)/nargs), (ds = (() -> sqrt(sum((args[k] - sr(args))^2, k=1..nargs)/nargs)))]);

( )

T sr  nargs  ds ( ) → nargs k = 1 (argsnargsk − sr args( ))2 ]) := table([ = ( ) → `+` args , =

> with(T), 6*T[sr](64,59,10,17,39,44), 6*T[ds](64,59,10,17,39,44); ⇒ [ds sr, ], 233, 14249

> save(T,"C:\\Temp\\lib.m"); restart; read("C:\\Temp\\lib.m"); with(T); ⇒ [ds, sr]

> 6*T[sr](64,59,10,17,39,44), 6*T[ds](64,59,10,17,39,44); ⇒ 233, 14249

Данный подход эффективен, например, когда требуется создавать средства модуля пакета табличного типа поэтапно – экспорты (входы таблицы). В этом случае одноименные входы заменяют соответствующие им выходы, тогда как новые входы просто добавляются в таблицу. Данный подход имеет и другие привлекательные особенности.

Относительно структур типа массив (array) и таблица (table) следует сделать одно существенное пояснение. Все используемые Maple структуры, кроме этих двух, обладают свойством ненаследования, т.е. для них справедливы следующие соотношения:

X:= a: Y:= X: Y:= b: [X, Y]; ⇒ [a, b]

т.е. присвоение X-значения Y-значению с последующим переопределением второго не изменяет исходного X-значения. В случае же структур типа массив и таблица это вполне естественное свойство не соблюдается. Поэтому Maple-язык располагает специальной процедурой copy(<Массив/Таблица>), позволяющей создавать копии указанного массива/таблицы, модификация которой не затрагивает самого оригинала. Следующий весьма простой фрагмент иллюстрирует вышесказанное:

> X:= a: Y:= X: Y:= b: [X, Y]; ⇒ [a, b]

> A:= array(1..5, [42, 47, 67, 88, 96]); ⇒ A := [42, 47, 67, 88, 96]

> C:= copy(A); ⇒ C := [42, 47, 67, 88, 96]

> C[3]:= 39: C[5]:= 10: [A[3], A[5]]; ⇒ [67, 96]

> B:= A: B[3]:= 95: B[5]:= 99: [A[3], A[5]]; ⇒ [95, 99]

Создание по copy-процедуре копий объектов указанных двух типов позволяет модифицировать только их копии без изменения самих объектов-оригиналов.

В качестве встроенных индексных атрибутов Maple для table-функции используются: sparse, symmetric, antisymmetric, diagonal и identity (а также определяемые пакетными модулями), детальнее обсуждаемые при рассмотрении матричных объектов. Обсуждение tableтипа структуры данных будет постоянно детализироваться и многоаспектно рассматриваться на протяжении последующих глав книги. Целый ряд полезных средств для работы с табличными объектами представляет и наша Библиотека, прилагаемая к книге [103] и находящаяся в свободном доступе по адресам [109].

В завершение рассмотрения структур данных, поддерживаемых пакетом, будет вполне уместно представить и связанные с объектами array-типа, уже упоминаемые матрицы и векторы. Матрицы являются весьма широко применимым понятием в целом ряде естественно-научных дисциплин, составляя самостоятельный объект исследования современной математики – теорию матриц, выходящую за рамки традиционного университетского курса высшей алгебры. Основу теории матриц составляет алгебра квадратных матриц, для обеспечения которой Maple-язык располагает целым рядом важных и полезных средств, поддерживаемых функциональными средствами – модулей LinearAlgebra и linalg. Данные модули содержат определения 118 и 114 процедур соответственно (в зависимости от релиза пакета; пример приведен для Maple 10), обеспечивающих расширенные средства линейной алгебры по работе с матричными и векторными выражениями. Данные средства не входят в задачу настоящей книги, акцентирующей внимание на вопросах собственно программирования в Maple, поэтому здесь мы лишь представим основные структуры, с которыми имеют дело средства линейной алгебры пакета, а именно: матрицы (matrix) и векторы (vector).

Матрицы (matrix); в среде Maple-языка матрица представляется в виде 2D-массива, нумерация строк и столбцов которого производится, начиная с единицы. Матрица определяется либо явно через рассмотренную выше array-функцию, например M:= array(1..n, 1..p), или посредством процедуры matrix, имеющей следующие два простых формата кодирования, а именно:

matrix(L) и matrix(n, p, {L|Fn|Lv})

где L – вложенный список (listlist) векторов элементов матрицы, n и p – число ее строк и столбцов, Fn – функция генерации элементов матрицы и Lv – список или вектор элементов матрицы. Функция Fn генерации элементов матрицы определяется в форме пользовательских функции или процедуры, например Fn:=(k,j) -> Ф(k,j), такой, что M[k,j]:= Fn(k,j). Процедура randmatrix модуля linalg позволяет стохастически генерировать матрицы (mxn)-размерности, что удобно, например, для различного рода отладок и тестов.

По тестирующей функции type(M, {matrix|'matrix'(K{, square})}) возвращается true-значение, если М-выражение является матрицей; при этом допускается проверка на принадлежность значений ее элементов заданной К-области и/или квадратности (square) матрицы. В противном случае возвращается false-значение. Так как матрица является одним из типов более общего понятия массив, то функция type(M, 'array') также возвращает true-значение, если М - матрица. Обратное же в общем случае неверно, например, в случае одномерных массивов-векторов. По функции convert(L, 'matrix') возвращается матрица, если L - соответствующая ей вложенная списочная структура. С другой стороны, по функции convert(M, 'multiset') производится конвертация матричной структуры в структуру вложенных списков, каждый элемент-список которой содержит три элемента, где первые два определяют координаты элемента матрицы, а третий – его значение. Следующий фрагмент иллюстрирует вышесказанное.

> M:= matrix(3, 3, [x, y, z, a, b, c, V, G, S]);

M :=

Vax Gby Scz

> type(M, 'array'), type(M, 'matrix'), type(M, 'matrix'(symbol, square)); ⇒ true, true, true > convert([[x, y, z], [a, b, c], [V, G, S]], 'matrix');

V ax G by S cz 

> convert(M, 'multiset');

[[3, 2, G], [2, 2, b], [1, 1, x], [1, 3, z], [1, 2, y], [2, 1, a], [3, 1, V], [2, 3, c], [3, 3, S]]

> convert((a+b)*x/(c+d)*y, 'multiset'); ⇒ [[a+b, 1], [x, 1], [c+d, -1], [y, 1]]

Следует отметить, что функция convert(M, 'multiset') имеет более широкое применение, допуская в качестве своего первого фактического М-аргумента любое Maple-выражение (при этом, трактовка составляющих возвращаемого ею вложенного списка весьма существенно зависит от типа М-выражения), однако наибольший смысл она имеет для М-выражений типа массив или содержащих термы-множители. Не отвлекаясь на частности, рекомендуем читателю определить влияние типа М-выражения на семантику возвращаемого функцией вложенного списка, тем более, что в ряде случаев это может оказаться весьма полезным приемом в практическом программировании.

Обращение к элементам М-матрицы производится по индексированной M[k, j]-конструкции, идентифицирующей ее (k, j)-й элемент. Посредством этой конструкции элементам М-матрицы можно как присваивать значения, так и использовать их в вычислениях в качестве обычных переменных. Следовательно, данная индексированная конструкция обеспечивает адресное обращение к элементам М-матрицы. Совместное использование функций map и F-функции/процедуры позволяет применять последнюю одновременно ко всем элементам М-матрицы, не идентифицируя их отдельно, т.е. имеет место следующее определяющее соотношение: map(F, M {, <опции>}) ≡ ∀(k)∀(j) (M[k, j] := F(M[k, j] {, <опции>}))

Например:

> M:= matrix(3, 3, [x, y, z, a, b, c, V, G, S]): map(F, M, Art, Kr, Arn);

FFF(((V Art Kr Arna Art Kr Arnx,,, Art,,,Kr,,,Arn))) FFF(((G Art Kr Arnb Art Kr Arny,,,Art,,,Kr,,,Arn))) FFF(((S Art Kr Arnc Art Kr Arnz,,,Art,,, Kr,,, Arn)))

В целом же М-матрица рассматривается как единый объект и для его вывода или возвращения в матричной нотации можно использовать соответствено функции print и {op|evalm}. Однако на основе адресного подхода обработку матриц можно производить и рассмотренными функциональными средствами, ориентированными на сугубо скалярные аргументы. В частности, по конструкции numboccur(eval(M), <Элемент>) можно тестировать наличие в указанной M-матрице заданного элемента. Позволяет это делать и наша процедура belong [103,109].

С другой стороны, целый ряд процедур позволяют обрабатывать М-матрицу как единый объект; такие функции будем называть матричными, т.е. в качестве одного из ведущих аргументов их выступает идентификатор матрицы. Язык Maple располагает достаточно обширным набором матричных процедур, характеристика которых весьма детально рассмотрена в книгах [8-14,55-60,86-88] с той или иной степенью охвата и в полном объеме в справке по пакету. Лишь некоторые из них будут рассмотрены ниже. По функции evalm(VM) возвращается результат вычисленим VM-выражения, содержащего матрицы, применяя функциональные map-преобразования над матрицами. Однако, применение evalm-функции требует особой осмотрительности, ибо Maple-язык перед передачей фактических аргументов evalm-функции может производить их упрощения (предварительные вычисления), в ряде случаев приводящие к некорректным с точки зрения матричной алгебры результатам. Например, по evalm(diff(M, x)) возвращается нулевое значение, тогда как предполагается результат дифференцирования М-матрицы. Это связано с тем обстоятельством, что отличные от матричных функции воспринимают идентификатор матрицы неопределенным. В VM-выражении все неопределенные идентификаторы полагаются evalm-функцией в зависимости от их использования символьными матрицами либо скалярами. В суммах, содержащих матрицы, все скалярные константы рассматриваются умноженными на единичную матрицу, что позволяет естественным образом определять матричные полиномы. Так как операция произведения матриц некоммутативна, то для нее следует использовать матричный (&*)-оператор произведения, приоритет которого идентичен приоритету (*)-оператора скалярного произведения. В качестве операндов бинарного матричного (&*)-оператора могут выступать матрицы либо их идентификаторы. Следующий фрагмент иллюстрирует способы определения, тестирования и вычисления матричных выражений:

> M1:= convert([[42, 47], [67, 89]], 'matrix'): M2:=matrix(2, 3, [x, x^2, x^3, x^3, x^2, x]):

> M3:= matrix([[T, G], [G, M]]): M4:=matrix(2, 2, [ln(x), x*sin(x), Catalan*x^2, sqrt(x)]): > map(type, [M1, M2, M3, M4], 'matrix'); ⇒ [true, true, true, true]

> map(op, [M1, M2, M3, M4]);

4267 4789, xx3 xx22 xx3, GT MG, Catalan xln(x) 2 x sin (x)

> [map(diff, M2, x), map(int, M4, x)];

31x2 22 xx 31x2, Catalan xx ln(3x) − x3 sin(x2) − x3(x3/2cos) (x)

> evalm(10*M1^2 + 17*M1);

4984488909 11221362369

> evalm(M4&*(M1 + M3));

Catalan xln(x) (42 + 2 (T42) + + xTsin) + (xx) ((6767 + + GG)) Catalan xln(x) (47 + 2 (G47) + + Gx sin) + (xx) ((8989 + + MM))

С учетом сказанного, примеры фрагмента представляются достаточно прозрачными и каких-либо особых дополнительных пояснений не требуют.

Векторы (vector); в среде Maple-языка вектор представляется в виде 1D-массива, нумерация строк и столбцов которого производится, начиная с единицы. Вектор определяется либо явно через рассмотренную выше array-функцию, например V:= array(1..n), или посредством процедуры vector, имеющей следующие два простых формата:

vector([X1, X2, ..., Xn]) и vector(n, { |Fn|[X1, X2, ..., Xn]})

где Xk - элементы вектора, n - число его элементов и Fn - функция генерации элементов вектора аналогично случаю определения матриц. По тестирующей же функции type(V, {'vector'(K)| vector}) возвращается значение true, если V-выражение является вектором; при этом, допускается проверка на принадлежность значений его элементов заданной К-области. В противном случае функцией возвращается false-значение. Так как вектор является одним из типов общего понятия массив, то функция type(V, 'array') также возвращает true-значение, если V – вектор. Обратное в общем случае неверно, например, в случае списка. По convert(L, 'vector')-функции возвращается вектор, если L – соответствующая ему списочная структура.

Следует отметить, что в силу специфики организации базовых структур, на которых основываются векторы, массивы, и матрицы, они не вполне однозначно тестируются пакетными процедурой whattype и функцией type, как иллюстрирует фрагмент ниже:

> v:=vector(3,[]): m:=matrix(3,3,[]): a:= array(1..2,1..3,[]): map(whattype, map(eval, [v,m,a]));

[array, array, array]

> map(type, [v,m,a], 'array'), map(type, [v,m,a], 'vector'), map(type, [v,m,a], 'matrix');

[true, true, true], [true, false, false], [false, true, true]

Так, если процедура whattype отождествляет все типы {array, matrix, vector} как обобщающий array-тип, то функция type обеспечивает более дифференцированное тестирование, хотя и в этом случае типы array и matrix ею не различаются. Предыдущий фрагмент хорошо иллюстрирует сказанное.

Основные характеристики матриц и векторов. Прежде всего, среди класса матричных объектов выделим квадратные (square) матрицы, которые будут основным объектом нашего рассмотрения и для работы с которыми Maple-язык располагает довольно широким набором функциональных средств. Поэтому, если не оговаривается противного, то под понятием «матрица» в дальнейшем понимается именно квадратная матрица. Для работы с такого типа матрицами Maple располагает большим набором средств, находящихся в пакетных модулях linalg и LinearAlgebra.

Прежде всего, по функции indices(M) возвращается последовательность 2-элементных списков, определяющих индексное простанство М-матрицы, а по функции entries(M) – последовательность 1-элементных списков, содержащих значения элементов М-матрицы. В случае использования в качестве М-аргумента таблицы функция {indices|entries} возвращает соответственно ее входы и выходы. При этом, следует иметь в виду, что в случае матриц обе функции возвращают последовательности списков, имеющих внутреннее взаимно-однозначное соответствие (при отсутствии оного в их выходных порядках), не управляемых пользователем. Вместе с тем, на основе несложной Pind-процедуры [103], в качестве единственного фактического аргумента использующей выражение типа {array, matrix, table}, можно получать (nxp)-размерность произвольной М-матрицы в виде [n, p]списка, как это иллюстрирует следующий весьма простой фрагмент:

> Fn:= (k, j) -> k^2+3*k*j: M:= matrix(3, 10, Fn): M1:= matrix(3,3,[]): map(evalm, [M, M1]);

18104 16277 223610 284513 543416 634019 467222 815225 285890 649931, M1M1M1132,,, 111 M1M1M1321,,, 222 M1M1M1123,,, 333

> indices(M);

[1, 3], [3, 2], [2, 1], [3, 6], [1, 6], [2, 5], [3, 10], [1, 2], [1, 4], [3,3], [1, 7], [1, 9], [3, 7], [2, 9], [1,5], [2,10],

[2, 4], [1, 1], [3, 4], [2, 7], [1, 8], [3, 8], [2, 6], [2, 2], [1, 10], [2, 8], [3, 1], [3, 5], [2, 3], [3, 9]

> entries(M); ⇒ [10], [27], [10], [63], [19], [34], [99], [7], [13], [36], [22], [28], [72], [58], [16],

[64], [28], [4], [45], [46], [25], [81], [40], [16], [31], [52], [18], [54], [22], [90]

> Pind(M), Pind(M1); ⇒ [3, 10], [3, 3]

> assign(Kr=matrix(3,3,[[a,e*ln(x),c],[d,e*ln(x),f],[e*ln(x),h,k]])), eval(Kr), Mem1(Kr,e*ln(x));

e lna d( )x ee ln lnh ( ( )x x) c k f  , ,3 [1, 2], [2, 2], [3, 1] 

Во фрагменте иллюстрируется определение М-матрицы на основе matrix-функции с использованием Fn-функции генерации элементов матрицы. Представлено применение Pind-процедуры для определения размерности матриц. Процедура Mem1(M, h) возвращает последовательность, первый элемент которой определяет число вхождений в Mматрицу элементов, заданных вторым h-аргументом функции, а последующие определяют списки-координаты искомых элементов. В нашей Библиотеке [103] представлен целый ряд других полезных процедур для работы с матрицами. Можно определять целый ряд других полезных процедур как на основе представленных, так и других функций Maple-языка, что оставляется читателю в качестве весьма полезного практического упражнения. В реальной работе с пакетом это вполне возможные ситуации.

Как таблица, так и общая функция array({<ИФ>}{, <Размерность>}{, <НЗ>}) допускает 3 необязательных ключевых аргумента: ИФ – индексная функция, размерность, кодируемая в виде диапазонов (..) изменения значений индексов, и НЗ – список начальных значений для определения элементов массива. Два последних из них достаточно просты и неоднократно обсуждались при рассмотрении массивов и таблиц. Несколько детальнее остановимся на индексной функции, имеющей для матриц особое значение, определяя общего уровня их классификацию. В общем случае индексная функция определяет правило присваивания значений элементам массива или таблицы, или их обработки. При отсутствия ИФ-аргумента используется стандартный метод индексации элементов массива (матрицы). Индексная функция может определяться пользовательской процедурой, принцип организации которой здесь не рассматривается, но общий подход был указан выше. Для этих целей может использоваться, например, процедура indexfunc пакетного модуля linalg. Язык Maple располагает пятью основными встроенными ИФ с идентификаторами symmetric, antisymmetric, sparse, diagonal и identity для функций table и array. Мы рассмотрим вкратце данные индексные функции.

Индексная symmetric-функция применима в качестве первого фактического аргумента для определения симметричности элементов матрицы/таблицы относительно ее главной диагонали, т.е. для М-матрицы предполагается определяющее соотношение M[k,j] = M[j, k]. В свою, очередь antisymetric-аргумент определяет М-матрицу с соотношением M[k, j] = -M[j, k], следовательно ∀(k)∀(j)(k=j)(M[k, j]=0). Если же при определении такого типа матрицы были заданы ненулевые начальные значения для ее диагональных элементов, то они получают нулевые значения с выводом соответствующей диагностики. Аргумент diagonal определяет диагональную М-матрицу, для которой справедливо соотношение ∀(k)∀(j)(k≠j)(M[k, j]=0). Посредством sparse-аргумента определяется М-матрица, чьи неопределенные входы получают нулевые значения. Например, по вызову array(sparse, 1..n, 1..p) возвращается нулевая матрица (nxp)-размерности. Наконец, по identity-аргументу возвращается единичная матрица, для которой имеет место соотношение ∀(k)∀(j)[(k=j) → (M[k, j] = 1)] & [(k≠j) → (M[k,j] = 0)]. Следующий пример иллюстрирует получение рассмотренных выше пяти типов матриц:

> MS:=array(symmetric, 1..3, 1..3): MS[1, 1]:=10: MS[1, 2]:=17: MS[1, 3]:=39: MS[2, 2]:=64:

MS[2, 3]:=59: MS[3, 3]:=99: MD:=array(diagonal, 1..3, 1..3): MD[1, 1]:=2: MD[2, 2]:=10:

MD[3, 3]:=32: MAS:= array(antisymmetric, 1..3, 1..3): MAS[1, 2]:=10: MAS[1, 3]:=32:

MAS[2, 3]:=52: MI:= array(identity, 1..3, 1..3): MSp:=array(sparse, 1..3, 1..3): map(evalm,

[MS, MAS, MD, MI, MSp]);

39 59 99 ,  -10-320 -5210 0 5232 0 ,  2 00 1000 3200  ,  1 00 100 100 , 0 00 000 000      10 17 6417 5939

Так как вектор представляет собой (1xn)-матрицу, то к нему применим и целый ряд сугубо матричных функций, например evalm-функция, позволяющая возвращать вычисленные векторные выражения в векторной (списочной) нотации. Более того, все имеющее силу относительно матриц в полной мере (с поправкой на размерность) относится и к векторам. В частности, это относится и к (&*)-оператору матрично/векторного произведения. Длину V-вектора, подобно случаю матриц, можно вычислять, в частности, посредством конструкции вида nops([{indices|entries}(V)]); при этом, нулевое значение возвращается, если элементам V-вектора не присваивалось значений. Следующий простой фрагмент иллюстрирует некоторые способы определения, тестирования и вычисления простых векторных выражений в среде Maple-языка:

> V1:= array(1..9): V2:= array(1..6, [64, 59, 39, 10, 17, 44]): V3:= vector(5, [42, 47, 67, 89, 96]): V4:= vector(5, [64, 59, 39, 17, 10]): Fn:= k -> 3*k^2 + 10*k + 99: V5:= vector(5, Fn):

V6:= vector(6, [V, G, S, Art, Kr, Ar]): map(type, [V1, V2, V3, V4, V5, V6], 'vector');

[true, true, true, true, true, true]

> map(evalm, {V5, V6}); ⇒ {[112, 131, 156, 187, 224], [V, G, S, Art, Kr, Ar]}

> map(nops, [[indices(V6)], [entries(V6)]]); ⇒ [6, 6]

> V:=vector(6, []): map(nops, [[indices(V)], [entries(V)]]); ⇒ [0, 0]

> [type(V4, 'vector'(integer)), type(V6, 'vector'(symbol))]; ⇒ [true, true]

> Z:= vector([W, G, S]): M:= array(1..3, 1..3, [[1, 2, 3], [4, 5, 6], [7, 8, 10]]): evalm(M&*Z);

[W + 2 G + 3 S, 4 W + 5 G + 6 S, 7 W + 8 G + 10 S]

Для обеспечения работы с матричными и векторными выражениями Maple-язык располагает довольно развитым набором функциональных средств, поддерживаемых, в первую очередь, пакетными модулями linalg и LinearAlgebra. Данные модули содержат определения 114 и 118 процедур соответственно (в зависимости от релиза пакета; пример приведен для Maple 10), обеспечивающих расширенные средства линейной алгебры по работе с матричными и векторными выражениями. Учитывая обилие указанных средств и направленность данной книги, мы не будем акцентировать на них внимания, отсылая к нашей книге [12] либо к ее авторскому оригинал-макету, который можно загрузить с университетского Web-сайта www.grsu.by/cgi-bin/lib/lib.cgi?menu=links&path=sites. В них рассмотрены средства пакета, составляющие определенную базу для обеспечения работы с матрично/векторными выражениями в рамках классической линейной алгебры. Лишь кратко определим способы доступа к таким модульным средствам пакета.

Ряд средств линейной алгебры имеют классический формат вызова вида Id(<аргументы>), тогда как основная масса таких средств определяется пакетным модулем linalg. Поэтому первый вызов любого из них должен предваряться предложением одного из следующих трех форматов, а именно: with(linalg), with(linalg, <Процедура_1>, <Процедура_2>, <Процедура_3>, ...) или linalg[<Процедура>](<аргументы>). Исходя из обстоятельств, пользователь будет производить вызов процедур linalg-модуля необходимым ему способом. Первый формат обеспечивает доступ сразу ко всем процедурам модуля, но их загрузка требует дополнительной памяти в рабочей области пакета, второй формат активирует определения конкретных процедур, используемых в текущем сеансе, и третий формат определяет разовый вызов конкретной процедуры на конкретных фактических аргументах. Последний формат часто используется в пользовательских процедурах. Вызов процедуры packages() возвращает список пакетных модулей, загруженных в текущий сеанс. Вышесказанное иллюстрирует следующий простой пример:

> restart; packages(), map(type, [col, det], 'procedure'); ⇒ [], [false, false] > with(linalg, col, det), packages(), map(type, [col, det], 'procedure');

[col, det], [linalg], [true, true]

Из приведенного примера следует, что загрузка даже отдельных процедур модуля идентифицируется процедурой packages как загрузка модуля в целом.

С учетом сказанного, средства модуля linalg не представляют каких-либо затруднений при использовании их знакомым с основами линейной алгебры читателем, а приведенные здесь и в прилож. 1 [12] примеры и дополнительные замечания вполне достаточно иллюстрируют средства матричной алгебры, поддерживаемые Maple-языком. Наряду с наиболее общими linalg-модуль располагает целым рядом других, более специальных, матричных средств, включая средства создания специального типа матриц (Вандермонда, Теплица, Сильвестра и др.), в полном же объеме со средствами матричной алгебры, обеспечиваемыми Maple-языком, рекомендуется ознакомиться по книгам [10-14,59,80,86,90].

Базовые структуры данных модуля LinearAlgebra . Одну из самых больших особенностей Maple составляет его модуль LinearAlgebra, определяющий новые стандарты эффективности, надежности, полезных свойств и точности для вычислительной линейной алгебры. Данная задача была решена путем интегрирования в пакет современных программ линейной алгебры фирмы NAG (Numerical Algorithm Group) через внешний механизм вызовов [13,14,39]. Это позволяет использовать мощные вычислительные алгоритмы NAG для решения задач линейной алгебры с высокими точностью и производительностью. Однако, прежде, чем переходить к характеристике средств LinearAlgebraмодуля, кратко остановимся на различиях между ним и рассмотренным выше linalg-модулем линейной алгебры.

Если в основе векторно-матричных объектов, с которыми оперирует модуль linalg, лежит структура данных array-типа, то основу объектов, обрабатываемых средствами модуля LinearAlgebra, составляет так называемая rtable-структура данных, генерируемая одноименной встроенной функцией. Данная функция и генерируемые ею rtable-объекты детально были нами рассмотрены в [13,14,39]; ряд новых средств по работе с такого типа объектами как отдельно, так и в совокупности с array-объектами представлен нами в книгах [39,41,42,45,46,103], а также в нашей Библиотеке [103,109].

Визуализация rtable-объекта (Array, Matrix, Vector) определяется его размером. Если размер его меньше или равен значению, определенному предопределенной rtablesize-переменной процедуры interface (по умолчанию rtablesize=10), то объект визуализируется полностью. В противном случае он заменяется специальным шаблоном. Установка опции rtablesize=0 определяет вывод любого rtable-объекта в виде шаблона, тогда как установка rtablesize=infinity определяет вывод rtable-объекта полностью безотносительно его размера, как иллюстрирует следующий фрагмент:

> interface(rtablesize=2); Kr:= rtable(1..3, [89, 11, 99]); restart: Kr:=rtable(1..3, [89, 11, 99]);

Kr :=  1..3 1-D Array Data Type: anything Storage: rectangular Order: Fortran_order 

Kr := [89, 11, 99]

> interface(rtablesize = 6); Kr:= rtable(1..3, 1..3, [[42, 47, 67], [64, 59, 39], [44, 10, 17]]);

42 47 67

Kr := 6444 5910 3917  

Из фрагмента нетрудно заметить, что шаблон rtable-объекта представляет собой стандартный описатель объекта (размерность, тип и основные характеристики). В случае больших размеров массивов данное средство оказывается весьма удобным. При этом, следует иметь в виду следующее важное обстоятельство. Если размер rtable-объекта больше определяемого rtablesize-переменной, то выводится его шаблон даже в случае наличия рекурсивности в его определении, в противном случае сразу же идентифицируется аварийная ситуация, требующая перезагрузки пакета. Причиной этого является переполнение системного стэка. Впрочем, работа пакетного стэка и так имеет много нареканий.

Для работы с rtable-объектами пакет располагает рядом полезных функций и процедур, детально рассмотренных в [45,46]. Данные средства применимы к любому rtable-объекту (Array, Matrix, Vector), однако каждый из трех типов объектов располагает и собственными аналогичными средствами, кратко рассматриваемыми ниже. Для вывода rtable-объектов можно использовать и форматирующие функции printf-группы: printf, fprintf, sprintf, nprintf, с опциями которых для этого случая можно ознакомиться по справке пакета. Аналогично этому к любому rtable-объекту применимы и функции scanf-группы (scanf, fscanf, sscanf) для выполнения синтаксического анализа объектов.

Объекты rtable-типа . На основе упомянутой rtable-функции определяются три базовых объекта LinearAlgebra-модуля: Array, Matrix и Vector. Непосредственно посредством rtable-функции в среде пакета можно определять любой из указанных трех объектов. После их создания с ними можно работать в рамках алгебры, определяемой операциями, довольно детально рассмотренными в книгах [39,41,42,45,46,103]. Приведем простой фрагмент, иллюстрирующий использование операций rtable-алгебры пакета.

> A:=rtable(1..4, 1..4, random(4..11, 0.95), subtype=Array, storage=sparse, datatype=integer):

M:= rtable(1..4, 1..4, random(42..99, 0.58), subtype=Matrix, storage=rectangular): C:=rtable(1..4, 1..4): V:= rtable(1..4, random(42..99, 0.64), subtype=Vector[column], storage=rectangular): A, M, V, C;

10 869 106 55 107 56 108 56 , 69 95460 73 9500 54 536885 55 49760 ,   45 477284 ,  0 000 0 000 0 000 0 000 

> Vr:= rtable(1..4, random(47..99, 0.99), subtype = Vector[row], storage = sparse, datatype = integer); ⇒ Vr := [65, 56, 57, 90]

> Vr.M^(-2) + 6*Vr;

208520698 897237 , 787693955 1794474 , 13386043721 34095006 , 164952480446 301172553 

> A^2 + 10*A + 99, M^2 + 17*M + 95, (Vr.M + 17*Vr).(10*M + 17);

299 243195270

[3

195

299

174

174

15128

218

174

195

299

80 27,

174

243 , 14517 10416206155035 11408 1662550359913 17113 24383120175406

299

195

251067 36562329 26471066, , ]

8531

12411,

17632

4123

С учетом сказанного, примеры фрагмента особых пояснений не требуют. В этой связи имеет смысл лишь вкратце пояснить различие между табличной организацией собственно Maple-среды (базируется на table-функции) и NAG-организацией (базируется на функции rtable). В первом случае массивы и таблицы базируются на внутренних хэш-таблицах пакета, тогда как во втором случае используется формат импортированного NAG-модуля линейной алгебры. В этом случае для каждого измерения rtable-объекта используется по одному вектору индексов и отдельный вектор отводится под значения элементов массива. На основе такого представления формируются такие структуры данных как Array, Matrix, Vector[row] и Vector[column]. Более того, следует иметь в виду, что одноименные рассмотренным структуры array, matrix и vector, начинающиеся со строчных букв (исключение составляют пассивные функции), относятся к средствам собственно Maple-языка и их обработка производится иными средствами, детально рассмотренными в цитируемых выше книгах. При этом, следует отметить, что функции на основе rtable-функции принципиально отличаются от одноименных функций array, matrix и vector, как отмечалось выше.

Базовые объекты модуля LinearAlgebra . В качестве таких объектов выступают Array, Matrix, Vector[row] и Vector[column], кратко рассмотренные выше в связи с rtable-функцией, на основе которой они формируются. Между тем, пользователь имеет возможность непосредственно создавать указанные объекты на основе встроенных функции Array и процедур Matrix или Vector, кратко рассматриваемых ниже.

По функции Array(ИФ, D, НЗ, <Опции>) создается массив с заданными характеристиками, определяемыми ее фактическими аргументами. Каждый из аргументов функции не является обязательным; если же вызов функции Array определен без фактических аргументов, то возвращается пустой 0-мерный массив. Смысл и форматы кодирования аргументов функции (ИФ - индексирующая функция, D - размерность, НЗ - начальные условия и <Опции>) довольно прозрачны и особых пояснений не требуют. Поэтому на данном вопросе детальнее останавливаться не будем, а приведем несколько примеров на прямое определение массивов Array-типа.

> A:= Array(1..6, [42, 47, 67, 62, 89, 96], datatype=integer, readonly): Ao:=Array(): A, Ao;

[42, 47, 67, 62, 89, 96], Array({}, datatype = anything, storage = rectangular, order = Fortran_order)

> A[4]:= 2006;

Error, cannot assign to a read-only Array

> ArrayOptions(A);

datatype = integer, storage = rectangular, order = Fortran_order, readonly

> B:=Array(symmetric, 1..3, 1..3, datatype=integer): B[1, 1]:=42: B[2, 2]:=47: B[3, 3]:=67: B[1, 2]:= 64: B[1, 3]:=59: B[2, 3]:=39: B, ArrayIndFns(B), ArrayOptions(B);

42 6459 64 4739 59 3967 , symmetric, datatype = integer, storage = triangularupper,

order = Fortran_order

> op(1, B), ArrayOptions(B, order=C_order), ArrayOptions(B);

symmetric, datatype = integer, storage = triangularupper , order = C_order

> ArrayDims(B), ArrayNumDims(B); ⇒ 1 .. 3, 1 .. 3, 2

> ArrayElems(B);

{(1, 1) = 42, (1, 2) = 64, (1, 3) = 47, (2, 2) = 59, (2, 3) = 39, (3, 3) = 67}

> ArrayNumElems(B, NonZeroStored), ArrayNumElems(B, All); ⇒ 6, 9

Относительно сугубо Maple-объектов NAG-объекты характеризуются двумя важными чертами, а именно: (1) ссылка на их идентификатор возвращает непосредственно сам объект, не требуя таких функций как evalm, и (2) объекты создаются всегда с определенными элементами, по меньшей мере нулевыми, если не было определено противного. Второе обстоятельство весьма упрощает ряд процедур с такими объектами, не требуя их предварительного обнуления. Обусловлено это тем, что если Maple-объекты изначально ориентированы на символьные вычисления, то NAG-объекты на числовые.

Для работы с Array-объектами Maple-язык располагает рядом полезных функций, детально рассмотренных в наших книгах [13,14,29,30,33]. Примеры предыдущего фрагмента иллюстрируют создание посредством Array-функции одномерного А-массива и двумерного В-массива, а также применение к ним ряда функций работы с массивами, которые ввиду их простоты не требуют дополнительных пояснений.

Вызов процедуры Matrix(n, m, НЗ, <Опции>) возвращает матричную структуру данных (матрицу), являющуюся одной из базовых структур, с которыми работают функциональные средства LinearAlgebra-модуля. Первые два формальных аргумента процедуры определяют число строк и столбцов матрицы соответственно; при этом, фактические значения для аргументов могут кодироваться или целыми неотрицательными числами, или диапазонами вида 1 .. h (h – целое неотрицательное). Третий аргумент определяет начальные значения, тогда как опции - дополнительные характеристики создаваемого матричного объекта. Все аргументы процедуры необязательны и при их отсутствии возвращается матрица (0х0)-размерности. В общем же случае при создании матричного объекта вызов Matrix-процедуры должен содержать достаточное количество информации о его структуре и элементах. Сказанное по начальным значениям относительно rtable-функции в полной мере переносится и на третий аргумент Matrix-процедуры. Среди допустимых опций (четвертый аргумент) многие аналогичны общему случаю rtable-функции, рассмотренной выше. Однако, среди них имеются и специфические для матричной структуры. Детальнее с описанием опций функции можно ознакомиться по справке пакета. Приведем примеры на создание некоторых простых матричных объектов и их тестирование.

> M1:=Matrix(3,[[42,47,67],[64,59,39],[89,96,62]],readonly): M2:=Matrix([[a,b],[c,d]]): M1,M2;

426489 475996 673962, ac bd

> M1[3, 3]:= 47;

Error, cannot assign to a read-only Matrix

> M3:= Matrix(3, (j, k) -> 4*j^2+11*k^2-89*j-96*k+99*j*k, datatype=integer[2]): M4:=

Matrix(1..3,1..3,rand(42..99,0.58)): M5:= <<42,47,67>| <64,59,39>|<10,17,44>>: M3, M4, M5;

-49 86 243 , 44

-71-19 215-35 47123 617167 5898 538699, 424767 645939 101744

> map(type, [M1, M2, M2, M5], 'Matrix'); ⇒ [true, true, true, true]

> M6:= Matrix(2, {(1, 1)=64, (1, 2)=59, (2, 1)=10, (2, 2)=17}): type(M5,'matrix'), whattype(M5), type(M6, 'Array'), type(M6, 'Matrix'); ⇒ false, Matrix, false, true

Фрагмент представляет различные способы определения Matrix-объектов, принцип которых легко усматривается из самих примеров. Из фрагмента также следует, что в компактном виде Matrix-объект можно определять конструкцией следующего вида:

M := <M11, M21, M31>|<M12, M22, M32>|<M13, M23, M33>

недостатком которой является невозможность указания для матрицы в точке определения других ее характеристик. Более того, последний пример фрагмента иллюстрирует тот факт, что type-функция не распознает Matrix-объект в качестве Maple-матрицы, тогда как тестирующая whattype-процедура определяет его как Matrix-объект. Детальнее с описанием и применением функции Matrix можно ознакомиться в книгах [13,14,42] и в справке по пакету. С учетом сказанного Matrix-объекты (NAG-матрицы) довольно прозрачны, но за более подробной информацией по их определению можно обращаться к справке по пакету (например, оперативно по конструкции ?Matrix).

Наконец, по функции Vector[T](n,НЗ, <Опции>) возвращается векторная структура данных (вектор), являющаяся одной из основных структур, с которыми работают функциональные средства LinearAlgebra-модуля пакета. Индекс Т определяет сам тип вектора (column – столбец, row – строка, по умолчанию полагается column). Первый формальный аргумент функции определяет число элементов вектора; при этом, фактические значения для аргумента могут кодироваться или целым неотрицательным числом, либо диапазоном вида 1..h. Второй аргумент определяет начальные значения, тогда как опции – дополнительные характеристики создаваемого векторного объекта. Все аргументы функции необязательны и при их отсутствии возвращается вектор 0-размерности, т.е. элемент 0-мерного векторного пространства. В общем же случае при создании векторного объекта вызов Vector-функции должен содержать достаточное количество информации о его структуре и элементах. Сказанное выше по начальным значениям относительно rtable-функции в полной мере переносится и на второй аргумент Vector-функции. Среди допустимых опций (третий аргумент) многие аналогичны общему случаю rtable-функции, рассмотренной выше. Однако допускаемые ими значения имеют векторную специфику. Детальнее с описанием опций Vector-функции можно познакомиться в справке по пакету. Приведем простые примеры на создание векторных объектов.

> V1:= Vector(1..4, [42, 47, 67, 89]): V2:= Vector[row](4, {1=64, 2=59, 3=39, 4=17}): V3:= Vector(1..4): V1, V2, V3;

 4247 6789 , [64 59 39 17, , , ],   00 00 

 > VectorOptions(V2); shape = [], datatype = anything, orientation = row, storage = rectangular, order = Fortran_order > V4:= <Sv, Ar, Art, Kr>: V5:= <Sv|Ar|Art|Kr>: V4, V5, VectorOptions(V4, readonly);

ArtKr Ar Sv  , [Sv Ar Art Kr, , , ], false

> V6:= Vector[row](6, rand(42..99)): V7:=Vector(): V6, type(V6, vector), whattype(V6), V7;

[57, 89, 80, 52, 48, 71], false, Vector[row], []

Фрагмент представляет различные способы определения Vector-объектов, принцип которых усматривается из самих примеров. Из фрагмента также видно, что в компактной форме объект Vector-типа можно определять конструкциями следующего вида:

V := <V1, V2, V3, …, Vn> или V := <V1|V2|V3| … |Vn>

недостатком которых является невозможность задания для вектора в точке его определения других характеристик. При этом, первый формат определяет вектор-столбец, тогда как второй – вектор-строку. Более того, последний пример фрагмента иллюстрирует тот факт, что type-функция не распознает Vector-объект в качестве Maple-вектора, тогда как тестирующая whattype-процедура определяет его как Vector-объект. С учетом сказанного Vector-типа объекты (NAG-векторы) довольно прозрачны и за более детальной информацией по их определению можно обращаться к справке (например, оперативно по конструкции ?Vector).

Как уже отмечалось выше, между Maple-объектами (array, vector и matrix) и NAG-объектами (Array, Vector и Matrix) имеются принципиальные различия. Более того, классификация вторых относительно тестирующих функции type и процедуры whattype выгодно отличается однозначностью, тогда как первые распознаются whattype-процедурой как объекты array-типа. В приведенном ниже фрагменте этот момент иллюстрируется весьма наглядно:

> a:=array(1..2, 1..2, [[42,47], [64,59]]): A:=Array(1..3, 1..3): v:=vector([1,2,3]):

V:=Vector([1,2,3]): m:=matrix([[G, S, Vic], [47, 67, 42]]): M:=Matrix([[G, S, Vic], [47, 67, 42]]):

> type(a, 'array'), type(a, 'matrix'), type(v, 'vector'), type(v, 'array'), type(m, 'matrix'), type(m,'array'); ⇒ true, true, true, true, true, true

> type(A, 'Array'), type(A, 'Matrix'), type(V, 'Vector'), type(V, 'Array'), type(M, 'Matrix'), type(M, 'Array'); ⇒ true, false, true, false, true, false

> map(whattype, map(eval, [a, v, m])), map(whattype, [A, V, M]);

[array, array, array], [Array, Vector[column], Matrix]

> convert(a, 'listlist'), convert(A, 'listlist'); ⇒ [[42, 47], [64, 59]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]] > a[1]:= [x, y, z];

Error, array defined with 2 indices, used with 1 indices

> A[1]:= [x, y, z]; ⇒ A[1] := [x, y, z]

> convert(A, 'listlist'); ⇒ [[[x, y, z], [x, y, z], [x, y, z]], [0, 0, 0], [0, 0, 0]]

> Ar:=Array(1..2, 1..2, 1..4, [[[64, 59, 39, 10], [47, 67, 42, 6]], [[c2, b2, a2, 17], [c3, b3, a3, 6]]]): > convert(Ar, 'listlist'); ⇒ [[[64, 59, 39, 10], [47, 67, 42, 6]], [[c2, b2, a2, 17], [c3, b3, a3, 6]]] > Ar[1, 2]:= AVZ; ⇒ Ar[1, 2] := AVZ

> convert(Ar, 'listlist'); ⇒ [[[64,59,39,10], [AVZ,AVZ,AVZ,AVZ]], [[c2,b2,a2,17], [c3,b3,a3,6]]]

> A1:= Array(1..3, 1..3, [[a, b, с], [x, y, z], [42, 47, 6]]); ⇒ A1 :=

42ax 47by ñ6z

> A1[2]:= [Grodno, Tallinn]: A1;

[Grodno Tallinn42a , ] [Grodno Tallinn47b , ] [Grodno Tallinnñ 6, ]

В целом ряде случаев точная идентификация rtable-объектов тестирующими средствами пакета играет весьма существенную роль. Еще на одном моменте данного фрагмента имеет смысл обратить внимание, а именно. Если 2-мерный Maple-массив (array) не допускает возможности замены своих строк путем присвоения, инициируя ошибочную ситуацию, то массив NAG (Array) такую операцию допускает, однако присваиваемое значение дублируется по числу элементов строки. Соответствующим образом это обобщается и на n-мерные массивы, как показано выше. В ряде случаев это может представить практический интерес при программировании ряда приложений в среде пакета.

rtabobj := proc()

local a b c k j t A M V x y z, , , , , , , , , , , ; assign(a = map(convert, {anames '( rtable')}, 'string'), b = { }, c = { }, x = [ ], y = [ ], z = [ ]);

seq `if`( (search(k, "RTABLE_SAVE/" ' ', t ) and t = 1, assign(' 'c = {k, op( )c }), assign('b' = {k, op(b)})), k = a); for k in b do

if type(`` k || , 'Array') then A := [ ]k ;

for j in c do

if convert(`` || k, 'listlist') = convert(`` || j, 'listlist') then

A := [op(A), ]j end if

end do; x := [op( )x , A]

elif type(`` || k, 'Matrix') then M := [ ]k ; for j in c do

if convert(`` k || , 'listlist') = convert(`` j || , 'listlist') then M := [op(M), ]j

end if

end do; y := [op( )y , M]

else

V := [ ]k ; for j in c do

if convert(`` k || , 'listlist') = convert(`` j || , 'listlist') then

V := [op(V), ]j end if

end do; z := [op(z), V]

end if

end do; x y z, ,

end proc

> rtabobj();

[["A"], ["A1", "RTABLE_SAVE/12988492", "RTABLE_SAVE/12468956"]],

[["M", "RTABLE_SAVE/15041652", "RTABLE_SAVE/15049896"], ["M1",

"RTABLE_SAVE/15041652", "RTABLE_SAVE/15049896"]],

[["V", "RTABLE_SAVE/3384632"], ["V1", "RTABLE_SAVE/14862784", "RTABLE_SAVE/15017212", "RTABLE_SAVE/15026360"]]

Вместе с тем, Maple-объекты существенно проще NAG-объектов и несложно конвертируются во вторые. В наших книгах [29,33,42,43,103] и приложенной к ним Библиотеке представлены дополнительные средства конвертации Maple-объектов в NAG-объекты, и наоборот. Наряду с ними, представлен ряд других средств по работе с rtable-объектами, которые существенно расширяют стандартные средства пакета. Эти средства оказываются достаточно полезными при продвинутом программировании разнообразных задач, имеющих дело с rtable-объектами, обеспечивая Maple-программиста целым рядом дополнительных возможностей. Так, например, вызов представленной выше процедуры rtabobj() возвращает трехэлементную последовательность вложенных (в общем случае) списков, где элементы данной последовательности представляют информацию о rtable-объектах в разрезах типов Array, Matrix и Vector соответственно, активных в текущем сеансе. При этом, каждый подсписок первым элементом в строчном формате определяет имя rtable-объекта соответствующего типа, тогда как остальные его элементы определяют в строчном формате соответствующие ему идентификационные номера вида «RTABLE_SAVE/nnnnnnnn». Более детальный анализ данной информации позволяет, например, прослеживать историю работы с rtable-объектами текущего сеанса [42,103].

Процедура MwsRtb(F) анализирует mws-файл F на наличие в нем rtable-объектов [103].

MwsRtb := proc(F::{string, symbol, name}) local a b c d k p h t, , , , , , , ; if not type( ,F 'file') or Ftype(F) ≠ ".mws"then

ERROR("%1 is not a mws-datafile or does not exist", F) else assign(a = RS([`\n` = ``], readbytes(F, 'TEXT', ∞))), close(F); if not search( ,a "{RTABLE_HANDLES" ' ', t ) then RETURN(

WARNING("rtable-objects do not exist in datafile <%1>", F)) end if;

assign(' 'a = a[t + 1 .. -1], c = "{RTABLE "), search( ,a "}{" ' ', t ), assign(b = map(SD, SLD(a[15 .. t − 2], " ")), 'a' = a[t + 1 .. -1]),

`if`(1 < nargs and type(args 2[ ], 'symbol'), assign(args 2[ ] = b), NULL)

end if;

assign(p = SLD(a c, ), d = map2(cat, "RTABLE_SAVE/", b), h = MkDir cat(( [libname ][1][1 .. 2], "\rtbobj")));

for k to nops(p) do

assign(' 'a = cat(h, "\_", b k[ ], ".m")), writeline(a, cat(p k[ ][1 .. 4], "

", p k[ ][5 .. -2])), close(a); read a

end do;

d, WARNING("rtable-objects of datafile <%1> are active in the current session\

and have been saved in directory <%2>; their names are %3", F h, , map2(cat, "_", ,b ".m" ))

end proc

> MwsRtb("C:/Academy\\Examples\\MPL9MWS\\IdR.mws", 'h'); h;

Warning, rtable-objects of datafile <C:/Academy\Examples\MPL9MWS\IdR.mws> are

active in a current session and have been saved in directory <c:\program files\ maple 9\rtbobj>; their names are [_5290620.m, _5603484.m, _5188712.m]

["RTABLE_SAVE/5290620", "RTABLE_SAVE/5603484", "RTABLE_SAVE/5188712"] [5290620, 5603484, 5188712]

При наличии таких объектов возвращаются их вызовы в строчном формате, иначе возвращается NULL-значение. Через необязательный второй аргумент возвращаются идентификационные номера (rtable-индексы) rtable-объектов, находящихся в файле F.

Весьма детальный обзор функциональных средств модуля LinearAlgebra, его базовые структуры данных, средства их создания и алгебра над ними детально рассмотрены в вышеупомянутых книгах [13,14,29,30,33]. Пакетный модуль LinearAlgebra предназначен как для интерактиного режима использования, так и для эффективного программирования в Maple. Для этого пакет прикладных программ линейной алгебры фирмы NAG был имплантирован в среду пакета Maple как программный модуль. Данная организация позволяет обращаться к его средствам следующими двумя способами, а именно:

(1) как к стандартному модулю пакета по конструкции формата: LinearAlgebra[Функция](Аргументы) (2) как к программному модулю по конструкции формата:

LinearAlgebra:- Функция(Аргументы)

В зависимости от удобства и ситуации может быть выбран любой из двух способов. Как правило, это определяется как опытом пользователя, так и самим алгоритмом программируемой в среде пакета задачи.

Начиная с 6-го релиза, пакет Maple предоставляет два альтернативных выбора при решении задач линейной алгебры, базирующихся соответственно на модулях LinearAlgebra и linalg. Средства первого модуля и его концепция были довольно детально рассмотрены в наших книгах [8-14], тогда как второго в книгах [29-33]. Отметим здесь только наиболее характерные отличительные черты модуля LinearAlgebra. Прежде всего, модуль LinearAlgebra представляет собой большой набор процедур линейной алгебры, покрывающих почти все функциональные возможности пакета linalg. Вместе с тем, он имеет значительно более четкие структуры данных, располагает дополнительными средствами для создания специальных типов матриц, и улучшенными возможностями для решения матричных задач. Его преимущества особенно наглядны при вычислениях с большими числовыми матрицами, элементами которых являются значения float-типа (как данные пакетной float-арифметики, так и данные машинной float-арифметики). В качестве иллюстрации представим ряд примеров использования средств модуля LinearAlgebra для решения некоторых массовых задач линейной алгебры:

> alias(LA = LinearAlgebra): A:= <<64, 59, 39>|<42, 47, 67>|<38, 62, 95>>: B:= <10, 17, 99>: > LA[Transpose](LA[LinearSolve](A, B)), LA[Transpose](LA[LeastSquares](A, B));

-33081855 , 69211855, -67 , -33081855 , 69211855, -67 

> LA:- Determinant(A);

Error, `LA` does not evaluate to a module

> LA[Determinant](A); ⇒ -33390

> M:= rtable([[64, 59, 39], [42, 47, 67], [43, 10, 17]], subtype = Matrix, readonly = true);

644243 594710 671739

M :=

> LA[Determinant](M), LA[Rank](M), LA[Trace](M); ⇒ 73670, 3, 128 > evalf(LA[Eigenvalues](M, output = 'Vector[row]'));

[131.7149459, -1.85747297 + 23.57676174 I, -1.85747297 - 23.57676174 I]

> evalf(LA[MatrixInverse](M, method='LU'), 6); ⇒

0.00175105-0.02173200.0294150 -0.00799511-0.008320890.0257500 0.00719424-0.03597120.0287770 

> evalf(LA[QRDecomposition](evalf(M), output = 'NAG'), 6);

0.2766779999999999800.283266000000000018-87.8008999999999986 -0.664429999999999965-70.3864999999999981-28.9090999999999988 -26.8831999999999988-68.803399999999996429.0239000000000011 ,

1.728920000000000011.387480000000000040. 

> LA[FrobeniusForm](M); LA[CharacteristicPolynomial](%, h);

010 001 73670128-70

h3 − 128 h2 + 70 h − 73670

> evalf(LA[SingularValues](evalf(M), output = 'list'), 6); ⇒ [136.209, 30.5125, 17.7259]

> evalf(LA[Eigenvectors](evalf(M), output = 'vectors'), 8);

[0.720627899999999988 + 0. I ,

0.278720599999999984 + 0.381360960000000027 I ,

0.278720599999999984 − 0.381360960000000027 I]

[0.613184240000000047 + 0. I , -0.671878590000000053 + 0. I ,

-0.671878590000000053 + 0. I]

[0.323574589999999995 + 0. I ,

0.315224249999999984 − 0.475490770000000006 I ,

0.315224249999999984 + 0.475490770000000006 I]

> LinSolve:=(P::package, A::{Matrix, matrix}, B::{Vector, vector}) -> op([assign('K'=with(P)),

`if`(`if`(P = linalg, det, Det)(A)<>0, `if`(P = LinearAlgebra, LinearSolve, linsolve)(A, B),

ERROR("Matrix is singular"))]): LinSolve(LinearAlgebra, A, B); # (1)

LinearSolve  64 5939 42 4767 38 9562  ,   10 1799   

> a:= matrix([[64, 59, 39], [42, 47, 67], [38, 62, 95]]): b:= vector([10, 17, 99]): # (2)

> alias(la = linalg): evalf(LinSolve(linalg, a, b)); ⇒ [-4.75624439, 5.94860737, -0.937646002]

Прежде всего, следует обратить внимание на одно важное обстоятельство. С целью устранения неудобства, связанного с многократным использованием довольно длинного имени модуля LinearAlgebra, ему был присвоен алиас "LA", однако этот довольно удобный подход выявил ряд его особенностей. Прежде всего, алиас нельзя использовать в конструкциях вида LA:- Функция, как это хорошо иллюстрирует третий пример фрагмента. В этом случае следует использовать конструкцию LA[Функция]. Общей рекомендацией является определение алиаса для имени модуля вне тела процедуры/функции, в таком случае он может использоваться наравне с основным именем. Второй пример фрагмента иллюстрирует решение системы линейных уравнений посредством 2-х процедур LinearSolve и LeastSquares модуля. Посредством Transpose-процедуры результат решения системы линейных уравнений A.X = B по-лучаем в виде вектора-строки. Последующие примеры фрагмента иллюстрируют применение различных процедур пакетного модуля LinearAlgebra. Исключение составляют последние примеры 1 и 2, иллюстрирующие принципиальное отличие модуля linalg от LinearAlgebra относительно их использования внутри определений функций/процедур. В примере (2) показано, что использование вызова with(linalg) внутри тела функции делает доступными процедуры мо- дуля linalg как в области определения самой функции, так и вне ее. Тогда как согласно примера (1) аналогичный подход, но на основе модуля LinearAlgebra не работает, что в определенной мере сужает выразительные возможности программирования с использованием его функциональных средств. Имеется ряд других различий пакетных модулей linalg и LinearAlgebra, обусловленных проблемами полной интеграции второго в среду пакета, однако мы не будем здесь на них останавливаться. Заинтересованный же читатель отсылается к нашим книгам [13-14,29-33,39,41-43,45,46,103].

С учетом сказанного рассмотренные средства линейной алгебры и их базовые структуры данных не представляют каких-либо затруднений при использовании их знакомым с основами линейной алгебры читателем. В свете сказанного следует иметь в виду, что нами были представлены наиболее употребительные форматы матрично-векторных средств Maple-языка с определенными акцентами скорее на особенностях их реализации и выполнения, чем на их математической сущности, хорошо известной имеющим опыт в данном разделе математики. Поэтому за деталями их описания необходимо обращаться к справочной системе пакета либо к цитированной выше литературе. Однако, многие вопросы снимаются при практической апробации рассмотренных средств. В настоящее время имеется целый ряд внешних модулей пакета (часть из них поставляется по выбору), весьма существенно расширяющих рассмотренные базовые средства матричновекторных операций Maple-языка. Эти средства постоянно расширяются. На базе рассмотренных средств Maple-языка пользователь имеет возможность программировать и собственные, недостающие для решения его матрично-векторных задач, средства. Пакет постоянно, расширяя свои приложения, между тем, не в состоянии в должной мере учесть все потребности, поэтому его программная среда и предоставляет пользователям возможность расширять его новыми средствами под ваши специфические нужды. Типичным примером такого подхода и является наша Библиотека программных средств [41,42,103,109]. Ниже вопросы решения задач линейной алгебры в среде Maple (учитывая специфику настоящей книги) не рассматриваются. Заинтересованный читатель отсылается к книгам [8-14,78,84,86,88,55,59-62], а также к [91] с адресом сайта, с которого можно бесплатно загрузить некоторые книги, и к [109] с нашей Библиотекой.

Псевдослучайные числа. Еще с одним видом значений - псевдослучайных пользователь имеет возможность работать на основе встроенного генератора псевдослучайных чисел (ГПСЧ), активизируемого по rand-процедуре, имеющей три формата кодирования. По вызову rand() возвращается псевдослучайное неотрицательное integer-число (ПСЧ) длиной в 12 цифр, тогда как по вызову rand({n|n..p}) (n, p – целочисленные значения и n ≤ p) возвращается равномерно распределенное на интервале соответственно [0 .. n] и [n .. p] целое ПСЧ. Для установки на-чального значения для ГПСЧ используется предопределенная _seed-переменная пакета, имеющая значение 427419669081, которое в любой момент может быть переопределено пользователем, например: _seed:= 2006.

Для этих же целей, но с более широкими возможностями, используется и randomize-процедура, кодируемая в одном из допустимых форматов вида: randomize({|n}), где n > 0 – целочисленное выражение, значение которого и присваивается _seed-переменной. Если используется формат randomize() вызова процедуры, то для _seed-переменной устанавливается значение, базирующееся на текущем значении системного таймера. Так как вызов randomize() возвращает базовое значение для ГПСЧ на основе текущего значения таймера, то таким способом можно достаточно эффективно генерировать различные последовательности ПСЧ. Сохраняя необходимые значения _seed-переменной, можно повторять процесс вычислений с псевдослучайными числами, что особенно важно в случае необходимости проведения повторных вычислений.

Вызов rand может инициировать вывод самого тела соответствующей ей процедуры, рекомендуемый подавлять по завершающему обращение к процедуре двоеточию. В случае же намерений пользователя написать собственную процедуру для подобных целей текст пакетной rand-процедуры может оказаться определенным прообразом. В общем же случае для обеспечения доступа к ГПСЧ рекомендуется использовать конструкции вида Id:= rand({|n|n .. p}), выполнение которых открывает доступ к последовательностям ПСЧ по вызовам Id(), каждое использование которого инициирует получение очередного равномерно распределенного на заданном интервале ПСЧ. Следующий пример иллюстрирует применение рассмотренных средств Maple-языка по работе с псевдослучайными числами:

> rand(); AVZ:= rand(1942 .. 2006): _seed:= 2006: ⇒ 403856185913

> seq(AVZ(), k=1 .. 10); ⇒ 2003, 1960, 1944, 1990, 1986, 1972, 1986, 1992, 2006, 1964

> randomize(2006): _seed; ⇒ 2006

> ПСЧ:= array[1 .. 10]: for k while k <= 10 do ПСЧ[k]:= AVZ() end do:

> seq(ПСЧ[k], k=1 .. 10); ⇒ 2005, 1946, 1958, 1951, 1983, 1992, 1946, 1955, 1995, 1951 > restart; [_seed, rand(), _seed]; ⇒ [_seed, 427419669081, 427419669081]

Наряду с приведенными Maple располагает и другими подобными средствами, в частности, для решения статистических задач (модуль stats), работы со стохастическими объектами (модуль RandomTools), стохастической генерации матриц (linalg[randmatrix]), полиномов (процедура randpoly) и др. Средства поддержки работы с псевдослучайными числами могут быть использованы во многих вычислительных задачах стохастического и квазистохастического характера, а также в задачах, требующих наборов данных для отладки программ и тестирования вычислительных алгоритмов. Рассмотренные средства широко используются нами в различного рода иллюстративных примерах для генерации разнообразных числовых данных.

Выше мы уже не раз употребляли такое понятие как математическое выражение (либо просто выражение), являющееся одним из важнейших понятий Maple, да и математики в целом. Работа с математическими выражениями в символьном виде — основа основ символьной математики. Не меньшую роль они играют и в численных вычислениях. Выражение — центральное понятие всех математических систем. Оно определяет то, что должно быть вычислено в численном или символьном виде. Не прибегая к излишнему формализму, несколько поясним данное понятие. Математические выражения строятся на основе чисел, констант, переменных, операторов, вызовов функций/процедур и различных специальных знаков, например, скобок, изменяющих порядок вычислений. Выражение может быть представлено в общепринятом виде (как математическая формула или ее часть) с помощью операторов, например, с*(х + у^2+sqrt(z)) либо (a+b)/(c+d), оно может определять просто вызов некоторой функции или процедуры F(x,y,z), либо их комбинацию. Используемые в дальнейшем многочисленные иллюстративные фрагменты представят достаточное число примеров на определение допустимых Maple-выражений, что уже практически позволит уяснить данное ключевое понятие пакета.

Наряду с рассмотренными Maple-язык поддерживает работу с рядом других структур данных (стэк, очередь, функциональные ряды, связные графы, графические объекты и т.д.). В этом направлении нами был создан ряд ролезных средств, расширяющих и дополняющих стандартные. В частности, для работы со структурами типа стэк (stack) и очередь (queue), а также введен новый тип структур прямого доступа (dirax) [41,42,103]. Пока же нам будет вполне достаточно приведенных сведений по типам данных и структур данных для понимания сути излагаемого материала, который ссылается на данные понятия. Переходим теперь к средствам Maple-языка, тестирующим вышерассмотренные типы данных, структур данных и выражений.

1.6. Средства тестирования типов данных, структур данных и выражений

Согласно аксиоматике Maple под типом понимается Т-выражение, распознаваемое typeфункцией и инициирующее возврат логического {true|false}-значения на некотором множестве допустимых Maple-выражений. В общем случае Т-выражения языка относятся к одной из четырех групп: (1) системные, определяемые идентификаторами языка {float, integer, list, set и др.}; (2) процедурные, когда тип входит в качестве аргумента в саму тестирующую функцию {type(<Выражение>, <Тип>)}; (3) приписанные и (4) структурные, представляющие собой Maple-выражения, отличные от строковых и интерпретируемые как тип {set(<Id> = float)}. К пятой группе можно отнести типы, определяемые модульными средствами пакета. Несколько детальнее о данной классификации типов будет идти речь ниже по мере рассмотрения все более сложных Maple-объектов. Уже неоднократно упоминавшееся понятие выражения, хорошо знакомое обладающему определенной компьютерной грамотностью читателю, является одним из фундаментальных понятий Maple-языка. Понятие выражения Maple-языка аккумулирует такие рассмотренные конструкции языка как константы, идентификаторы, переменные, данные и их структуры, а также рассматриваемые детально ниже функции, процедуры, модули и т. д. К выражениям в полной мере можно относить, в частности, и такие конструкции языка, как процедуры, ибо их определения допустимо непосредственно использовать при создании сложных выражений. Детальнее на данном вопросе не будем акцентироваться, а отсылаем заинтересованного читателя, например, к таким книгам как [9-14, 29-33, 59-

62, 78-89, 103].

Для определения рассмотренных типов данных и структур данных язык пакета располагает развитыми средствами, базирующимися на специальных тестирующих процедуре whattype и функциях typematch, type, имеющих следующие форматы кодирования:

{type|typematch}(<Maple-выражение>, {<Тип>|<Множество типов>}) whattype(<Выражение>)

где в качестве первого аргумента выступает произвольное допустимое Maple-выражение, а в качестве второго указывается идентификатор требуемого Типа либо их множество. Булева функция {type|typematch} возвращает логическое true-значение, если значение выражения Maple имеет тип, определяемый ее вторым аргументом, и false-значение в противном случае. При определения в качестве второго аргумента множества типов функция {type|typematch} возвращает логическое true-значение в том случае, если тип значения Maple-выражения принадлежит данному множеству, и false-значение в противном случае. При этом, следует помнить, что в качестве второго аргумента может использоваться только множество ({}-конструкция, а не []-конструкция; данное обстоятельство может на первых порах вызывать ошибки пользователей, ранее работавших с пакетом Mathematica, синтаксис которого для списочной структуры использует именно первую конструкцию). Для второго аргумента {type|typematch}-функции допускается более 202 определяемых пакетом типов (Maple 10), из которых здесь рассмотрим только те, которые наиболее употребляемы на первых этапах программирования в Maple и которые непосредственно связаны с рассматриваемыми нами конструкциями языка пакета: идентификаторы, текст, числовые и символьные данные, структуры данных и др. При этом, typematch-функция имеет более расширенные средства тестирования типов, поэтому детальнее она рассматривается несколько ниже.

Наконец, по тестирующей процедуре whattype(<Выражение>) возвращается собственно тип Maple-выражения, определяемого ее фактическим аргументом. При этом, следует отметить, что данная процедура в ряде случаев решает задачу тестирования более эффективно, например для последовательностных структур и в случае неизвестного типа, что позволяет избегать перебора подвергающихся проверке типов. Тут же уместно отметить, что средства тестирования типов, обеспечиваемые, в частности, { typematch| type}-функцией существенно более развиты, чем подобные им средства Mathematica [28-30,32,42,43]. На основе данных средств предоставляется возможность создания достаточно эффективных средств программного анализа типов данных и их структур. В табл. 5 представлены некоторые допустимые Maple-языком типы, тестируемые функцией {type|typematch} и используемые в качестве значений ее второго фактического аргумента, а также их назначение.

Таблица 5

Id типа

тестируемое Maple-выражение; пояснения и примечания:

algnum

алгебраическое число

array

массив; дополнительно позволяет проверять вид массива, тип его элементов и другие характеристики

Array

массив rtable-типа; дополнительно позволяет проверять вид массива, тип его элементов и другие характеристики

hfarray

массив МАПТ-типа; используется средствами МАПТ

anything

любое допустимое Maple-выражение, кроме последовательности

boolean

логическая константа {true, false, FAIL}

complex

комплексная константа; не содержит нечисловых констант {true, false, FAIL, infinity}, тестирует тип действительной и комплексной частей

complexcons

комплексная константа; a+b*I, где evalf(a) и evalf(b) - float-числа

constant

числовая константа

{odd|even}

{нечетное|четное} целое выражение