Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Arrays 使用起始和长度说明符通过引用传递静态/动态数组的切片_Arrays_Delphi_Slice - Fatal编程技术网

Arrays 使用起始和长度说明符通过引用传递静态/动态数组的切片

Arrays 使用起始和长度说明符通过引用传递静态/动态数组的切片,arrays,delphi,slice,Arrays,Delphi,Slice,将数组(动态或静态)传递给方法/过程/函数, 声明可以如下所示: procedure WorkWithArray( const anArray : array of Integer); (* or procedure WorkWithArray( var anArray : array of Integer); *) var i : Integer; begin for i := Low(anArray) to High(anArray) do begin // Do som

将数组(动态或静态)传递给方法/过程/函数, 声明可以如下所示:

procedure WorkWithArray( const anArray : array of Integer);
(* or procedure WorkWithArray( var anArray : array of Integer); *)
var
  i : Integer;
begin
  for i := Low(anArray) to High(anArray) do
  begin
    // Do something with the "open array" anArray
    WriteLn(anArray[i]);
  end;
end;

...
var
  staticArray : array[0..2] of Integer;
  dynArray : array of integer;
  dynArrayG : TArray<Integer>;
begin
  SetLength(dynArray,10);
  SetLength(dynArrayG,10);

  WorkWithArray(staticArray);  // Using a static array
  WorkWithArray(dynArray);     // Using a dynamic array
  WorkWithArray(dynArrayG);    // Using a dynamic generic array
  ...
end;
注意:动态数组不能直接放入
Slice()
函数, 看见 因此,必须使用类型铸造的解决方法


如果我们想处理不从第一个元素开始的子范围,该怎么办

同样可行:

WorkWithArray(Slice(PIntLongArray(@staticArray[1])^,2));
WorkWithArray(Slice(PIntLongArray(@dynArray[1])^,2));
WorkWithArray(Slice(PIntLongArray(@dynArrayG[1])^,2));
注意:偏移量和切片的总和不得超过数组的元素计数

我知道在输入声明为常量的情况下,可以使用Copy(myArray,x1,x2), 但这将生成数组的副本,对于大型数组来说是无效的。(还有堆栈溢出的风险)


最后,我的问题是:

虽然这演示了一种使用起始索引和长度说明符通过引用传递数组子范围的方法, 看起来有点尴尬。
有更好的替代方案吗?如果有,怎么做?

更新了有关泛型解决方案,请参阅下文

下面是一个替代方案,它封装了函数内偏移量所需的类型转换,该函数驻留在声明为类函数的高级记录中。 除了隐藏类型转换之外,偏移量还根据数组的高索引进行范围检查

如果需要,可以添加更多类型

Type
  SubRange = record
    Type
      TIntLongArray = array[0..MaxInt div SizeOf(Integer) - 1] of integer;
      PIntLongArray = ^TIntLongArray;
      TByteLongArray = array[0..MaxInt div SizeOf(Byte) - 1] of Byte;
      PByteLongArray = ^TByteLongArray;

    class function Offset( const anArray : array of Integer; 
                                 offset  : Integer) : PIntLongArray; overload; static;
    class function Offset( const anArray : array of Byte; 
                                 offset  : Integer) : PByteLongArray; overload; static;
    // ToDo: Add more types ...
  end;

class function SubRange.Offset(const anArray : array of Integer; 
                                     offset  : Integer): PIntLongArray;
begin
  Assert(offset <= High(anArray));
  Result := PIntLongArray(@anArray[offset]);
end;

class function SubRange.Offset(const anArray : array of Byte; 
                                     offset  : Integer): PByteLongArray;
begin
  Assert(offset <= High(anArray));
  Result := PByteLongArray(@anArray[offset]);
end;
虽然这看起来更好,但我仍然不相信这是最佳解决方案


更新

在编写上述解决方案时,我将泛型解决方案作为最终目标

下面是一个利用匿名方法和泛型实现
切片(anArray、startIndex、Count)
方法,该方法可用于静态数组和动态数组

一个直接的泛型解决方案将依赖于在使用它的每个位置关闭范围检查, 这不是一个很好的解决方案。 原因是
SizeOf(T)
无法用于声明最大大小的静态数组类型:

TGenericArray=T的数组[0..MaxInt div SizeOf(T)-1];//SizeOf(T)未解析

因此,我们必须使用:

TGenericArray=T的数组[0..0]

相反。当它打开时,这会触发范围检查,对于索引>0

解决方案

但问题可以通过另一种策略解决,
回调
,或者更现代的术语是
控制反转
(IoC)或
依赖注入
(DI)。 最好用“不要打电话给我,我们打电话给你”来解释这个概念

我们不使用直接函数,而是将操作代码作为匿名方法与所有参数一起传递。 现在,范围检查问题包含在
切片
帧中

Slice<Integer>.Execute(
  procedure(const arr: array of Integer)
  begin
    WriteLn(Math.SumInt(arr));
  end, dArr, 2, 7);
Slice.Execute(
过程(常量arr:整数数组)
开始
书面(数学计算(arr));
完,dArr,2,7);

unitugenericslice;
接口
类型
切片=记录
私有的
类型
PGenericArr=^TGenericArr;
TGenericArr=T的数组[0..0];
公众的
类型
TConstArrProc=对过程的引用(const anArr:T的数组);
类过程执行(aProc:tconstrarproc;
常数排列:T的数组;
startIndex,计数:整数);静止的
结束;
实施
类过程Slice.Execute(aProc:TConstArrProc;
常量数组:T的数组;startIndex,计数:整数);
开始

if(startIndex如何避免开放数组和切片并使用类似的方法

type
   TArrayRef<T> = record
   strict private
     type PointerOfT = ^T;
     FItems: PointerOfT;
     FCount: Integer;
   public  
     // FItems := @AItems[Offset]; FCount := Count;
     constructor Create(AItems: array of T; Offset, Count: Integer);
     property Items[Index: Integer]: T read GetItem; // Exit(FItems[Index])
     property Count: Integer read FCount; 
   end;
   TArrayRef = record // helpers
     class function Create<T>(AItems: array of T; Offset, Count: Integer); static;
     class function Create<T>(AItems: array of T; Count: Integer); static;
     class function Create<T>(AItems: array of T); static;
   end; 

procedure WorkWithArray(const anArray : TArrayRef<Integer>);
var
  I: Integer;
begin
  for I := 0 to anArray.Count - 1 do WriteLn(anArray[I]);
end;

WorkWithArray(TArrayRef.Create(StaticArray, 3)); // first three items
WorkWithArray(TArrayRef.Create(DynArray, 10, 3)); 
类型
TArrayRef=记录
严格保密
类型PointerOfT=^T;
FItems:PointerOfT;
FCount:整数;
公开的
//FItems:=@AItems[Offset];FCount:=计数;
构造函数创建(AItems:T的数组;偏移量,计数:整数);
属性项[Index:Integer]:T读取GetItem;//退出(FItems[Index])
属性计数:整数读取FCount;
结束;
TArrayRef=record//helpers
类函数Create(AItems:T的数组;偏移量,计数:整数);静态;
类函数Create(AItems:T的数组;Count:Integer);静态;
类函数Create(AItems:T的数组);静态;
结束;
程序WorkWithArray(常量数组:TArrayRef);
变量
I:整数;
开始
对于I:=0到anArray.Count-1 do WriteLn(anArray[I]);
结束;
WorkWithArray(TArrayRef.Create(StaticArray,3));//前三项
WorkWithArray(TArrayRef.Create(DynArray,10,3));

以防其他人像我一样被绊倒。在旧版本的Delphi(D2007和旧版本。不确定XE版本)中,如果使用重载,也会出现E2193错误:

  procedure Polygon(Points: array of TPoint); overload;
  procedure Polygon(Points: array of TDPoint); overload;
如果移除过载:

  procedure Polygon(Points: array of TPoint); 
  procedure PolygonD(Points: array of TDPoint);

这在Delphi 10.3.0(可能还有其他较旧版本)中得到了修复。

为什么不简单地重新实现
Slice
来接受动态数组?@user3764855,
Slice
是一个神奇的函数(内在的)由编译器插入,不是一个正常的函数来重载或更改本身。你不能只返回指向数组的指针,而不是使用这些匿名方法。是的,问题中已经说明了这一点,但我想要一种没有类型转换的更好方法。你能用类型转换发布解决方案吗?谢谢。
program ProjectGenericSlice;

{$APPTYPE CONSOLE}

uses
  Math,
  uGenericSlice in 'uGenericSlice.pas';

function Sum(const anArr: array of Integer): Integer;
var
  i: Integer;
begin
  Result := 0;
  for i in anArr do
    Result := Result + i;
end;

procedure SumTest(const arr: array of integer);
begin
  WriteLn(Sum(arr));
end;

procedure TestAll;
var
  aProc: Slice<Integer>.TConstArrProc;
  dArr: TArray<Integer>;
  mySum: Integer;
const
  sArr: array [1 .. 10] of Integer = (
    1,2,3,4,5,6,7,8,9,10);

begin
  dArr := TArray<Integer>.Create(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

  aProc :=
    procedure(const arr: array of Integer)
    begin
      WriteLn(Sum(arr));
    end;

  // Test predefined anonymous method
  Slice<Integer>.Execute( aProc, dArr, 2, 7);

  // Test inlined anonymous method
  Slice<Integer>.Execute(
    procedure(const arr: array of Integer)
    begin
      WriteLn(Sum(arr));
    end, dArr, 2, 7);

  // Test call to Math.SumInt
  Slice<Integer>.Execute(
    procedure(const arr: array of Integer)
    begin
      WriteLn(Math.SumInt(arr));
    end, dArr, 2, 7);

  // Test static array with Low(sArr) > 0
  Slice<Integer>.Execute(
    procedure(const arr: array of Integer)
    begin
      WriteLn(Sum(arr));
    end, sArr, 3 - Low(sArr), 7);

  // Using a real procedure
  Slice<Integer>.Execute(
    SumTest, // Cannot be nested inside TestAll
    dArr, 2, 7);

  // Test call where result is passed to local var
  Slice<Integer>.Execute(
    procedure(const arr: array of Integer)
    begin
      mySum := Math.SumInt(arr);
    end, dArr, 2, 7);
  WriteLn(mySum);

end;

begin
  TestAll;
  ReadLn;
end.
type
   TArrayRef<T> = record
   strict private
     type PointerOfT = ^T;
     FItems: PointerOfT;
     FCount: Integer;
   public  
     // FItems := @AItems[Offset]; FCount := Count;
     constructor Create(AItems: array of T; Offset, Count: Integer);
     property Items[Index: Integer]: T read GetItem; // Exit(FItems[Index])
     property Count: Integer read FCount; 
   end;
   TArrayRef = record // helpers
     class function Create<T>(AItems: array of T; Offset, Count: Integer); static;
     class function Create<T>(AItems: array of T; Count: Integer); static;
     class function Create<T>(AItems: array of T); static;
   end; 

procedure WorkWithArray(const anArray : TArrayRef<Integer>);
var
  I: Integer;
begin
  for I := 0 to anArray.Count - 1 do WriteLn(anArray[I]);
end;

WorkWithArray(TArrayRef.Create(StaticArray, 3)); // first three items
WorkWithArray(TArrayRef.Create(DynArray, 10, 3)); 
  procedure Polygon(Points: array of TPoint); overload;
  procedure Polygon(Points: array of TDPoint); overload;
  procedure Polygon(Points: array of TPoint); 
  procedure PolygonD(Points: array of TDPoint);