Перегрузка операций FPC 2.0.x
 
Зачем это нужно Как это сделать? Примеры использования
Перегрузка функций   Вычисление многочлена матрицы Реализация "больших" множеств
    Вычисление квадратного корня из матрицы  
       

Зачем это нужно?

Очень часто при работе с типами данных, определенных пользователями, не хватает возможности работать с этими типами, как со встроенными в язык, т.е., например для сложения матриц не использовать вызов процедуры
MatrixAdd(C, A, B);
, а записать эту операцию в виде
C := A + B;

Или для сравнения матрицы с нулевой (или единичной) использовать не
If isEqual(mx, mxO) or isEqual(mx, mxE)
, а
If (mx = mxO) or (mx = mxE)...


В решении этой задачи могут помочь перегруженные операции.

Как это сделать?

Прежде всего - чтобы перегрузка была доступна, программа (или модуль, если это делается в модуле) должна компилироваться с ключом {$mode objfpc}. При использовании модулей директива {$mode objfpc} обязательна только для модуля, содержащего реализацию перегрузки, вызывающая программа может такой директивы не иметь...

Ну, и второе: для того, чтобы использовать в программе перегруженные операции, их нужно сначала определить. Это делается так:

Operator {перегружаемая операция} ({список параметров} ) {возвращаемое значение};

где:
{перегружаемая операция} - одна из следующих:
  1. арифметическая (+, -, *, /, **, mod, div)
  2. сравнения (=, <, <=, >, >=)
  3. присваивания (:=)
  4. логическая (or, and, xor, not)
{список параметров} - те параметры, к которым будет применяться {перегружаемая операция}. Параметров в списке должно быть ровно 2 (первый параметр - тот что находится слева от знака операции, второй - тот, который находится справа), причем один из них должен обязательно иметь тип, определенный пользователем, т.е. нельзя переопределить, например, свою операцию сложения двух целых чисел, а вот определить операцию сложения матрицы с числом - можно.

{возвращаемое значение} - то, что вернется в вызывающую программу как результат выполнения операции. Например, при операции сложения 2-х матриц, результатом также является матрица:
Operator + (Const m1, m2: TMatrix) R: TMatrix;
Begin
  { Заполняем матрицу R нужными значениями, и она вернется как результат операции }
End;

После этого описания в программе можно делать так:
Var A, B, C: TMatrix;
Begin
  ...
  C := A + B; { матрица - сумма присвоится переменной C }
  ...
End.


Перегрузка функций


Кроме перегрузки операций (переопределения операций для пользовательских типов) в FPC введена еще и перегрузка функций, т.е. в программе может быть несколько процедур/функций с одинаковыми именами (сюда же относятся и Operator-ы), при условии, что их списки параметров неодинаковы.
Т.е. можно сделать так:
function test(a: integer): integer;
begin
  result := 0;
end;

{ Разрешено - список параметров отличается от списка предыдущей функции }
function test(a: double): integer;
begin
  result := 0;
end;

{ Ошибка: несмотря на то, что тип результата другой, списки параметров одинаковы, а это недопустимо }
function test(a: integer): double;
begin
  result := 0.0;
end;


Эта возможность поможет, например, в таком случае.
Допустим, была переопределена операция + для матриц:
operator + (const mx1, mx2: TMatrix) r: TMatrix;
var i, j: integer;
begin
  for i := 1 to n do
    for j := 1 to n do
      r[i, j] := mx1[i, j] + mx2[i, j];
end;

но иногда модет понадобиться и операция сложения матрицы с числом, т.е. увеличение каждого элемента матрицы на какую-то величину. Без перегрузки функций здесь не обойтись:
operator + (const mx: TMatrix; const X: integer) r: TMatrix;
var i, j: integer;
begin
  for i := 1 to n do
    for j := 1 to n do
      r[i, j] := mx[i, j] + X;
end;

(параметры первой и второй Operator + различны, следовательно ошибки не будет)

Теперь можно использовать как сложение матриц, так и сложение матрицы с числом:
Var A, B, C: TMatrix;
Begin
  A := A + 2;
  C := A + B;
End.


Внимание: перегруженные операции не являются коммутативными, если типы переменных слева и справа от знака операции различны, т.е. если можно записать
A := A + 2;
это совсем не значит, что можно сделать и
A := 2 + A;

Компилятор сразу сообщит об ошибке: "Operator is not overloaded" ("Операция не перегружена")... Для того, чтобы иметь возможность использовать оба варианта сложения матрицы с числом, придется определить еще одну операцию:
operator + (const X: integer; const mx: TMatrix) r: TMatrix;
begin
  r := mx + X; { Пользуемся уже определенным сложением с другим порядком операндов }
end;


Примеры использования:

1. Вычисление многочлена матрицы


Здесь:
Как вычислить заданный многочлен от матрицы A приведен пример вычисления заданного многочлена от матрицы A ...

С использованием перегрузки операторов та же программа будет выглядеть вот так:
{$mode objfpc}

const
  size = 4;
  
type
  TMatrix = array[1 .. size, 1 .. size] of double;

operator * (const a, b: TMatrix) m: TMatrix;
var i, j, k: integer;
begin
  for i := 1 to size do
    for j := 1 to size do begin
      m[i, j] := 0;
      for k := 1 to size do
        m[i, j] := m[i, j] + a[i, k] * b[k, j]
    end;
end;

operator * (const a: TMatrix; const f: double) m: TMatrix;
var i, j: integer;
begin
  for i := 1 to size do
    for j := 1 to size do
      m[i, j] := f * a[i, j]
end;

operator + (const a, b: TMatrix) m: TMatrix;
var i, j: integer;
begin
  for i := 1 to size do
    for j := 1 to size do
      m[i, j] := a[i, j] + b[i, j]
end;

{ Возведение матрицы в степень }
operator ** (const a: TMatrix; const pow: integer) m: TMatrix;
var i, j: Integer;
begin
  if pow = 0 then begin
    for i := 1 to size do
      for j := 1 to size do
        m[i, j] := Byte(i = j);
    exit
  end;

  m := a;
  for i := 1 to pred(pow) do
    m := m * a;
end;

procedure matrixPrint(a: TMatrix);
var i, j: integer;
begin
  for i := 1 to size do begin
    for j := 1 to size do
      write(a[i, j]:9:2);
    writeln
  end
end;


const
  n = 3;
  p: array[1 .. n] of double = (1.0, -2.0, 3.0);

const
  a: TMatrix = (
    (10, 11, 14, 16),
    (12, 17, 10, 16),
    ( 8, 12, 12,  7),
    ( 8,  5, 17,  1)
  );
  
var
  Res: TMatrix;
  i: Integer;
  
begin
  matrixPrint(a);
  
  for i := 1 to n do
    res := res + (a ** (n - i)) * p[i]; { Все вычисление записывается в одну строчку }
  
  matrixPrint(Res)
end.





Free Web Hosting