开发者

How to swap two rows of a two-dimensional array, and why would it work?

Summarization:

Please check the comments below from David, Uwe, and other experts.

================================================================================

The following code swaps two rows in a two-dimensional, dynamic array of double values. I am wondering: (1) whether the following code is a best practice of swapping two rows of a two-dimensional array? If not, then what is the best practice to do this kind of job? (2) why would the following code work? I mean, isn't two-dim开发者_JS百科ensional array a continuous contiguous section of memory? Does the following code work only by luck? Any suggestion is appreciated!

    unit Unit5;

    interface

    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs;

    type
      TAADouble = array of array of Double;

      TForm5 = class(TForm)
        procedure FormShow(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;

    var
      Form5: TForm5;

    procedure SwapRows(arr: TAADouble; row0, row1: Integer);

    implementation

    {$R *.dfm}

    procedure SwapRows(arr: TAADouble; row0, row1: Integer);
    var
      Tmp: Integer;
    begin
      {$IFDEF FPC}
      Tmp := PtrUInt(arr[row0]);
      PtrUInt(arr[row0]) := PtrUInt(arr[row1]);
      PtrUInt(arr[row1]) := Tmp;
      {$ELSE}
      Tmp := Integer(arr[row0]);
      Integer(arr[row0]) := Integer(arr[row1]);
      Integer(arr[row1]) := Tmp;
      {$ENDIF}

    end;

    procedure TForm5.FormShow(Sender: TObject);
    var
      tmpArray: TAADouble;
      I, J: Integer;
      rowStr: string;
    begin
      SetLength(tmpArray, 10, 10);
      rowStr := '';

      for I := 0 to 9 do
        for J := 0 to 9 do
          tmpArray[I][J] := I * J;

      for I := 0 to 9 do
      begin
        rowStr := '';
        for J := 0 to 9 do
          rowStr := rowStr + FloatToStr(tmpArray[I][J]) + '  ';
        OutputDebugString(PWideChar(rowStr));
      end;

      SwapRows(tmpArray, 3, 4);

      for I := 0 to 9 do
      begin
        rowStr := '';
        for J := 0 to 9 do
          rowStr := rowStr + FloatToStr(tmpArray[I][J]) + '  ';
        OutputDebugString(PWideChar(rowStr));
      end;
    end;

    end.


You ask:

Does the following code work only by luck?

Well, yes, you are relying on implementation specific details.

In fact the correct way to write it is perfectly natural and simple:

type
  TDoubleArray = array of Double;
  TDoubleMatrix = array of TDoubleArray;

procedure SwapRows(M: TDoubleMatrix; Row1, Row2: Integer);
var
  Temp: TDoubleArray;
begin
  Temp := M[Row1];
  M[Row1] := M[Row2];
  M[Row2] := Temp;
end;

You need to declare an intermediate type for the row, TDoubleArray, so that you can perform the assignment to Temp in the swap routine.

A 2D constant size array

array [1..M] of array [1..N] of TMyType

is a contiguous block of memory.

A 2D dynamically size array as you have is not. Indeed it can even be ragged in the sense that the rows have different numbers of columns. So you can have, say, a triangular matrix.


A dynamic array is implemented as a pointer to a memory block representing that array. So a two-dimensional dynamic array is actually a pointer to an array of pointers. Thats why swapping the row(-pointer)s actually works.

See David's answer for a cleaner approach.

Update: If you are allowed to use generics you might as well do this:

procedure <SomeClassOrRecord>.SwapRows<T>(var arr: TArray<T>; row0, row1: Integer);
var
  Tmp: T;
begin
  Tmp := arr[row0];
  arr[row0] := arr[row1];
  arr[row1] := Tmp;
end;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜