Как составить матрицу в делфи

0 / 0 / 0

Регистрация: 27.04.2014

Сообщений: 77

1

Как создать и вывести матрицу?

12.05.2014, 10:16. Показов 7401. Ответов 3


Студворк — интернет-сервис помощи студентам

как создать и вывести матрицу



0



long399

Модератор

2547 / 1645 / 895

Регистрация: 16.10.2013

Сообщений: 4,866

Записей в блоге: 13

12.05.2014, 12:49

2

Цитата
Сообщение от 123Roman
Посмотреть сообщение

как создать и вывести матрицу

К примеру для матрицы разм. 10х10:

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
uses math;
 
var
  Form1: TForm1;
  a: array [1..11,1..11] of integer;
 
procedure TForm1.Button1Click(Sender: TObject);
var i,j:integer;
begin
for i := 1 to 11 do
for j := 1 to 11 do
a[i,j]:=randomrange(1, 100); // заполним массив
end;
 
procedure TForm1.Button2Click(Sender: TObject);
var i,j:integer;
begin
for i := 1 to 11 do
for j := 1 to 11 do
stringgrid1.Cells[i,j]:=inttostr(a[i,j]); // выведем массив
end;



0



Puporev

Почетный модератор

64287 / 47586 / 32739

Регистрация: 18.05.2008

Сообщений: 115,182

12.05.2014, 13:45

3

Цитата
Сообщение от long399
Посмотреть сообщение

К примеру для матрицы разм. 10х10:

Delphi
1
a: array [1..11,1..11] of integer;

И где логика?



0



Модератор

2547 / 1645 / 895

Регистрация: 16.10.2013

Сообщений: 4,866

Записей в блоге: 13

12.05.2014, 20:13

4

Puporev, с индексами напутал. Сделал для 11х11, Вы правы.



0



IT_Exp

Эксперт

87844 / 49110 / 22898

Регистрация: 17.06.2006

Сообщений: 92,604

12.05.2014, 20:13

4

Многомерные массивы в Delphi (Матрицы в Delphi)

Как видите, в квадратных скобках перечислены через запятую размеры строк и столбцов. Заметьте, что мы объявили массив от 0 до 3 — это будет четыре эле­мента и от 0 до 4, что будет составлять 5 элементов.

Работа с таким массивом также достаточно простая 

var

t:array[0..3, 0..4] of integer; begin

t[0][0]=1;

t[l][0]=2;

t[2] [0]=3;

t[3] [0]=4;

t[l][1] =5;

end; 

После выполнения этого примера таблица примет вид:

1 0 0 0 0

2  5 0 0 0

0 0 0 0

0 0 0 0

Двумерность не предел, и вы можете создавать и трехмерные массивы. Давайте просто посмотрим на содержимое листинга 10.6, и вам все должно стать понятно.

var

t:array[0..3, 0..4, 0..2] of integer;

begin t[0][0][0]:=1;

t[l][0][0]:=2;

t [2] [0] [0] : =3;

t[3][0][0]:=4;

t[l][1][0]:=5;

end;

Использование массивов очень удобно, но иногда может оказаться излишне рас­точительным. Дело в том, что двумерный массив из 100×100 строк может «съесть» достаточно много оперативной памяти (100*100* на длину строки).

Да, в наше время о памяти мало кто заботится, но это ужасно, и ничего хорошего в этом нет.

Помоги проекту! Расскажи друзьям об этом сайте:

  • 40
  • 1
  • 2
  • 3
  • 4
  • 5

Время на прочтение
11 мин

Количество просмотров 12K

В этой статье рассматривается проектирование типов для работы с объектами линейной алгебры: векторами, матрицами, кватернионами. Показано классическое применение механизма перегрузки стандартных операций, использование приёма «Copy On Write» и аннотаций.

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

TVec3 = array[1..3] of Extended;
TMatrix3x3 = array[1..3, 1..3] of Extended;
function MVMult(M: TMatrix3x3; V: TVec3): TVec3;

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

  • Версия среды разработки Delphi XE7
  • Для числовых данных использовать тип Extended, как наиболее точный;
  • Использовать динамические массивы для хранения данных, т.к. размеры векторов и матриц могут быть любыми и в отладчике их удобно смотреть;
  • Элементы векторов и матриц нумеруются с 1, элементы кватернионов с 0;
  • Для вычислений использовать операции +, -, *, /;
  • Обеспечить возможность передачи по значению и копирование векторов и матриц операцией «:=»;
  • Обеспечить возможность автоматизированной инициализации векторов и матриц по заранее заданным размерам.

Проектирование

Для удобства определим вспомогательные типы:

TAbstractVector = array of Extended;
TAbstractMatrix = array of array of Extended;

Теперь определим структуры кватерниона, вектора и матрицы:

TQuaternion = record
private
  FData: array[0..3] of Extended;
  procedure SetElement(Index: Byte; Value: Extended);
  function GetElement(Index: Byte): Extended;
public    
  property Element[Index: Byte]: Extended read GetElement write SetElement; default;
end;

TVector = record
private
  FData: TAbstractVector;
  FCount: Word;
  procedure SetElement(Index: Word; Value: Extended);
  function GetElement(Index: Word): Extended;    
public    
  constructor Create(ElementsCount: Word);
  property Count: Word read FCount;
  property Elements[Index: Word]: Extended read GetElement write SetElement; default;
end;

TMatrix = record
private
  FData: TAbstractMatrix;
  FRowsCount: Word;
  FColsCount: Word;
  procedure SetElement(Row, Col: Word; Value: Extended);
  function GetElement(Row, Col: Word): Extended;  
public  
  constructor Create(RowsCount, ColsCount: Word);
  property RowCount: Word read FRowsCount;
  property ColCount: Word read FColsCount;  
  property Elements[Row, Col: Word]: Extended read GetElement write SetElement; default;
end;

Мы используем именно record, т.к. перегрузка операций для конструкции class в Delphi не разрешена. К тому же у объектов record есть полезное свойство — их данные разворачиваются в памяти по месту объявления, другими словами, объект record не является ссылкой на экземпляр в динамической памяти.
Однако, в нашем случае элементы векторов и матриц будут храниться в динамическом массиве, объект которого является ссылкой. Поэтому будет удобно использовать явные конструкторы. Они выполняют инициализацию внутренних полей, выделяя память под требуемое число элементов:

constructor TVector.Create(ElementsCount: Word);
begin
  FCount := ElementsCount;
  FData := nil;
  SetLength(FData, FCount);
end;

constructor TMatrix.Create(RowsCount, ColsCount: Word);
begin
  FRowsCount := RowsCount;
  FColsCount := ColsCount;
  FData := nil;
  SetLength(FData, FRowsCount, FColsCount);
end;

Кватерниону на данном этапе конструктор не требуется, т.к. он хранит данные в статическом массиве и разворачивается в памяти по месту своего объявления.
Для доступа к элементам здесь служат свойства-индексаторы, их удобно сделать default, чтобы опускать имя. Доступ к запрашиваемому элементу происходит после проверки его индекса на допустимые значения. Показана реализация для TVector:

function TVector.GetElement(Index: Word): Extended;
begin
  {$R+}
  Result := FData[Pred(Index)];
end;

procedure TVector.SetElement(Index: Word; Value: Extended);
begin
  {$R+}
  FData[Pred(Index)] := Value;
end;

На этом этапе, чтобы создавать наши объекты, придется использовать такой код:

var
  V: TVector;
. . .
V := TVector.Create(3);
V[1] := 1;
V[2] := 2;
V[3] := 3;

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

TQuaternion = record
public 
  . . . 
  constructor Create(Q: TAbstractVector);
  class operator Implicit(V: TAbstractVector): TQuaternion;
end;

TVector = record
public    
  . . .
  constructor Create(V: TAbstractVector); overload;  
  class operator Implicit(V: TAbstractVector): TVector;
end;

TMatrix = record
public  
  . . .
  constructor Create(M: TAbstractMatrix); overload;  
  class operator Implicit(M: TAbstractMatrix): TMatrix;
end;

И реализация:

constructor TQuaternion.Create(Q: TAbstractVector);
begin
  if Length(Q) <> 4 then
    raise EMathError.Create(WRONG_SIZE);
  Move(Q[0], FData[0], SizeOf(FData));
end;

class operator TQuaternion.Implicit(V: TAbstractVector): TQuaternion;
begin
  Result.Create(V);
end;

constructor TVector.Create(V: TAbstractVector);
begin
  FCount := Length(V);
  FData := Copy(V);
end;

class operator TVector.Implicit(V: TAbstractVector): TVector;
begin
  Result.Create(V);
end;

constructor TMatrix.Create(M: TAbstractMatrix);
var
  I: Integer;
begin
  FRowsCount := Length(M);
  FColsCount := Length(M[0]);
  FData := nil;  
  SetLength(FData, FRowsCount, FColsCount);
  for I := 0 to Pred(FRowsCount) do      
    FData[I] := Copy(M[I]);    
end;

class operator TMatrix.Implicit(M: TAbstractMatrix): TMatrix;
begin
  Result.Create(M);
end;

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

var
  V: TVector;
  M: TMatrix;
. . .  
  V := [4, 5, 6];
  //
  M := [[1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]];

Перегрузка операций

Здесь, для примера, будет реализована только перегрузка операции * для умножения матрицы на вектор. Остальные операции можно посмотреть в прикрепленном к статье файле. А полный перечень возможностей по перегрузке — здесь.

TMatrix = record
public   
  . . . 
  class operator Multiply(M: TMatrix; V: TVector): TVector;
end;

class operator TMatrix.Multiply(M: TMatrix; V: TVector): TVector;
var
  I, J: Integer;
begin
  if (M.FColsCount <> V.FCount) then
    raise EMathError.Create(WRONG_SIZE);
  Result.Create(M.FRowsCount);
  for I := 0 to M.FRowsCount - 1 do
    for J := 0 to M.FColsCount - 1 do
      Result.FData[I] := Result.FData[I] + M.FData[I, J] * V.FData[J];
end;

Первый аргумент метода Multiply() — матрица слева от знака *, второй аргумент — вектор-столбец, находящийся справа от знака *. Результатом произведения является новый вектор, объект которого создается в процессе вычисления. В случае несовпадения количества столбцов матрицы и числа элементов вектора возбуждается исключение. Вот как выглядит использование этой операции в программе:

var
  V, VResult: TVector;
  M: TMatrix;
. . .
  VResult := M * V;

Удобно применять функции-обертки, чтобы конструировать анонимные вектора и матрицы из литералов массивов «налету»:

function TVec(V: TAbstractVector): TVector;
begin
  Result.Create(V);
end;

function TMat(M: TAbstractMatrix): TMatrix;
begin
  Result.Create(M);
end;

function TQuat(Q: TAbstractVector): TQuaternion;
begin
  Result.Create(Q);
end;

Использование оберток выглядит следующим образом. Показан эквивалент выражению из предыдущего примера:

  V := TMat([[1, 2, 3],
             [4, 5, 6],
             [7, 8, 9]]) * TVec([4, 5, 6]);

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

TMatrix = record
public  
  . . .
  function Inv: TMatrix;
end;

function TMatrix.Inv: TMatrix;
var
  Ipiv, Indxr, Indxc: array of Integer;
  DimMat, I, J, K, L, N, ICol, IRow: Integer;
  Big, Dum, Pivinv: Extended;
begin
  // Алгоритм Жордана.
  if (FRowsCount <> FColsCount) then
    raise EMathError.Create(NOT_QUAD);
  Result := Self;
  DimMat := FRowsCount;
  SetLength(Ipiv, DimMat);
  SetLength(Indxr, DimMat);
  SetLength(Indxc, DimMat);
  IRow := 1;
  ICol := 1;
  for I := 1 to DimMat do
  begin
    Big := 0;
    for J := 1 to DimMat do
      if (Ipiv[J - 1] <> 1) then
        for K := 1 to DimMat do
          if (Ipiv[K - 1] = 0) then
            if (Abs(Result[J, K]) >= Big) then
            begin
              Big := Abs(Result[J, K]);
              IRow := J;
              ICol := K;
            end;
    Ipiv[ICol - 1] := Ipiv[ICol - 1] + 1;
    if (IRow <> ICol) then
      for L := 1 to DimMat do
      begin
        Dum := Result[IRow, L];
        Result[IRow, L] := Result[ICol, L];
        Result[ICol, L] := Dum;
      end;
    Indxr[I - 1] := IRow;
    Indxc[I - 1] := ICol;
    if Result[ICol, ICol] = 0 then
      raise EMathError.Create(SINGULAR);
    Pivinv := 1.0 / Result[ICol, ICol];
    Result[ICol, ICol] := 1.0;
    for L := 1 to DimMat do
      Result[ICol, L] := Result[ICol, L] * Pivinv;
    for N := 1 to DimMat do
      if (N <> ICol) then
      begin
        Dum := Result[N, ICol];
        Result[N, ICol] := 0.0;
        for L := 1 to DimMat do
          Result[N, L] := Result[N, L] - Result[ICol, L] * Dum;
      end;
  end;
  for L := DimMat downto 1 do
    if (Indxr[L - 1] <> Indxc[L - 1]) then
      for K := 1 to DimMat do
      begin
        Dum := Result[K, Indxr[L - 1]];
        Result[K, Indxr[L - 1]] := Result[K, Indxc[L - 1]];
        Result[K, Indxc[L - 1]] := Dum;
      end;
end;

Копирование по значению

Использование динамических массивов для хранения элементов векторов и матриц приводит к тому, что при попытке копировать их целиком в объекте-приёмнике (том, что слева от «:=») создается копия ссылки на этот динамический массив.
Например, попытка сохранить значение матрицы М после вычисления выражения приведет к инвертированию так же и матрицы MStore.

var
  M, MStore: TMatrix;  
. . .  
  MStore := M;
  M := M.Inv;

Для того, чтобы корректно реализовать копирование по значению, используем тот факт, что по отрицательному смещению от адреса первого элемента динамического массива, наряду со значением длины, хранится счетчик ссылок на этот массив. Если значение счетчика 0, то менеджер памяти этот массив освобождает. Если значение счетчика 1, это означает, что существует только одна ссылка на экземпляр массива в памяти.
Следовательно при копировании мы должны проанализировать значение счетчика и, если оно больше 1, то создать полноценную копию массива, скопировав его в объект-приёмник поэлементно. Ниже представлен код функции, которая возвращает True только в том случае, когда значение счетчика ссылок переданного во входном параметре динамического массива превышает 1.

{$POINTERMATH ON}
function NotUnique(var Arr): Boolean;
begin
  Result := (PCardinal(Arr) - 2)^ > 1;
end;

В какой момент следует выполнять полное копирование? Это достаточно дорогая по времени операция, поэтому нет смысла выполнять её при обращении к элементу вектораматрицы на чтение. Будь у нас хоть тысяча ссылок на оригинал, если сам он не подвергается никаким изменениям, то все они остаются одинаковыми. Следовательно, копировать нужно только при обращении к элементу на запись. Для этого модифицируем методы SetElement() для векторов и матриц, добавив в начале проверку на уникальность экземпляра массива FData:

procedure TVector.SetElement(Index: Word; Value: Extended);
begin
  {$R+}
  CheckUnique;
  FData[Pred(Index)] := Value;
end;

procedure TVector.CheckUnique;
begin
  if NotUnique(FData) then
    FData := Copy(FData);
end;

procedure TMatrix.SetElement(Row, Col: Word; Value: Extended);
begin
  {$R+}
  CheckUnique;
  FData[Pred(Row), Pred(Col)] := Value;
end;

procedure TMatrix.CheckUnique;
var
  I: Integer;
begin
  if NotUnique(FData) then
    begin
      FData := Copy(FData);
      for I := 0 to Pred(FRowsCount) do
        FData[i] := Copy(FData[i]);
    end;
end;

Таким образом, при попытке изменить значение элемента произойдет проверка на уникальность ссылки, и, если таковая не подтвердится, будет создана поэлементная копия, в которую и будет внесено изменение.

Аннотации и автоматическая инициализация

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

var
  V: TVector;  
  M: TMatrix;
begin
  // V[1] := 1;                   // Ошибка: объект не создан
  V := TVector.Create(4);         // Явный вызов конструктора
  M := TMatrix.Create(4, 4);      // Явный вызов конструктора
  // V := [1, 0, 0, 0];           // Неявный вызов конструктора  
  // V := M * TVec([1, 0, 0, 0]); // Неявный вызов конструктора
  V[1] := 1;                      // Корректное обращение к элементу: объект создан 

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

TMovement = record
  R: TVector;
  V: TVector;
  W: TVector;
  Color: TVector;
end;

TMovementScheme = class
private
  FMovement: array[1..100] of TMovement;
  FOrientation: TMatrix;  
end;

Требуется разработать способ автоматизированной инициализации всех полей типа TVector и TMatrix: выделить память для векторов и матриц в соответствии с нужным количествам элементов и размерами. В этом нам поможет механизм аннотаций (или атрибутов, в терминах Delphi) — средство, которое позволяет дополнять типы произвольными метаданными. Так, для каждого вектора должно быть заранее известно количество его элементов, для матрицы — число строк и столбцов.
Создадим класс, инкапсулирующий данные о размерностях, по правилам создания классов атрибутов.

TDim = class(TCustomAttribute)
private
  FRowCount: Integer;
  FColCount: Integer;
public
  constructor Create(ARowCount: Integer; AColCount: Integer = 0); overload;
  property RowCount: Integer read FRowCount;
  property ColCount: Integer read FColCount;
end;

constructor TDim.Create(ARowCount: Integer; AColCount: Integer = 0);
begin
  FRowCount := ARowCount;
  FColCount := AColCount;
end;

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

TMovement = record
  [TDim(3)] R: TVector;
  [TDim(3)] V: TVector;
  [TDim(3)] W: TVector;
  [TDim(4)] Color: TVector;
end;

TMovementScheme = class
private
  FMovement: array[1..100] of TMovement;
  [TDim(3, 3)] FOrientation: TMatrix;  
end;

Ниже приведен код, осуществляющий инициализацию объектов типа TVector и TMatrix на основе информации, взятой из атрибутов.

procedure Init(Obj, TypeInfoOfObj: Pointer; Offset: Integer = 0);
const
  DefaultRowCount = 3;
  DefaultColCount = 3;
  VectorTypeName = 'TVector';
  MatrixTypeName = 'TMatrix';
var
  RTTIContext: TRttiContext;
  Field : TRttiField;
  ArrFld: TRttiArrayType;
  I: Integer;
  Dim: TCustomAttribute;
  RowCount, ColCount: Integer;
  OffsetFromArray: Integer;
begin
  for Field in RTTIContext.GetType(TypeInfoOfObj).GetFields do
  begin
    if Field.FieldType <> nil then
    begin
      RowCount := DefaultRowCount;
      ColCount := DefaultColCount;
      for Dim in Field.GetAttributes do
      begin
        RowCount := (Dim as TDim).RowCount;
        ColCount := (Dim as TDim).ColCount;
      end;
      if Field.FieldType.TypeKind = tkArray then
      begin
        ArrFld := TRttiArrayType(Field.FieldType);
        if ArrFld.ElementType.TypeKind = tkRecord then
        begin
          for I := 0 to ArrFld.TotalElementCount - 1 do
          begin
            OffsetFromArray := I * ArrFld.ElementType.TypeSize;
            if ArrFld.ElementType.Name = VectorTypeName then
              PVector(Integer(Obj) +
                      Field.Offset +
                      OffsetFromArray +
                      Offset)^ := TVector.Create(RowCount)
            else if ArrFld.ElementType.Name = MatrixTypeName then
              PMatrix(Integer(Obj) +
                      Field.Offset +
                      OffsetFromArray +
                      Offset)^ := TMatrix.Create(RowCount, ColCount)
            else
              Init(Obj, ArrFld.ElementType.Handle, Field.Offset + OffsetFromArray);
          end;
        end;
      end
      else if Field.FieldType.TypeKind = tkRecord then
      begin
        if Field.FieldType.Name = VectorTypeName then
          PVector(Integer(Obj) +
                  Field.Offset +
                  Offset)^ := TVector.Create(RowCount)
        else if Field.FieldType.Name = MatrixTypeName then
          PMatrix(Integer(Obj) +
                  Field.Offset +
                  Offset)^ := TMatrix.Create(RowCount, ColCount)
        else
          Init(Obj, Field.FieldType.Handle, Field.Offset)
      end;
    end;
  end;
end;

Процедура Init() получает на вход адрес объекта-контейнера и его RTTI-данные. Далее происходит рекурсивный обход всех полей контейнера, и для всех встречных полей с именами типов «TVector» и «TMatrix» будут явно вызваны их конструкторы.
Доработаем класс TMovementScheme с применением процедуры Init():

TMovementScheme = class
. . .
public
  constructor Create;  
end;

constructor TMovementScheme.Create;  
begin
  Init(Self, Self.ClassInfo);
end;

Вариант вызова Init() для произвольной записи:

var
  Movement: TMovement;
. . .  
  Init(@Movement, TypeInfo(TMovement));

По умолчанию, Init() создает вектора с тремя элементами, а матрицы размером 3х3, поэтому в объявлении типов TMovement и TMovementScheme атрибуты [TDim(3)] и [TDim(3, 3)] можно опустить, оставив только [TDim(4)].

К статье прилагается файл, в котором реализация описываемых идей приведена в полном объёме.

The specific problem in your code is that the array you are declaring is dynamic. That is, the bounds are not fixed and can be changed at run-time.

In older versions of Delphi (XE6 and earlier) is it simply not possible to declare dynamic array constants. In XE7 and later it is possible but the syntax is different than for fixed array constants.

In all versions, if you declare a constant array with specified (and therefore fixed) bounds you can then specify the contents of the constant array thus:

const
  Matrix : array[0..7, 0..7] of String = 
   (
    ('A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7'),
    ('B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7'),
    ('C0', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7'),
    ('D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7'),
    ('E0', 'E1', 'E2', 'E3', 'E4', 'E5', 'E6', 'E7'),
    ('F0', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7'),
    ('G0', 'G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7'),
    ('H0', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'H7')
   );

If your array needs to be dynamic in a version of Delphi earlier than XE6 then you cannot initialise such an array with a declaration like this.

If you are using Delphi XE7 or later, then you can use the alternate syntax for declaring a dynamic array constant. This is very similar to the syntax for a fixed array constant but uses square braces [] instead of regular parentheses ():

 const
   Matrix : array of array of String =
   [
    ['A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7'],
    ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7'],
    ['C0', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7'],
    ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7'],
    ['E0', 'E1', 'E2', 'E3', 'E4', 'E5', 'E6', 'E7'],
    ['F0', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7'],
    ['G0', 'G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7'],
    ['H0', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'H7']
   ];

Hybrid Solution for Older Delphi Versions

If you are using an older version of Delphi then even with a dynamic array, if you have some initial state (bounds and content) that you would like to initialise it with then you could use a fixed array constant to define that initial state and then initialise your dynamic array at run-time from that constant, something like:

const
  MX_DIM  = 8;
  MX_DEFAULT : array[0..MX_DIM - 1, 0..MX_DIM - 1] of String =
   (
    ('A0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7'),
    ('B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7'),
    ('C0', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7'),
    ('D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7'),
    ('E0', 'E1', 'E2', 'E3', 'E4', 'E5', 'E6', 'E7'),
    ('F0', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7'),
    ('G0', 'G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7'),
    ('H0', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'H7')
   );

// Then in your code:

var
  x, y: Integer;
  Matrix: array of array of String;
begin
  // Initialise 'Matrix' from MX_DEFAULT:

  SetLength(Matrix, MX_DIM, MX_DIM);
  for x := 0 to Pred(MX_DIM) do
    for y := 0 to Pred(MX_DIM) do
      Matrix[x, y] := MX_DEFAULT[x, y];
end;

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

Понятие о массиве

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

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

Пример

const
n=3; m=5;

type

vec = array [0..m-1] of integer;

mas = array [0..n-1] of vec;

var

massiv:mas;

В
данном примере объявлен двумерный
массив massiv.Massivпредставляет матрицу, состоящую из 3
строк и 5 столбцов.

Возможны
и другие способы описания двумерных
массивов:

var

massiv : array
[1..3,1..5] of integer;

Так
же, как и одномерные массивы, двумерные
массивы можно описывать как типизированные
константы:

соnst

massiv : array [1..2,1..3] of integer =
((5,8,1),(3,1,9));

Двумерные
массивы объявляются так же, как и
одномерные, за исключением того, что в
квадратных скобках после зарезервированного
слова arrayстоят два
разделенных запятыми определения
индексов, относящихся к двум размерностям
массива. Каждое определение индекса
независимо одно от другого. Индексы
могут быть порядкового типа. Типы
первого и второго индексов могут
различаться.

По
такому же принципу можно объявить не
только двумерные, но и многомерные
массивы. Размерность массивов ограничивает
только объем памяти конкретной ЭВМ.

Пример описания
трехмерных массивов aиb

type

vec = array [1..3] of integer;

mas = array [1..5] of vec;

tmas = array [1..8] of mas;

var

a: tmas;

b: array
[1..3,1..5,1..8] of integer;

Над
многомерными массивами как единым
целым можно выполнить три действия:
операции отношения =, < >; оператор
присваивания. Эти действия выполняются
аналогично, как и с одномерными массивами.

В
многомерных массивах последний индекс
возрастает первым, например:

var

A: array [1..2,1..3] of real;

A[1,1]

A[1,2]

A[1,3]

A[2,1]

A[2,2]

A[2,3]

Первый
индекс обозначает номер строки в
матрице, второй индекс – номер столбца.
Например, запись А[2,3] делает доступным
для обработки значение элемента,
находящегося во второй строке третьего
столбца массива А.

Для
работы с двумерными массивами обычно
используются вложенные циклы. Часто
применяется структура алгоритма
вложенных циклов со счетчиками (рис.
6.1).

Рис.
6.1. Схема алгоритма вложенных циклов

Обнуление
элементов двумерного массива можно
выполнить, используя вложенные операторы
for:

for K:=1 to 3 do

for L:=1 to 5 do

A[K,L]:=0;

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

Пример

{i– номер столбца;j– номер строки таблицыStringGrid1.}

Edit1.text:=StringGrid1.Cells[i,j];

После
выполнения этого оператора присваивания
в поле Edit1.Textбудет содержаться текст из ячейки
таблицыStringGrid1.

Начиная
с четвертой версии Delphi,
были введенымногомерныединамические
массивы
.Размеры динамического
массива можно установить во время
выполнения программы.

Описание
двумерного динамического массива целых
чисел:

var

mas: array of array
of integer;

Выполнять
операции с динамическим массивом можно
только после задания размеров этого
массива процедурой

SetLength(mas,n1,n2);

Параметрами этой процедуры являются:
mas– имя массива;n1
– количество строк;n2 –
количество столбцов.

Нумерация элементов динамического
массива начинается с нуля.

Пример.Обнулить элементы динамического
двумерного массива, имеющего 5 строк и
5 столбцов:

var mas: array of
array of real;

i,j: integer;

begin

SetLength(mas,5,5);

for i:=0 to 4 do

for j:=0 to 4 do

mas[i,j]:=0;

end;

Пример программирования
с использованием двумерного массива

Задание. Подсчитать сумму всех
элементов в двумерном массиве А [4,5].

1. Разработка алгоритма (рис. 6.2):

а)
входные данные: A
– массив, состоящий из вещественных
чисел;

б)
выходные данные: sum– вещественная
переменная,
сумма всех элементов массива;

в)
промежуточные данные: i, j– счетчики циклов.

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

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

Рис.6.2.
Схема алгоритма вычисления суммы
элементов массива

2. Разработка формы (рис. 6.3, табл. 6.1):

Рис.6.3.
Внешний вид формы

Таблица 6.1

Используемые
компоненты

Имя

компонента

Страница
палитры компонент

Настраиваемые
свойства

Значение

1.
Form1

Caption

Лабораторная
работа №6

2.
Label1

Standard

Caption

Введите
элементы матрицы

3.
Label2

Standard

Caption

Сумма
элементов равна

4.
Label3

Standard

Caption

5.
Button1

Standard

Caption

Рассчет

6.
StringGrid1

Additional

FixedCols

0

RowCount

4

FixedRows

0

Options

[goEditing,
goTabs]

3. Текст
программы:

unit
Unit1;

interface

uses Windows, Messages, SysUtils, Classes,
Graphics, Controls, Forms, Dialogs, StdCtrls, Grids;

type

TForm1 =
class(TForm)

StringGrid1:
TStringGrid;

Label1: TLabel;

Button1: TButton;

Label2: TLabel;

Label3: TLabel;

procedure
Button1Click(Sender: TObject);

private

{ Private
declarations }

public

{ Public
declarations }

end;

var

Form1: TForm1;

implementation

{$R
*.DFM}

procedure
TForm1.Button1Click(Sender: TObject);

const
m=4; n=5;

type

mas=array
[0..m-1,0..n-1] of real;

var

i,j:integer;

a:mas; sum:real;

begin

//ввод
массива

for
i:=0 to m–1 do

for j:=0 to n–1
do

a[i,j]:=StrToFloat(StringGrid1.Cells[j,i]);

sum:=0;

for
i:=0 to m-1 do

for j:=0 to n-1 do

sum:=sum+a[i,j];

//вывод
результата

Label3.Caption:=FloatToStr(sum);

end;

end.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

Понравилась статья? Поделить с друзьями:

Не пропустите также:

  • Код ошибки 0x0 windows 10 как исправить
  • Пробита проводка в стене как исправить
  • Как составить анкету для подруг
  • Как исправить своего парня
  • Как тревору найти фургон с эмблемой deludamol

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии