unit Matrix; // модуль Matrix { Здесь описываются функции для работы с матрицами (сложение, вычитание, умножение, ...) } interface // интерфейсная часть; type MyMatrix = class { Класс для работы с матрицами MyMatrix Ниже описаны зоны видимости: private - приватная зона видимости. Это значит, что все переменные и функции, описанные в области могут использоваться ТОЛЬКО внутри класса MyMatrix. К ним не получить доступ извне. Например, MyMatrix.matrix - вызовет ошибку (что-то по типу undefined index, но хз как именно в паскале) public - публичная зона видимости. Это значит, что ко всем переменным и функциям, описанным в этой области можно получить доступ извне. Что и используется в программе. } private { List - это тоже класс, встроенный в компилятор. Здесь впервые встречается List. В кратце опишу что это. Представляет список на базе динамического массива. Это тип данных, похожий на динамический массив, но имеет несколько отличий. По функционалу похож на линковый стэк. Приведу пример, допустим нужно сделать список с название list, размерностью 4 и значениями: 1, 2, 4, 3. Если бы это был простой массив в Pascal, то пришлось бы написать следующее: var list: array of ineger; SetLength(list, 4); list[0] := 1; list[1] := 2; list[2] := 4; list[3] := 3; Тоже самое, но с использованием списка: var list: List; // указываем переменной list тип данных - List. list := new List(); // создаем пустой лист без размерности (0). list.Add(1); // добавляем в список значение, и автоматически увеличиваем размерность листа на 1 list.Add(2); // добавляем в список значение, и автоматически увеличиваем размерность листа на 1 list.Add(4); // добавляем в список значение, и автоматически увеличиваем размерность листа на 1 list.Add(3); // добавляем в список значение, и автоматически увеличиваем размерность листа на 1 Это удобно тем, что не нужно указывать индекс при добавлении, а также заранее указывать размерность, т.к. в любой момент мы можем добавить или удалить элемент. Получить доступ к элементу можно как и из обычного массива: writeln(list[2]); // вернет 4. (в точности, как в строке 26) } matrix: List>; // создаем переменную для матрицы, которая будет создаваться при вызове класса MyMatrix с типом данных - двумерный список indices: List; // создаем переменную для хранения индексов массивов (это те самые цифры над матрицей, номера элементов и т.д.) matrix_size: integer; // переменная, которая хранит размерность матрицы function GetValue(x, y: integer): integer; // функция (заголовок) для получения значение из матрицы по переданным индексам x и y procedure SetValue(x, y: integer; value: integer); // функция (заголовок) для изменения значения в матрице по переданным индексам x и y public { Здесь используется понятие конструктор, логика такая же как и в c++/c#/java При создании экземпляра Класса MyMatrix будет вызван конструктор. Помимо этого, здесь используется прием - перегрузка функции, 4 строчки ниже - перегрузка констуктора. Что такое перегрузка и для чего она нужна? Допустим, есть функция sum(a, b: integer) ..., которая складывает переданные ей целочисленные значения a и b. Если передать в эту функцию a, b: float, тогда возникнет ошибка. Но не писать же новую функцию sum_float, например? Для этого создается функция с точно таким же названием, в данном случае sum(a, b: float) и в ней производится такая же операция сложения. Конструкторы ниже перегружены, ниже описание в каком случае какой конструктор } constructor Create(obj: MyMatrix); // конструктор constructor Create(size: integer); // конструктор, если передан размер матрицы constructor Create(path: string); // конструктор, если передан путь к файлу с матрицей constructor Create(count_cell, plate_columns: integer); // конструктор, если переданы размеры платы - например, 4х3 property Size: integer read matrix_size; // свойство size property Value[x, y: integer]: integer read GetValue write SetValue; default; // свойство value class function operator+(a, b: MyMatrix): MyMatrix; // перегрузка оператора сложения для двух матриц class function operator*(a, b: MyMatrix): MyMatrix; // перегрузка оператора умножения для двух матриц function Transpose(): MyMatrix; // заголовок функции транспонирования function DiagonalAmount(): integer; // заголовок функции нахождения диагональной суммы function Gamma(): MyMatrix; // заголовок функции нахождения гаммы для матрицы function Mins(): List<(integer, integer)>; {функция нахождения матрицы L ()} function Swap(a, b: integer): MyMatrix; // заголовок функции перестановки элементов местами function ToList(): List>; // заголовок функции, возвращающей матрицу в виде листа function ToString(): string; override; // заголовок функции, возвращающей матрицу в виде строки (для вывода в виде текста) function ToHalfString(): string; // // заголовок функции, возвращающей матрицу в виде строки треугольником (ну, типо отсекает одинаковые значения в матрице) end; implementation // исполняемая часть - здесь находится код всех функций, описанных выше; constructor MyMatrix.Create(obj: MyMatrix); {конструктор для заполнения матрицы obj: MyMatrix} begin { Идентификаторы: x, y - используются для хранения итераций циклов; } self.matrix := new List>(); // создаем пустой двумерный лист, который будет хранить матрицу self.indices := new List(); // создаем пустой лист, который будет хранить индексы матрицы (номера элементов над матрицей) for var y := 0 to obj.Size - 1 do begin // цикл от 0 до размерности матрицы - 1 (т.к. все динамические структуры данных начинаются с нуля) self.matrix.Add(new List()); self.indices.Add(obj.indices[y]); for var x := 0 to obj.Size - 1 do begin self.matrix[y].Add(obj[x, y]); end; end; self.matrix_size := obj.Size; end; constructor MyMatrix.Create(size: integer); { конструктор для заполнения если передан размер матрицы } begin { Идентификаторы: x, y - используются для хранения итераций циклов; } // self - это указатель на текущий экземпляр класса. В интерфейсе были описаны переменные и функции self.matrix := new List>(); // создаем пустой двумерный лист для матрицы self.indices := new List(); // создаем пустой двумерный лист для номеров элементов {цикл ниже создает пустую матрицу переданной размерности size и заполняет список номеров элементов} for var y := 0 to size - 1 do begin // цикл от 0 до size - 1 (размерность матрицы) self.matrix.Add(new List()); // добавляем на каждой итерации цикла пустой лист в матрицу self.indices.Add(y + 1); // добавляем на каждой итерации цикла в список номеров элементов значение y + 1 () for var x := 0 to size - 1 do begin // цикл от 0 до size - 1 (размерность матрицы) self.matrix[y].Add(0); // заполняем вложенный список нулями end; // конец цикла end; // конец цикла self.matrix_size := size; // устанавливаем приватной переменной matrix_size локальное значение size end; constructor MyMatrix.Create(path: string); {конструктор для заполнения если передан путь к файлу} begin { Идентификаторы: text - используется для хранения данных из файла; line - используется для хранения строки из файла count - используется для подсчета количества строк в файле (это будет являться размерностью матрицы) } matrix := new List>(); // создаем пустой двумерный лист для матрицы self.indices := new List(); // создаем пустой двумерный лист для номеров элементов var text: PABCSystem.Text; // используется для хранения данных из файла assign(text, path); // связываем дескриптор файла с переменной text reset(text); // открываем файл для чтения var count := 0; // используется для подсчета количества строк в файле (это будет являться размерностью матрицы) while not eof(text) do begin // цикл от нуля, до конца файла (eof(text) - это конец файла) var line := ReadLnString(text); // используется для хранения строки из файла if (line.Length > 0) then begin // условие, если длина строки > 0 {строчку ниже можно расписать так: value := line.Split(' ').Select(i -> StrToInt(i)).ToList(); Split это встроенная функция, она разбивает строку на массив используя разделитель Конкретно в этом случае, она разбивает строку 0 1 2 3 4 5 6 на массив [0, 1, 2, 3, 4, 5, 6] Функция Select - тоже встроенная, она применяется к массивам. Вкратце, она перебирает массив поэлементно и делает операцию (i -> StrToInt(i)) В данном случае, она преобразовывает строку '0' в целочисленное значение 0, и так для каждого элемента. После чего используется ToList() - это встроенная функция, используется для преобразования массива в список (т.к. у нас везде используется список, это необходимо) matrix.Add(value); // и в самом конце, с помощью метода(функции) встроенной в класс List - .Add() мы добавляем результат в наш список } matrix.Add(line.Split(' ').Select(i -> StrToInt(i)).ToList()); self.indices.Add(count + 1); // добавляем в список индексов новый индекс на каждой итерации + 1 count := count + 1; // увеличиваем счетчик на 1. Кстати, можно писать inc(count); только что об этом узнал end; end; close(text); // закрываем файл self.matrix_size := matrix.Count; // устанавливаем приватной переменной matrix_size значение размерности полученной матрицы end; constructor MyMatrix.Create(count_cell, plate_columns: integer); {конструктор для заполнения если передано количество элементов и количество строк платы, используется для вычисления матрицы D} begin { Идентификаторы: count_cell - количество элементов на плате plate_columns - количество строк в плате x, y - используются для хранения итераций циклов; pos1, pos2 - используются для хранения } matrix := new List>(); // тоже самое, как и выше self.indices := new List(); // тоже самое, как и выше for var y := 0 to count_cell - 1 do begin // цикл от 0 до количества элементов платы - 1 matrix.Add(new List()); // все как и раньше self.indices.Add(y + 1); // добавляем в список индексов новый индекс (y + 1) на каждой итерации for var x := 0 to count_cell - 1 do begin // цикл от 0 до количества элементов платы - 1 { Тут рассчет расстояний, вкратце - как он сделан div - целочисленное деление, пример: 55 div 6 = 9 (деление, при котором остаток отбрасывается) 52 div 7 = 7 (деление, при котором остаток отбрасывается) div - получение остатка от деления, пример: 55 mod 6 = 1 (55 - 54) 52 mod 7 = 3 (52 - 49) Соответственно - переменные pos1 и pos2 это массивы, которые хранят значения: pos1[0] := y div plate_columns; pos1[1] := y mod plate_columns; pos2[0] := x div plate_columns; pos2[1] := x mod plate_columns; Функция abs - встроенная в паскаль, считает модуль переданного числа. Получаем сумму модуля разницы pos1[0] - pos2[0] И pos1[1] - pos2[1]). И так на каждой итерации рассчитывается расстояния каждого элемента к каждому в зависимости от заданного кол-ва столбцов платы (plate_columns) abs(pos1[0] - pos2[0]) + abs(pos1[1] - pos2[1]) - рассчитанное значение } var pos1 := (y div plate_columns, y mod plate_columns); var pos2 := (x div plate_columns, x mod plate_columns); matrix[y].Add(abs(pos1[0] - pos2[0]) + abs(pos1[1] - pos2[1])); // добавляем в матрицу рассчитанное значение end; end; self.matrix_size := matrix.Count; // устанавливаем приватной переменной matrix_size значение размерности полученной матрицы end; function MyMatrix.GetValue(x, y: integer): integer; {функция для получения значения из матрицы} begin result := self.matrix[y][x]; end; procedure MyMatrix.SetValue(x, y: integer; value: integer); {функция для изменения значения из матрицы} begin self.matrix[y][x] := value; end; class function MyMatrix.operator+(a, b: MyMatrix): MyMatrix; {перегрузка оператора сложения для матриц} begin var res := new MyMatrix(a.Size); for var y := 0 to a.Size - 1 do begin for var x := 0 to a.Size - 1 do begin res[x, y] := a[x, y] + b[x, y]; end; end; result := res; end; class function MyMatrix.operator*(a, b: MyMatrix): MyMatrix; {перегрузка оператора умножения для матриц} begin var res := new MyMatrix(a.Size); for var y := 0 to a.Size - 1 do begin for var x := 0 to a.Size - 1 do begin for var z := 0 to a.Size - 1 do begin res[x, y] := res[x, y] + a[z, y] * b[x, z]; end; end; end; result := res; end; function MyMatrix.Transpose(): MyMatrix; {функция транспонирования матрицы} begin var res := new MyMatrix(self); for var y := 1 to matrix_size - 1 do begin for var x := 0 to y - 1 do begin res[x, y] := res[x, y] xor res[y, x]; res[y, x] := res[x, y] xor res[y, x]; res[x, y] := res[x, y] xor res[y, x]; end; end; result := res; end; function MyMatrix.DiagonalAmount(): integer; {функция нахождения диагональной суммы} begin var sum := 0; for var i := 0 to self.matrix_size - 1 do begin sum += self[i,i]; end; // Round(); - функция округления, возвращает целочисленное число result := Round(sum / 2); end; function MyMatrix.Gamma(): MyMatrix; {функция нахождения матрицы Gamma} begin var res := new MyMatrix(self); for var y := 0 to res.Size - 1 do begin var tmp := res[y, y]; for var x := 0 to res.Size - 1 do begin res[x, y] := res[x, y] - tmp; end; end; result := res; end; function MyMatrix.Mins(): List<(integer, integer)>; {функция нахождения матрицы L ()} begin var mins := new List<(integer, integer)>(); var min := -1; for var y := 0 to self.Size - 1 do begin for var x := y to self.Size - 1 do begin if (self[x, y] < min) then begin mins.Clear(); min := self[x, y]; end; if (self[x, y] = min) then begin mins.Add((x, y)); end; end; end; result := mins; end; function MyMatrix.Swap(a, b: integer): MyMatrix; { функция перестановки элементов местами использует XOR операцию Пример: a := 2; b := 3; чтобы присвоить переменной a значение 3, а после присвоить b значение 2 нужно либо использовать третью переменную: temp := a; a := b; b := temp; Либо использовать xor операцию: a := a xor b b := a xor b a := a xor b } begin var res := new MyMatrix(self); res.indices[a] := res.indices[a] xor res.indices[b]; res.indices[b] := res.indices[a] xor res.indices[b]; res.indices[a] := res.indices[a] xor res.indices[b]; for var i := 0 to res.Size - 1 do begin res[a, i] := res[a, i] xor res[b, i]; res[b, i] := res[a, i] xor res[b, i]; res[a, i] := res[a, i] xor res[b, i]; res[i, a] := res[i, a] xor res[i, b]; res[i, b] := res[i, a] xor res[i, b]; res[i, a] := res[i, a] xor res[i, b]; end; result := res; end; function MyMatrix.ToList(): List>; begin // просто возвращает матрицу в виде двумерного листа result := self.matrix; end; function MyMatrix.ToString(): string; begin // возвращает матрицу в виде строки, нужна для "красивой" отрисовки матрицы в окошко результата var res: string; for var y := -1 to self.Size - 1 do begin if (y = -1) then begin res += ' '; end else begin if (indices[y].ToString().Length = 1) then begin res += ' ' + indices[y] + '|'; end else begin res += indices[y] + '|'; end; end; for var x:=0 to self.Size - 1 do begin var tmp := ''; if (y = -1) then begin tmp := indices[x].ToString() end else begin tmp := self[x, y].ToString(); end; case tmp.Length of 1: res += ' ' + tmp; 2: res += ' ' + tmp; 3: res += ' ' + tmp; end; end; if not (y = -1) then begin res += ' | '; end; res += #10; end; result := res; end; function MyMatrix.ToHalfString(): string; begin // возвращает матрицу в виде треугольника (половины), нужна для "красивой" отрисовки матрицы в окошко результата var res: string; for var y := -1 to self.Size - 1 do begin if (y = -1) then begin res += ' '; end else begin if (indices[y].ToString().Length = 1) then begin res += ' ' + indices[y] + '|'; end else begin res += indices[y] + '|'; end; end; for var x := 0 to self.Size - 1 do begin if (x > y - 1) then begin var tmp := ''; if (y = -1) then begin tmp := indices[x].ToString() end else begin tmp := self[x, y].ToString(); end; case tmp.Length of 1: res += ' ' + tmp; 2: res += ' ' + tmp; 3: res += ' ' + tmp; end; end else begin res += ' '; end; end; if not (y = -1) then begin res += ' | '; end; res += #10; end; result := res; end; end.