unit ArrayEx;
{ ******************************************************************************
泛型动态数组的扩展.
wr960204 武稀松
博客地址:
http://www.raysoftware.cn/
2013.6.4
****************************************************************************** }
interface
uses System.Generics.Defaults,System.SysUtils;
type
TArrayEx = record
strict private
type
TEnumerator = class
private
FValue: TArray;
FIndex: NativeInt;
function GetCurrent: T;
public
constructor Create(const AValue: TArray);
function MoveNext: Boolean;
property Current: T read GetCurrent;
end;
public
function GetEnumerator(): TEnumerator;
strict private
FData: TArray;
function GetRawData: TArray;
function GetElements(Index: Integer): T;
procedure SetElements(Index: Integer; const Value: T);
private
class function EqualArray(A, B: TArray): Boolean; static;
class function CompareT(const A, B: T): Boolean; static;
class procedure CopyArray(var FromArray, ToArray: TArray;
FromIndex: NativeInt = 0; ToIndex: NativeInt = 0;
Count: NativeInt = -1); static;
class procedure MoveArray(var AArray: array of T;
FromIndex, ToIndex, Count: Integer); static;
class function DynArrayToTArray(const Value: array of T): TArray; static;
class function Min(A, B: NativeInt): NativeInt; static;
procedure QuickSort(const Comparer: IComparer;
L, R: Integer);
public // operators
class operator Implicit(Value: TArray): TArrayEx; overload;
class operator Implicit(Value: array of T): TArrayEx; overload;
(*
这个无解,Delphi不允许array of T作为返回值.也就是这个转换是被废了.只好用AssignTo
class operator Implicit(Value: TArrayEx):array of T; overload;
*)
class operator Implicit(Value: TArrayEx): TArray; overload;
class operator Explicit(Value: TArrayEx): TArray; overload;
class operator Explicit(Value: array of T): TArrayEx; overload;
class operator Add(A, B: TArrayEx): TArrayEx; overload;
class operator Add(A: TArrayEx; const B: T): TArrayEx; overload;
class operator Add(const A: T; B: TArrayEx): TArrayEx; overload;
class operator Add(A: TArrayEx; B: array of T): TArrayEx; overload;
class operator Add(A: array of T; B: TArrayEx): TArrayEx; overload;
class operator In (A: T; B: TArrayEx): Boolean; overload;
//
class operator Equal(A, B: TArrayEx): Boolean; overload;
class operator Equal(A: TArrayEx; B: TArray): Boolean; overload;
class operator Equal(A: TArray; B: TArrayEx): Boolean; overload;
class operator Equal(A: array of T; B: TArrayEx): Boolean; overload;
class operator Equal(A: TArrayEx; B: array of T): Boolean; overload;
public
procedure SetLen(Value: NativeInt); inline;
function GetLen: NativeInt; inline;
function ByteLen: NativeInt; inline;
class function Create(Value: array of T): TArrayEx; overload; static;
class function Create(Value: TArrayEx): TArrayEx; overload; static;
class function Create(const Value: T): TArrayEx; overload; static;
function Clone(): TArrayEx;
procedure SetValue(Value: array of T);
function ToArray(): TArray;
function SubArray(AFrom, ACount: NativeInt): TArrayEx;
procedure Delete(AFrom, ACount: NativeInt); overload;
procedure Delete(AIndex: NativeInt); overload;
procedure Append(Values: TArrayEx); overload;
procedure Append(const Value: T); overload;
procedure Append(Values: array of T); overload;
procedure Append(Values: TArray); overload;
function Insert(AIndex: NativeInt; const Value: T): NativeInt; overload;
function Insert(AIndex: NativeInt; const Values: array of T)
: NativeInt; overload;
function Insert(AIndex: NativeInt; const Values: TArray)
: NativeInt; overload;
function Insert(AIndex: NativeInt; const Values: TArrayEx)
: NativeInt; overload;
procedure Unique();
//排序
procedure Sort(); overload;
procedure Sort(const Comparer: IComparer); overload;
procedure Sort(const Comparer: IComparer; Index, Count: Integer); overload;
//搜索
function BinarySearch(const Item: T;
out FoundIndex: Integer; const Comparer: IComparer;
Index, Count: Integer): Boolean; overload;
function BinarySearch(const Item: T;
out FoundIndex: Integer; const Comparer: IComparer): Boolean; overload;
function BinarySearch(const Item: T;
out FoundIndex: Integer): Boolean; overload;
property Size: NativeInt read GetLen write SetLen;
property Len: NativeInt read GetLen write SetLen;
property RawData: TArray read GetRawData;
property Elements[Index: Integer]: T read GetElements
write SetElements; default;
end;
implementation
uses System.RTLConsts;
class operator TArrayEx.Add(A, B: TArrayEx): TArrayEx;
begin
Result := A.Clone;
Result.Append(B);
end;
class operator TArrayEx.Add(A: TArrayEx; const B: T): TArrayEx;
begin
Result := A.Clone;
Result.Append(B);
end;
class operator TArrayEx.Add(const A: T; B: TArrayEx): TArrayEx;
begin
Result.SetValue([A]);
Result.Append(B);
end;
class operator TArrayEx.Add(A: TArrayEx; B: array of T): TArrayEx;
begin
Result := A.Clone;
Result.Append(B);
end;
class operator TArrayEx.Add(A: array of T; B: TArrayEx): TArrayEx;
begin
Result.FData := DynArrayToTArray(A);
Result.Append(B);
end;
class operator TArrayEx.In(A: T; B: TArrayEx): Boolean;
var
Tmp: T;
begin
Result := False;
for Tmp in B.FData do
if CompareT(A, Tmp) then
begin
Result := True;
Break;
end;
end;
class operator TArrayEx.Equal(A, B: TArrayEx): Boolean;
begin
Result := EqualArray(A.FData, B.FData);
end;
class operator TArrayEx.Equal(A: TArrayEx; B: TArray): Boolean;
begin
Result := EqualArray(A.FData, B);
end;
class operator TArrayEx.Equal(A: TArray; B: TArrayEx): Boolean;
begin
Result := EqualArray(A, B.FData);
end;
class operator TArrayEx.Equal(A: array of T; B: TArrayEx): Boolean;
begin
Result := EqualArray(DynArrayToTArray(A), B.FData);
end;
class operator TArrayEx.Equal(A: TArrayEx; B: array of T): Boolean;
begin
Result := EqualArray(A.FData, DynArrayToTArray(B));
end;
function TArrayEx.BinarySearch(const Item: T; out FoundIndex: Integer;
const Comparer: IComparer; Index, Count: Integer): Boolean;
var
L, H: Integer;
mid, cmp: Integer;
begin
if (Index < Low(FData)) or ((Index > High(FData)) and (Count > 0))
or (Index + Count - 1 > High(FData)) or (Count < 0)
or (Index + Count < 0) then
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
if Count = 0 then
begin
FoundIndex := Index;
Exit(False);
end;
Result := False;
L := Index;
H := Index + Count - 1;
while L <= H do
begin
mid := L + (H - L) shr 1;
cmp := Comparer.Compare(FData[mid], Item);
if cmp < 0 then
L := mid + 1
else
begin
H := mid - 1;
if cmp = 0 then
Result := True;
end;
end;
FoundIndex := L;
end;
function TArrayEx.BinarySearch(const Item: T; out FoundIndex: Integer;
const Comparer: IComparer): Boolean;
begin
Result := BinarySearch(Item, FoundIndex, Comparer,
Low(FData), Length(FData));
end;
function TArrayEx.BinarySearch(const Item: T;
out FoundIndex: Integer): Boolean;
begin
Result := BinarySearch(Item, FoundIndex, TComparer.Default,
Low(FData), Length(FData));
end;
function TArrayEx.ByteLen: NativeInt;
begin
Result := Length(FData) * Sizeof(T);
end;
class function TArrayEx.Min(A, B: NativeInt): NativeInt;
begin
Result := A;
if Result > B then
Result := B;
end;
class procedure TArrayEx.CopyArray(var FromArray, ToArray: TArray;
FromIndex, ToIndex, Count: NativeInt);
var
i: Integer;
begin
if Count = 0 then
Exit;
if Count < 0 then
Count := Min(Length(FromArray), Length(ToArray));
if Length(FromArray) < (FromIndex + Count) then
Count := Length(FromArray) - FromIndex;
if Length(ToArray) < (ToIndex + Count) then
Count := Length(ToArray) - ToIndex;
if Count > 0 then
for i := 0 to Count - 1 do
ToArray[ToIndex + i] := FromArray[FromIndex + i];
end;
class procedure TArrayEx.MoveArray(var AArray: array of T;
FromIndex, ToIndex, Count: Integer);
var
i: Integer;
begin
if Count > 0 then
begin
if FromIndex < ToIndex then
for i := Count - 1 downto 0 do
AArray[ToIndex + i] := AArray[FromIndex + i]
else if FromIndex > ToIndex then
for i := 0 to Count - 1 do
AArray[ToIndex + i] := AArray[FromIndex + i];
end;
end;
procedure TArrayEx.QuickSort(const Comparer: IComparer; L, R: Integer);
var
I, J: Integer;
pivot, temp: T;
begin
if (Length(FData) = 0) or ((R - L) <= 0) then
Exit;
repeat
I := L;
J := R;
pivot := FData[L + (R - L) shr 1];
repeat
while Comparer.Compare(FData[I], pivot) < 0 do
Inc(I);
while Comparer.Compare(FData[J], pivot) > 0 do
Dec(J);
if I <= J then
begin
if I <> J then
begin
temp := FData[I];
FData[I] := FData[J];
FData[J] := temp;
end;
Inc(I);
Dec(J);
end;
until I > J;
if L < J then
QuickSort(Comparer, L, J);
L := I;
until I >= R;
end;
class function TArrayEx.DynArrayToTArray(const Value: array of T): TArray;
var
i: Integer;
begin
SetLength(Result, Length(Value));
for i := Low(Value) to High(Value) do
Result[i] := Value[i];
end;
class function TArrayEx.EqualArray(A, B: TArray): Boolean;
var
i: Integer;
begin
Result := True;
if A = B then
Exit;
if Length(A) <> Length(B) then
begin
Result := False;
end
else
begin
for i := Low(A) to High(A) do
if not CompareT(A[i], B[i]) then
begin
Result := False;
Break;
end;
end;
end;
class function TArrayEx.CompareT(const A, B: T): Boolean;
var
Compare : IComparer;
begin
Compare := TComparer.Default;
Result := Compare.Compare(A, B) = 0;
end;
//class function TArrayEx.CompareT(const A, B: T): Boolean;
//var
// p1, p2: PByte;
// i: Integer;
//begin
// Result := True;
// p1 := PByte(@A);
// p2 := PByte(@B);
// for i := 0 to Sizeof(T) - 1 do
// begin
// //
// if p1^ <> p2^ then
// begin
// Result := False;
// Exit;
// end;
// Inc(p1);
// Inc(p2);
// end;
//end;
function TArrayEx.GetElements(Index: Integer): T;
begin
Result := FData[Index];
end;
function TArrayEx.GetEnumerator: TEnumerator;
begin
Result := TEnumerator.Create(FData);
end;
function TArrayEx.GetLen: NativeInt;
begin
Result := Length(FData);
end;
function TArrayEx.GetRawData: TArray;
begin
Result := FData;
end;
class operator TArrayEx.Implicit(Value: TArrayEx): TArray;
begin
SetLength(Result, Length(Value.FData));
CopyArray(Value.FData, Result, 0, 0, Length(Value.FData));
end;
class operator TArrayEx.Explicit(Value: array of T): TArrayEx;
begin
Result.SetValue(Value);
end;
class operator TArrayEx.Implicit(Value: array of T): TArrayEx;
begin
Result.SetValue(Value);
end;
class operator TArrayEx.Implicit(Value: TArray): TArrayEx;
begin
SetLength(Result.FData, Length(Value));
CopyArray(Value, Result.FData, 0, 0, Length(Value));
end;
class operator TArrayEx.Explicit(Value: TArrayEx): TArray;
begin
SetLength(Result, Length(Value.FData));
CopyArray(Value.FData, Result, 0, 0, Length(Value.FData));
end;
procedure TArrayEx.SetElements(Index: Integer; const Value: T);
begin
FData[Index] := Value;
end;
procedure TArrayEx.SetLen(Value: NativeInt);
begin
SetLength(FData, Value);
end;
procedure TArrayEx.SetValue(Value: array of T);
begin
FData := DynArrayToTArray(Value);
end;
procedure TArrayEx.Sort;
begin
QuickSort(TComparer.Default, Low(FData), High(FData));
end;
procedure TArrayEx.Sort(const Comparer: IComparer; Index, Count: Integer);
begin
if (Index < Low(FData)) or ((Index > High(FData)) and (Count > 0))
or (Index + Count - 1 > High(FData)) or (Count < 0)
or (Index + Count < 0) then
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
if Count <= 1 then
Exit;
QuickSort(Comparer, Index, Index + Count - 1);
end;
procedure TArrayEx.Sort(const Comparer: IComparer);
begin
QuickSort(Comparer, Low(FData), High(FData));
end;
function TArrayEx.ToArray(): TArray;
begin
SetLength(Result, Length(FData));
CopyArray(FData, Result, 0, 0, Length(FData));
end;
class function TArrayEx.Create(Value: array of T): TArrayEx;
begin
Result.SetValue(Value);
end;
class function TArrayEx.Create(Value: TArrayEx): TArrayEx;
begin
Result := Value.Clone;
end;
class function TArrayEx.Create(const Value: T): TArrayEx;
begin
Result.SetValue([Value]);
end;
function TArrayEx.Clone(): TArrayEx;
begin
Result := SubArray(0, Length(FData));
end;
function TArrayEx.SubArray(AFrom, ACount: NativeInt): TArrayEx;
begin
SetLength(Result.FData, ACount);
CopyArray(FData, Result.FData, AFrom, 0, ACount);
end;
procedure TArrayEx.Delete(AFrom, ACount: NativeInt);
begin
if AFrom >= Length(FData) then
Exit;
if (AFrom + ACount) > Length(FData) then
ACount := Length(FData) - AFrom;
MoveArray(FData, AFrom + ACount, AFrom, Length(FData) - (AFrom + ACount));
SetLength(FData, Length(FData) - ACount);
end;
procedure TArrayEx.Delete(AIndex: NativeInt);
begin
Delete(AIndex, 1);
end;
procedure TArrayEx.Append(Values: TArrayEx);
begin
Insert(Length(FData), Values);
end;
procedure TArrayEx.Append(Values: TArray);
begin
Insert(Length(FData), Values);
end;
procedure TArrayEx.Append(const Value: T);
begin
SetLength(FData, Length(FData) + 1);
FData[High(FData)] := Value;
end;
procedure TArrayEx.Append(Values: array of T);
begin
Insert(Length(FData), Values);
end;
function TArrayEx.Insert(AIndex: NativeInt; const Value: T): NativeInt;
var
i: Integer;
begin
Result := -1;
if (AIndex > Length(FData)) or (AIndex < 0) then
Exit;
SetLength(FData, Length(FData) + 1);
MoveArray(FData, AIndex, AIndex + 1, Length(FData) - AIndex);
FData[AIndex] := Value;
Result := AIndex;
end;
function TArrayEx.Insert(AIndex: NativeInt; const Values: array of T)
: NativeInt;
var
i: Integer;
begin
SetLength(FData, Length(Values));
MoveArray(FData, AIndex, AIndex + Length(Values), Length(FData) - AIndex);
for i := 0 to Length(Values) - 1 do
FData[AIndex + i] := Values[i];
Result := AIndex;
end;
function TArrayEx.Insert(AIndex: NativeInt; const Values: TArray)
: NativeInt;
var
i: Integer;
begin
SetLength(FData, Length(FData) + Length(Values));
MoveArray(FData, AIndex, AIndex + Length(Values), Length(FData) - AIndex);
for i := 0 to Length(Values) - 1 do
FData[AIndex + i] := Values[i];
Result := AIndex;
end;
function TArrayEx.Insert(AIndex: NativeInt; const Values: TArrayEx)
: NativeInt;
begin
Result := Insert(AIndex, Values.ToArray);
end;
procedure TArrayEx.Unique();
var
i, J: Integer;
Tmp: TArrayEx;
Flag: Boolean;
begin
for i := High(FData) downto Low(FData) do
begin
Flag := False;
for J := High(Tmp.FData) downto Low(Tmp.FData) do
begin
if CompareT(FData[i], Tmp[J]) then
begin
Flag := True;
Break;
end;
end;
if not Flag then
Tmp.Append(FData[i]);
end;
FData := Tmp.FData;
end;
{ TArrayEx.TEnumerator }
constructor TArrayEx.TEnumerator.Create(const AValue: TArray);
begin
FValue := AValue;
FIndex := -1;
end;
function TArrayEx.TEnumerator.GetCurrent: T;
begin
Result := FValue[FIndex];
end;
function TArrayEx.TEnumerator.MoveNext: Boolean;
begin
Result := False;
if (FIndex >= Length(FValue)) then
Exit;
Inc(FIndex);
Result := FIndex < Length(FValue);
end;
end.