Как использовать Move на 2-Dimension Dynami c Массивы целых / вещественных? - PullRequest
0 голосов
/ 08 апреля 2020

В моем приложении Delphi Rio я использую множество двухмерных массивов Dynami c. Чтобы ускорить некоторые операции, я бы хотел использовать команду перемещения вместо копирования. Я мог бы заставить его работать для 1D динамических c массивов, но для 2D или выше я не мог. Во втором измерении после выполненного перемещения (A, B, size) элементы массива B указывают на один и тот же адрес памяти элементов массива A, т.е. ссылки на B A. На самом деле я хочу работать с B отдельно от A. См. мой код:

program ProjArrayMove;

    {$APPTYPE CONSOLE}

    {$R *.res}

    uses
      System.SysUtils;

    Type

         Arrayint   = Tarray<Integer>;
         Arrayreal  = TArray<Real>;
         MatrixInt  = Array of Arrayreal;
    var
        vectorA, vectorB : Arrayint;
        matA, matB       : MatrixInt;
        idx,idy          : integer;
    begin
         TRY
           // =============== TESTING WITH 1D DYNAMIC ARRAYS OF SIMPLE TYPES ==================
              Writeln('==============================================================');
              Writeln('========= TESTING 1-DIMENSION DYNAMIC ARAYS ==================');
              Writeln('===============================================================');

              readln;
              Writeln('------- Fills Vector A ----------------------------');
              SetLength(vectorA,5);
              for idx :=0 to 4 do
              begin
                    vectorA[idx] := (idx+1) * 10;
                    Writeln('VectorA : [' + idx.tostring + ']  '  +
                              Format('Address : %p  [%d]' ,[PPointer(@VectorA), VectorA[idx] ]) );
              end;

              readln;
              Writeln('--------------------------------------------------');
              Writeln('------ Moves VectorA to VectorB ------------------');

              SetLength(VectorB,Length(vectorA));
              Move(VectorA[0],VectorB[0],SizeoF(VectorA[0]) * Length(VectorA));
              for idx :=0 to 4 do
                    Writeln('VectorB : [' + idx.tostring + ']  '  +
                              Format('Address : %p  [%d]' ,[PPointer(@VectorB), VectorB[idx] ]) );

              readln;
              Writeln('---------------------------------------------------');
              Writeln('------ Changes VectorB contents  ------------------');

              for idx :=0 to 4 do
              begin
                    vectorB[idx] := (idx+1) * 200;
                    Writeln('VectorB : [' + idx.tostring + ']  '  +
                              Format('Address : %p  [%d]' ,[PPointer(@VectorB), VectorB[idx] ]) );
              end;

              readln;
              Writeln('--------------------------------------------------');
              Writeln('------ Checking Vector A  ------------------------');

              for idx :=0 to 4 do
                    Writeln('VectorA : [' + idx.tostring + ']  '  +
                              Format('Address : %p  [%d]' ,[PPointer(@VectorA), VectorA[idx] ]) );
              Writeln;
              Writeln('CONCLUSION : ===>>  MOVE command works fine for 1-Dimension Dynamic Arrays!');
              readln;


    //=========================== TESTING WITH MATRIX 2D DYNAMIC ARRAYS OF SIMPLE TYPES ==================

              Writeln('===============================================================');
              Writeln('========= TESTING 2-DIMENSIONS DYNAMIC ARAYS ==================');
              Writeln('===============================================================');
              readln;
              Writeln('------ Fills MatrixA -----------------------------');
              SetLength(matA,5,2);
              for idx :=0 to 4 do
                 for idy := 0 to 1 do
                 begin
                       matA[idx][idy] := (idx +1) * (idy +1);
                        Writeln('Mat A : [' + idx.tostring + '][' + idy.tostring +']  '  +
                                  Format('Address : %p  [%f]' ,[PPointer(@matA[idx][idy]), matA[idx][idy] ] ));
                 end;

              readln;
              Writeln('-------------------------------------------------');
              Writeln('------ Move MatrixA to MatrixB ------------------');

              SetLength(matB,length(matA));
              //move(matA[0],MatB[0],Sizeof(matA[0]) * length(matA));
              move(matA,MatB,Sizeof(matA[0]) * length(matA));
              for idx :=0 to 4 do
              begin
                   Setlength(MatB[idx],length(matA[idx]));
                   //Move(matA[idx][0],matB[idx][0],sizeof(matB[idx][0]) * length(matB[idx]) );
                   Move(matA[idx],matB[idx],sizeof(matB[idx][0]) * length(matB[idx]) );
                   for idy := 0 to 1 do
                   begin
                          Writeln('Mat B : [' + idx.tostring + '][' + idy.tostring +']  '  +
                                  Format('Address : %p  [%f]' ,[PPointer(@matB[idx][idy]), matB[idx][idy] ] ));

                   end;
              end;

              readln;

              Writeln('-------------------------------------------------');
              Writeln('------ Change MatrixB content  ------------------');
              readln;

              for idx :=0 to 4 do
                 for idy := 0 to 1 do
                 begin
                      matB[idx][idy] := 100.5 * (idx+1) * (idy +1);
                      Writeln('Mat B : [' + idx.tostring + '][' + idy.tostring +']  '  +
                              Format('Address : %p  [%f]' ,[PPointer(@matB[idx][idy]), matB[idx][idy] ] ));
                 end;

              Writeln('-------------------------------------------------');
              Writeln('------ Checking Matrix A ------------------------');
              readln;

              for idx :=0 to 4 do
                 for idy := 0 to 1 do
                        Writeln('Mat A : [' + idx.tostring + '][' + idy.tostring +']  '  +
                                 Format('Address : %p  [%f]' ,[PPointer(@matA[idx][idy]), matA[idx][idy] ] ));


              Writeln;
              Writeln('CONCLUSION : ===>>  MOVE command DOES NOT WORK on 2-Dimensions Dynamic Arrays!');
              readln;

          except
            on E: Exception do
                 begin
                       Writeln(E.ClassName, ': ', E.Message);
                       readln;
                 end;
          end;
    end.

Что-то не так или отсутствует в команде перемещения для второго измерения?

Спасибо!

1 Ответ

2 голосов
/ 08 апреля 2020

Ваши проблемы начинаются здесь:

Move(matA, matB, Sizeof(matA[0]) * Length(matA));

Вы передаете динамический массив c в Move. Динамический массив c реализован как указатель на первый элемент массива. Поэтому вы перезаписываете указатель.

Возможно, вы хотели сделать что-то вроде этого:

Move(matA[0], matB[0], Sizeof(matA[0]) * Length(matA));

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

Фактически, если вы просто удалите эту строку кода, она должна работать. Обратите внимание, что я не проверил подробно, поэтому могут быть другие дефекты.

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

Вы хотите использовать функцию, подобную этой:

function CloneMatrixInt(const matrix: array of ArrayInt): MatrixInt;
var
  i: Integer;
begin
  SetLength(Result, Length(matrix));
  for i := 0 to High(Result) do
    Result[i] := Copy(matrix[i]);
end;

Обратите внимание, что это по существу идентично на ответ на ваш предыдущий вопрос. Почему это так?

Вам нужно перестать думать о MatrixInt как о многомерном массиве. Это не. Это просто массив. Это массив, элементы которого, в свою очередь, также являются массивами. Технически это зазубренный массив. Но вам просто нужно думать о нем как об одномерном массиве.

Таким образом, функция для его клонирования работает следующим образом:

  1. Выделите целевой массив.
  2. L oop над массивом назначения, клонирующим каждый элемент из исходного массива.

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

Теперь представьте, что у вас есть массив MatrixInt. Допустим,

type
  ArrayMatrixInt = array of MatrixInt;

Хорошо, клонируйте это так:

function CloneArrayMatrixInt(const arrayMatrix: array of MatrixInt): ArrayMatrixInt;
var
  i: Integer;
begin
  SetLength(Result, Length(arrayMatrix));
  for i := 0 to High(Result) do
    Result[i] := CloneMatrixInt(matrix[i]);
end;

Угадайте что, тот же самый код, что и раньше!

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

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

...