Одним из важнейших способов управления поведением объекта служит переопределение для него основных математических операторов. Делается это также, как и в предыдущем случае, при определении конструкторов и конверторов. А именно, нужно завести в папке, содержащей методы класса M-файл с именем, соответствующем имени переопределяемого оператора и в этом файле определить функцию с этим же именем, например:
function r = plus(p,q)
% @POLYNOM/PLUS.M
p = polynom(p);
q = polynom(q);
k = length(q.c) - length(p.c);
r = polynom([zeros(1,k) p.c] + [zeros(1,-k) q.c]);
Такая функция, определенная в файле @polynom/plus.m позволит складывать полиномы, как обычные объекты MATLAB - вещественные матрицы, вектора или числа. При этом, принудительное преобразование формальных аргументов к типу полинома в первых строках этой функции гарантирует, что будут правильно вычисляться смешанные выражения:
" p = polynom([1 2 3]);
" q = polynom([3 2 1]);
" pq = p + q;
" p1 = p + 1;
" q1 = 1 + q;
Вызовы функции zeros() в методе сложения полиномов дополняют нулями коэффициенты более короткого полинома из двух. В таблице приводится полный список имен методов для переопределения всех операторов MATLAB:
Оператор
Метод
Описание
a + b
plus(a,b)
Сложение
a - b
minus(a,b)
Вычитание
-a
uminus(a)
Унарный минус
+a
uplus(a)
Унарный плюс
a.*b
times(a,b)
Поэлементное умножение
a*b
mtimes(a,b)
Матричное умножение
a./b
rdivide(a,b)
Правое поэлементное деление
a.\b
ldivide(a,b)
Левое поэлементное деление
a/b
mrdivide(a,b)
Правое матричное деление
a\b
mldivide(a,b)
Левое матричное деление
a.^b
power(a,b)
Поэлементное возведение в степень
a^b
mpower(a,b)
Матричное возведение в степень
a < b
lt(a,b)
Меньше
a > b
gt(a,b)
Больше
a <= b
le(a,b)
Меньше или равно
a >= b
ge(a,b)
Больше или равно
a ~= b
ne(a,b)
Неравно
a == b
eq(a,b)
Равно
a & b
and(a,b)
Логическое И
a | b
or(a,b)
Логическое ИЛИ
~a
not(a,b)
Логическое НЕ
a:d:b или a:b
colon(a,d,b) colon(a,b)
Оператор "двоеточие" - генерация
a.'
transpose(a)
Транспонирование
a'
ctranspose(a)
Комплексно-сопряженное транспонирование
[a b]
horzcat(a,b,...)
Горизонтальная конкатенация
[a; b]
vertcat(a,b,...)
Вертикальная конкатенация
a(s1,s2,...sn)
subsref(a,s)
Ссылка по индексу
a(s1,...,sn) = b
subsasgn(a,s,b)
Присваивание по индексу
b(a)
subsindex(a,b)
Индексирование
Вывод в командной строке
display(a)
Распечатка значения
MATLAB всегда начинает просмотр функций, оперирующих над объектом с папки, содержащей методы класса - перед любой другой папкой, указанной в пути просмотра MATLAB (MATLAB-path). Это означает, что при создании нового класса всегда есть возможность переопределить любую функцию для данного класса. Например, для полиномов можно создать специализированную функцию roots(), вычисляющую корни полинома каким-либо методом, отличным от стандартного, реализуемого встроенной функцией roots().
Обычно, MATLAB считает объекты всех классов имеющими одинаковый приоритет и вызывает бинарный метод у левого операнда выражения. Существует возможность управлять иерархией приоритетов для разных классов, так, что в ситуациях a + b и b + a будет гарантированно вызываться метод операнда b.
Для того, чтобы повысить приоритет создаваемому классу по сравнению с другим классом нужно в конструкторе вызвать функцию superiorto() и в качестве ее единственного аргумента указать текстовую строку - имя класса, приоритет которого в выражениях должен быть всегда ниже приоритета создаваемого класса.
Аналогично, можно понизить приоритет создаваемого класса по сравнению с некоторым другим. Для этого в конструкторе надо вызвать функцию iferiorto().
Нужно не забывать, что объекты базируются на структурах, которые в MATLAB на самом деле всегда являются массивами структур. Этим фактом можно воспользоваться, для конструирования объектов-массивов. При этом во всех методах такого класса нужно будет пользоваться циклом по элементам базового массива структур - p(i).c, например.
К весьма нетривиальным последствиям может привести переопределение взятия значения по индексу (массива). В самом деле, для определенных выше полиномов операция p(c) может означать следующее:
значение полинома при x = 3;
третью производную;
третий коэффициент полинома;
коэффициент при x3;
третий полином в последовательности, например, полиномов Чебышева.
Разработчик класса полиномов может выбрать любой из этих путей, в зависимости от своих потребностей. Для того, чтобы зафиксировать выбранный способ интерпретации выражения p(c) ему придется переопределить метод ссылки по индексу - subsref(A,S) (см. таблицу).
Есть три возможных причины вызова этого метода:
1. A(i) - обычно, индекс в вещественном массиве
2. A{i} - обычно, индекс в массиве ячеек
3. A.val - обращение к полю структуры.
Во всех случаях будет произведен вызов subsref(A,S), где S - структура вида:
S.type - будет иметь значение текстовой строки '()', '{}' или '.' в зависимости от того, какой из приведенных выше случаев имеет место.
S.subs - массив ячеек, содержащий сами индексы. Если в выражении встретилось A(1:3,2,:), то S.subs будет равен {1:3, 2, ':'}.
Более сложный пример, A(1,2).name(3:4), породит один вызов subsref(A,S) с S, представляющей собой массив 3х1:
Аналогично, в случае присваивания значения по индексу A(I) = B, A{I} = B или A.I = B, генерируется вызов A = subsasgn(A,S,B), где S - та же структура, что и в предыдущем случае.
Наконец, последний переопределяемый метод индексирования - subsindex() вызывается независимо для каждого указанного индекса в выражении X(A), где A является объектом. Этот метод обязательно должен возвращать целочисленный массив реальных индексов, которые и будут использоваться для извлечения значений из массива X. Этот метод вызывается встроенными методами subsref(), subasgn(), и может понадобиться при переопределении этих методов.
MATLAB поддерживает одиночное и множественное наследование для классов. А именно, класс может унаследовать все поля и методы родительского класса, добавить свои поля и методы и переопределить часть методов родительского класса на свои. Наследование осуществляется вызовом функции class() в конструкторе класса с указанием имен всех родительских классов:
function p = circle(x,y,R)
% @circle\circle.m
center = figure(x,y);
p.r = R;
p = class(p,'circle',center);
End
Здесь мы предположили, что уже имеется класс "геометрическая фигура" с единственным атрибутом - координатами центра. Данный пример реализует конструктор класса "окружность", который наследует классу "геометрическая фигура" и добавляет свой атрибут - радиус. Отметим, что созданный объект будет генерировать логическое значение "истина" при передаче его в функцию isa() как при проверке, является ли он кругом, так и при проверке, является ли он геометрической фигурой.
Для реализации множественного наследования нужно при вызове функции class() в конструкторе указать список всех родителей через запятую:
p = class(p,'name', parent1, parent2, …);
Создаваемый класс унаследует все данные и методы всех родителей. Если у нескольких родителей совпадут имена каких-либо методов, то приоритет при вызове у потомка получат методы классов, идущих первыми в списке параметров функции class().
Как уже отмечалось, все данные у объекта являются защищенными - они доступны только для методов данного класса и его потомков. Методы класса при этом можно вызывать откуда угодно. MATLAB позволяет создавать также методы, полностью недоступные для внешнего использования. Для этого в папке класса нужно завести подпапку с именем private и в ней разместить все M-файлы методов, которые необходимо скрыть от внешнего использования - они будут доступны только из других методов данного класса и его потомков.
Задания
1.Создать базу данных, хранящую информацию о пациентах поликлиники: ФИО, дата рождения, даты посещения, температура и артериальное давление в дни посещений. Необходимо воспользоваться массивом структур.
2.Ту же самую базу данных оформить в виде массива ячеек.
3.Основную часть той же базы данных оформить как массив структур, а данные измерений - как массив ячеек.
4.Реализовать класс "дата" - набор полей день, месяц, год. Определить операции сложения и вычитания дат, а также умножение на число и печать в формате дд.мм.гг для объектов этого класса.
5.Реализовать класс чисел, поддерживающих арифметику с заданной точностью - количеством знаков после запятой (например, два). Определить все операторы и методы для объектов этого класса.
#$+Написание расширений MATLAB на языке C.
Существует несколько способов взаимодействия системы MATLAB с внешними программами:
Во-первых, можно написать расширение MATLAB на обычных языках программирования, таких как С/С++, Fortran и т.п.
Наоборот, можно вызвать вычислительное ядро MATLAB из своей программы и получить результат вычислений среды MATLAB в свою программу. В этом случае MATLAB выступает как своего рода вычислительный сервер для внешней программы.
Средой MATLAB можно управлять при помощи команд DDE (Dynamic Data Exchange) или ActiveX (OLE) Automation.
Наконец, можно просто обмениваться данными со средой MATLAB через MAT-файлы, структура которых описана в документации к системе.
Нетрудно видеть, что предоставляемый набор способов взаимодействия с системой внешних программ является весьма полным и гибким. Практически все эти способы описаны в документации к системе, однако, так как наиболее часто используется первый способ, сосредоточимся на нем.
Обычно, пользователь-программист работает с системой MATLAB непосредственно, кодируя необходимые алгоритмы на встроенном языке MATLAB. Встроенный язык весьма удобен для написания математических алгоритмов - писать и отлаживать на нем программу обычно занимает значительно меньше времени, чем на обычных языках программирования.
Однако, довольно часто эффективность подобных программ оставляет желать лучшего. Как правило, подобная ситуация возникает, когда алгоритм плохо векторизуется, например, при обработке матриц нельзя выразить этот алгоритм, пользуясь векторными операторами языка MATLAB, и приходится писать вложенные циклы, перераспределять память и т.п. В этом случае программа на языке С будет исполняться во много раз быстрее аналогичной программы на языке MATLAB. А ведь довольно часто время счета математической задачи может исчисляться сутками.
Кроме того, иногда возникают ситуации, когда те или иные сложные алгоритмы уже были реализованы на других языках программирования. В этом случае также будет быстрее не переписывать весь алгоритм на языке MATLAB, но написать относительно небольшой "переходник" от MATLAB к уде существующему на языке С модулю и вызвать его из среды MATLAB напрямую.
Для того, чтобы написать модуль, расширяющий набор функций MATLAB, нужно создать обычную динамическую библиотеку (DLL) для Microsoft Windows со специальным набором функций (интерфейсом). Данная библиотека может быть создана при помощи многих компиляторов языка С/С++, однако, наиболее часто используется компилятор разработки компании Microsoft - Visual C++, одной из последних версий которого (5.0) мы и воспользуемся для подготовки наших примеров.
В документации по системе MATLAB для подобных расширений употребляется термин MEX-файл (Matlab EXtension), и сама MATLAB по этому расширению имени файла может определить, что данный модуль является ее расширением. Хотя, специально назначать MEX в качестве расширения имени файла необязательно, MATLAB может прекрасно работать и со стандартным расширением подобных динамических библиотек - DLL.
Папка \matlab\extern на диске, на котором установлена система, содержит все необходимые файлы-заголовки для программ на С (каталог \matlab\extern\include), а также несколько примеров модулей (\matlab\extern\examples), реализующих некоторые расширения системы, которые приведены целиком в виде исходных текстов программ на С.
Кроме того, имеется папка \matlab\extern\src в которой приведен исходный текст некоторых вспомогательных функций, весьма облегчающий отладку модулей расширения MATLAB.
Программные интерфейсы
В модулях-расширениях MATLAB (MEX-файлах) для обмена параметрами всех типов с вычислительной средой MATLAB используется ровно одна структура, хотя и чрезвычайно гибкая. Называется эта структура - mxArray (Matlab Extension Array).
Все типы данных MATLAB - массивы, скаляры, строки, клеточные и многомерные массивы, объекты и т.п. выражаются при помощи этой единственной структуры.
Программный интерфейс создаваемой динамической библиотеки тоже достаточно прост. Должна быть обязательно экспортирована единственная функция с двумя параметрами - входным массивом структур mxArray и выходным массивом структур mxArray. Соответственно, задача модуля расширения заключается в том, чтобы на основе входной информации создать выходной массив.
Прототип интерфейсной функции объявлен в заголовочном файле \matlab\extern\include\mex.h следующим образом: