在 Delphi 中对 Double 数组的动态数组进行排序
Sorting dynamic Array of Array of Double in Delphi
我在 Delphi 中创建了一个动态矩阵:
AMatrix : Array of Array of Double;
假设我以这种方式初始化它。
SetLength(Amatrix,1000,10);
并使用一些值填充此矩阵。现在,我想根据存储在第二个维度上特定位置(从 0 到 9)中的特定值对第一个维度上的 1000 个项目进行排序。
有没有办法创建一个可以直接应用于 Amatrix 而无需创建其他数据结构(TList 或 TArray)的 TComparer?
使用@R.Hoeck 的想法,我写了一个简单的演示程序,它创建了一个双精度的二维数组,用随机数据填充它并使用给定的列作为键对其进行排序。
排序是通过为该列创建 index/value 的列表然后对列表进行排序来完成的。
排序后,未排序数组中的数据被复制到另一个将排序的数组中。
unit MatrixDemoMain;
interface
uses
Winapi.Windows, Winapi.Messages,
System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
System.Generics.Defaults,
System.Generics.Collections;
type
TMyRecord = record
Index : Integer;
Value : Double;
end;
TDynArray2OfDouble = array of array of Double;
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure DisplayArray(const Title : String;
const Arr : TDynArray2OfDouble);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
AMatrix : TDynArray2OfDouble;
SortedMatrix : TDynArray2OfDouble;
I, J : Integer;
SortCol : Integer;
Rec : TMyRecord;
List : TList<TMyRecord>;
begin
// Give dimension to unsorted array
SetLength(AMatrix, 10, 3);
// Give dimension to the sorted array
SetLength(SortedMatrix, High(AMatrix) + 1, High(AMatrix[0]) + 1);
// Select column to use as sort key
SortCol := 2;
// Fill matrix with random data
for I := 0 to High(AMatrix) do begin
for J := 0 to High(AMatrix[0]) do
AMatrix[I, J] := Random(1000);
end;
DisplayArray('Unsorted:', AMatrix);
// Create a list to sort data
List := TList<TMyRecord>.Create;
try
for I := 0 to High(AMatrix) do begin
Rec.Index := I;
Rec.Value := AMatrix[I, SortCol];
List.Add(Rec);
end;
// Sort the list
List.Sort(TComparer<TMyRecord>.Construct(
function(const Left, Right: TMyRecord): Integer
begin
if Left.Value = Right.Value then
Result := 0
else if Left.Value > Right.Value then
Result := 1
else
Result := -1;
end)
);
// Copy data from unsorted matrix using sorted list
for I := 0 to High(AMatrix) do
SortedMatrix[I] := AMatrix[List[I].Index];
DisplayArray('Sorted on column ' + SortCol.ToString, SortedMatrix);
finally
FreeAndNil(List);
end;
end;
// This procedure will display an array into the memo
procedure TForm1.DisplayArray(
const Title : String;
const Arr : TDynArray2OfDouble);
var
I, J : Integer;
Buf : String;
begin
Memo1.Lines.Add(Title);
for I := 0 to High(Arr) do begin
Buf := I.ToString + ') ';
for J := 0 to High(Arr[0]) do
Buf := Buf + Arr[I, J].ToString + ' ';
Memo1.Lines.Add(Buf);
end;
end;
end.
运行 演示将在备忘录中显示此结果:
Unsorted:
0) 293 547 16
1) 238 503 543
2) 428 950 663
3) 150 444 739
4) 160 388 373
5) 945 382 417
6) 863 818 392
7) 344 131 617
8) 91 458 330
9) 370 717 191
Sorted on column 2
0) 293 547 16
1) 370 717 191
2) 91 458 330
3) 160 388 373
4) 863 818 392
5) 945 382 417
6) 238 503 543
7) 344 131 617
8) 428 950 663
9) 150 444 739
Is there a way to create a TComparer<T>
that can be applied directly on [a variable of type array of array of Double
] without the need to create other data structures (TList or TArray)?
让我们试一试,但为简单起见,我们将使用整数:
program FailedAttempt;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils, Math, Generics.Defaults, Generics.Collections;
var
A: array of array of Integer;
begin
A :=
[
[5, 2, 1, 3, 6],
[1, 2, 6, 3, 2],
[1, 6, 7, 8, 3],
[5, 7, 4, 2, 1],
[0, 4, 9, 0, 5],
[4, 1, 8, 9, 6]
];
TArray.Sort<array of Integer>(A,
TComparer<array of Integer>.Construct(
function(const Left, Right: array of Integer): Integer
begin
if Left[2] < Right[2] then
Result := -1
else if Left[2] > Right[2] then
Result := +1
else
Result := 0;
end
)
);
for var i := 0 to High(A) do
begin
Writeln;
for var j := 0 to High(A[i]) do
Write(A[i, j]);
end;
Readln;
end.
不幸的是,这不会编译,因为 array of Integer
不是您可以用作 T
的有效类型。请注意,这就像您不能将 array of Integer
用作函数的 return 类型一样。解决方案也是一样的:创建一个定义为 array of Integer
.
的类型
program Solution1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils, Math, Generics.Defaults, Generics.Collections;
type
TIntArray = array of Integer;
var
A: array of TIntArray;
begin
A :=
[
[5, 2, 1, 3, 6],
[1, 2, 6, 3, 2],
[1, 6, 7, 8, 3],
[5, 7, 4, 2, 1],
[0, 4, 9, 0, 5],
[4, 1, 8, 9, 6]
];
TArray.Sort<TIntArray>(A,
TComparer<TIntArray>.Construct(
function(const Left, Right: TIntArray): Integer
begin
if Left[2] < Right[2] then
Result := -1
else if Left[2] > Right[2] then
Result := +1
else
Result := 0;
end
)
);
for var i := 0 to High(A) do
begin
Writeln;
for var j := 0 to High(A[i]) do
Write(A[i, j]);
end;
Readln;
end.
但是在 Delphi 的现代版本中,您不需要创建自己的类型(事实上,这是一个坏主意,因为不同的此类类型不兼容)。相反,只需使用确实定义为 array of Integer
的 TArray<Integer>
——这是一个动态整数数组,就像您的 array of Integer
:
program Solution2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils, Math, Generics.Defaults, Generics.Collections;
var
A: array of TArray<Integer>;
begin
A :=
[
[5, 2, 1, 3, 6],
[1, 2, 6, 3, 2],
[1, 6, 7, 8, 3],
[5, 7, 4, 2, 1],
[0, 4, 9, 0, 5],
[4, 1, 8, 9, 6]
];
TArray.Sort<TArray<Integer>>(A,
TComparer<TArray<Integer>>.Construct(
function(const Left, Right: TArray<Integer>): Integer
begin
if Left[2] < Right[2] then
Result := -1
else if Left[2] > Right[2] then
Result := +1
else
Result := 0;
end
)
);
for var i := 0 to High(A) do
begin
Writeln;
for var j := 0 to High(A[i]) do
Write(A[i, j]);
end;
Readln;
end.
如果实在无法改变A
的定义,可以使用强制转换:
program Solution3;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils, Math, Generics.Defaults, Generics.Collections;
var
A: array of array of Integer;
begin
A :=
[
[5, 2, 1, 3, 6],
[1, 2, 6, 3, 2],
[1, 6, 7, 8, 3],
[5, 7, 4, 2, 1],
[0, 4, 9, 0, 5],
[4, 1, 8, 9, 6]
];
TArray.Sort<TArray<Integer>>(TArray<TArray<Integer>>(A),
TComparer<TArray<Integer>>.Construct(
function(const Left, Right: TArray<Integer>): Integer
begin
if Left[2] < Right[2] then
Result := -1
else if Left[2] > Right[2] then
Result := +1
else
Result := 0;
end
)
);
for var i := 0 to High(A) do
begin
Writeln;
for var j := 0 to High(A[i]) do
Write(A[i, j]);
end;
Readln;
end.
最后,我还应该指出一个明显的问题:可以在不使用 TComparer<T>
的情况下对数据进行排序。 (实际上,在 Delphi 2009 年引入泛型之前,您被迫这样做。)
我在 Delphi 中创建了一个动态矩阵:
AMatrix : Array of Array of Double;
假设我以这种方式初始化它。
SetLength(Amatrix,1000,10);
并使用一些值填充此矩阵。现在,我想根据存储在第二个维度上特定位置(从 0 到 9)中的特定值对第一个维度上的 1000 个项目进行排序。
有没有办法创建一个可以直接应用于 Amatrix 而无需创建其他数据结构(TList 或 TArray)的 TComparer?
使用@R.Hoeck 的想法,我写了一个简单的演示程序,它创建了一个双精度的二维数组,用随机数据填充它并使用给定的列作为键对其进行排序。
排序是通过为该列创建 index/value 的列表然后对列表进行排序来完成的。
排序后,未排序数组中的数据被复制到另一个将排序的数组中。
unit MatrixDemoMain;
interface
uses
Winapi.Windows, Winapi.Messages,
System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
System.Generics.Defaults,
System.Generics.Collections;
type
TMyRecord = record
Index : Integer;
Value : Double;
end;
TDynArray2OfDouble = array of array of Double;
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure DisplayArray(const Title : String;
const Arr : TDynArray2OfDouble);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
AMatrix : TDynArray2OfDouble;
SortedMatrix : TDynArray2OfDouble;
I, J : Integer;
SortCol : Integer;
Rec : TMyRecord;
List : TList<TMyRecord>;
begin
// Give dimension to unsorted array
SetLength(AMatrix, 10, 3);
// Give dimension to the sorted array
SetLength(SortedMatrix, High(AMatrix) + 1, High(AMatrix[0]) + 1);
// Select column to use as sort key
SortCol := 2;
// Fill matrix with random data
for I := 0 to High(AMatrix) do begin
for J := 0 to High(AMatrix[0]) do
AMatrix[I, J] := Random(1000);
end;
DisplayArray('Unsorted:', AMatrix);
// Create a list to sort data
List := TList<TMyRecord>.Create;
try
for I := 0 to High(AMatrix) do begin
Rec.Index := I;
Rec.Value := AMatrix[I, SortCol];
List.Add(Rec);
end;
// Sort the list
List.Sort(TComparer<TMyRecord>.Construct(
function(const Left, Right: TMyRecord): Integer
begin
if Left.Value = Right.Value then
Result := 0
else if Left.Value > Right.Value then
Result := 1
else
Result := -1;
end)
);
// Copy data from unsorted matrix using sorted list
for I := 0 to High(AMatrix) do
SortedMatrix[I] := AMatrix[List[I].Index];
DisplayArray('Sorted on column ' + SortCol.ToString, SortedMatrix);
finally
FreeAndNil(List);
end;
end;
// This procedure will display an array into the memo
procedure TForm1.DisplayArray(
const Title : String;
const Arr : TDynArray2OfDouble);
var
I, J : Integer;
Buf : String;
begin
Memo1.Lines.Add(Title);
for I := 0 to High(Arr) do begin
Buf := I.ToString + ') ';
for J := 0 to High(Arr[0]) do
Buf := Buf + Arr[I, J].ToString + ' ';
Memo1.Lines.Add(Buf);
end;
end;
end.
运行 演示将在备忘录中显示此结果:
Unsorted:
0) 293 547 16
1) 238 503 543
2) 428 950 663
3) 150 444 739
4) 160 388 373
5) 945 382 417
6) 863 818 392
7) 344 131 617
8) 91 458 330
9) 370 717 191
Sorted on column 2
0) 293 547 16
1) 370 717 191
2) 91 458 330
3) 160 388 373
4) 863 818 392
5) 945 382 417
6) 238 503 543
7) 344 131 617
8) 428 950 663
9) 150 444 739
Is there a way to create a
TComparer<T>
that can be applied directly on [a variable of typearray of array of Double
] without the need to create other data structures (TList or TArray)?
让我们试一试,但为简单起见,我们将使用整数:
program FailedAttempt;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils, Math, Generics.Defaults, Generics.Collections;
var
A: array of array of Integer;
begin
A :=
[
[5, 2, 1, 3, 6],
[1, 2, 6, 3, 2],
[1, 6, 7, 8, 3],
[5, 7, 4, 2, 1],
[0, 4, 9, 0, 5],
[4, 1, 8, 9, 6]
];
TArray.Sort<array of Integer>(A,
TComparer<array of Integer>.Construct(
function(const Left, Right: array of Integer): Integer
begin
if Left[2] < Right[2] then
Result := -1
else if Left[2] > Right[2] then
Result := +1
else
Result := 0;
end
)
);
for var i := 0 to High(A) do
begin
Writeln;
for var j := 0 to High(A[i]) do
Write(A[i, j]);
end;
Readln;
end.
不幸的是,这不会编译,因为 array of Integer
不是您可以用作 T
的有效类型。请注意,这就像您不能将 array of Integer
用作函数的 return 类型一样。解决方案也是一样的:创建一个定义为 array of Integer
.
program Solution1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils, Math, Generics.Defaults, Generics.Collections;
type
TIntArray = array of Integer;
var
A: array of TIntArray;
begin
A :=
[
[5, 2, 1, 3, 6],
[1, 2, 6, 3, 2],
[1, 6, 7, 8, 3],
[5, 7, 4, 2, 1],
[0, 4, 9, 0, 5],
[4, 1, 8, 9, 6]
];
TArray.Sort<TIntArray>(A,
TComparer<TIntArray>.Construct(
function(const Left, Right: TIntArray): Integer
begin
if Left[2] < Right[2] then
Result := -1
else if Left[2] > Right[2] then
Result := +1
else
Result := 0;
end
)
);
for var i := 0 to High(A) do
begin
Writeln;
for var j := 0 to High(A[i]) do
Write(A[i, j]);
end;
Readln;
end.
但是在 Delphi 的现代版本中,您不需要创建自己的类型(事实上,这是一个坏主意,因为不同的此类类型不兼容)。相反,只需使用确实定义为 array of Integer
的 TArray<Integer>
——这是一个动态整数数组,就像您的 array of Integer
:
program Solution2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils, Math, Generics.Defaults, Generics.Collections;
var
A: array of TArray<Integer>;
begin
A :=
[
[5, 2, 1, 3, 6],
[1, 2, 6, 3, 2],
[1, 6, 7, 8, 3],
[5, 7, 4, 2, 1],
[0, 4, 9, 0, 5],
[4, 1, 8, 9, 6]
];
TArray.Sort<TArray<Integer>>(A,
TComparer<TArray<Integer>>.Construct(
function(const Left, Right: TArray<Integer>): Integer
begin
if Left[2] < Right[2] then
Result := -1
else if Left[2] > Right[2] then
Result := +1
else
Result := 0;
end
)
);
for var i := 0 to High(A) do
begin
Writeln;
for var j := 0 to High(A[i]) do
Write(A[i, j]);
end;
Readln;
end.
如果实在无法改变A
的定义,可以使用强制转换:
program Solution3;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils, Math, Generics.Defaults, Generics.Collections;
var
A: array of array of Integer;
begin
A :=
[
[5, 2, 1, 3, 6],
[1, 2, 6, 3, 2],
[1, 6, 7, 8, 3],
[5, 7, 4, 2, 1],
[0, 4, 9, 0, 5],
[4, 1, 8, 9, 6]
];
TArray.Sort<TArray<Integer>>(TArray<TArray<Integer>>(A),
TComparer<TArray<Integer>>.Construct(
function(const Left, Right: TArray<Integer>): Integer
begin
if Left[2] < Right[2] then
Result := -1
else if Left[2] > Right[2] then
Result := +1
else
Result := 0;
end
)
);
for var i := 0 to High(A) do
begin
Writeln;
for var j := 0 to High(A[i]) do
Write(A[i, j]);
end;
Readln;
end.
最后,我还应该指出一个明显的问题:可以在不使用 TComparer<T>
的情况下对数据进行排序。 (实际上,在 Delphi 2009 年引入泛型之前,您被迫这样做。)