问题描述
传递数组(动态或静态)来的方法/过程/函数与<$c$c>open数组参数 ,
声明可以是这样的:
Passing arrays (dynamic or static) to methods/procedures/functions with open array parameters
, declaration can look like this:
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;
传递数组像这样在整个德尔福RTL用了一个很常见的成语,其中包括用于处理数据数组一些非常优化的函数/过程。
Passing arrays like this is a very common idiom used throughout the Delphi RTL, including some very optimized functions/procedures for handling arrays of data.
假设我们需要调用 WorkWithArray
与我们的阵列的子区间。然后我们可以使用的内在。
因此,与类型转换的变通办法已被使用。
Note: dynamic arrays does not fit directly into the Slice()
function, see "Slice does not work with dynamic arrays"
. So the workaround method with type casting has to be used.
如果我们想要一个子范围不是从第一个元素开始?工作
What if we want to work with a subrange not starting from the first element?
可行,以及:
WorkWithArray(Slice(PIntLongArray(@staticArray[1])^,2));
WorkWithArray(Slice(PIntLongArray(@dynArray[1])^,2));
WorkWithArray(Slice(PIntLongArray(@dynArrayG[1])^,2));
注意:的总和的偏移和片必须不超过数组的元素计数
Note : the sum of the offset and the slice must not exceed the element count of the array.
我知道,使用复制(myarray的,X1,X2)能在输入被声明为常量的情况下使用,
但是这将使该数组的副本,是大型阵列ineffiecient。 (带以及堆栈溢出的风险)。
最后,我的问题:
虽然此展示了一个方法通过用起始索引和长度说明符引用传递阵列的子范围,
它看起来有点别扭。
有没有更好的替代品,如果是这样如何?
While this demonstrates a way to pass a subrange of an array by reference with a start index and a length specifier, it looks a bit awkward. Are there better alternatives and if so how?
推荐答案
更新查看了一下下来了仿制药的解决方案。
Updated See a bit down for a generics solution.
下面是一个封装所需的功能,它驻留在一个先进的记录声明为类函数内的偏移量类型转换的替代品。
除了隐藏式投,偏移范围内对数组的高指标检查。
Here is an alternative that encapsulates the type cast needed for the offset inside a function, which resides in an advanced record declared as a class function.Besides hiding the type cast, the offset is range checked against the high index of the array.
更多类型可以根据需要添加。
More types can be added if needed.
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;
注意:的总和的偏移和片必须不超过数组的元素计数
Note : the sum of the offset and the slice must not exceed the element count of the array.
调用示例:
WorkWithArray( Slice(SubRange.Offset(staticArray,1)^,2));
WorkWithArray( Slice(SubRange.Offset(dynArray,1)^,2));
WorkWithArray( Slice(SubRange.Offset(dynArrayG,1)^,2));
虽然这看起来更好,我仍然不相信这是最佳的解决方案。
While this looks better, I'm still not convinced this is the optimal solution.
更新
在编写上述溶液中,我有一个泛型解决方案为最终目标。
When writing the above solution, I had a generics solution as the ultimate goal.
下面是利用匿名方法和泛型实施回答的切片(anArray,则startIndex,计数)
方法可以与静态和动态阵列被使用。
Here is an answer that utilizes anonymous methods and generics to implement a Slice(anArray,startIndex,Count)
method that can be used with both static and dynamic arrays.
一个直仿制药的解决方案将依靠范围检查每把它用在这里被关闭,
这将不会是一个pretty溶液。
其原因是,中SizeOf(T)
不能用于申报最大尺寸的静态数组类型:
A straight generics solution would rely on range checking be turned off at every placed where it was used,and that would not be a pretty solution.The reason is that SizeOf(T)
could not be used to declare a static array type of maximum size:
TGenericArray =阵列[0..MaxInt格中SizeOf(T) - 1]的T; //一下SizeOf(T)没有得到解决。
因此,我们将不得不使用:
So we would have to use:
TGenericArray =数组[0..0]的T;
来代替。而这将触发范围检查,当它打开时,对指数> 0
instead. And this triggers the range check when it is on, for index > 0.
解决方案
但问题可以通过另一种策略来解决,回调
或一个更现代的术语是控制反转
(IOC)或 Dependeny注射
(DI)。
这个概念是最好的,不要给我打电话,我们叫你解释说。
But the problem could be solved by another strategy, callbacks
or a more modern terminology would be Inversion of Control
(IoC) or Dependeny Injection
(DI).The concept is best explained with, "Don't call me, we call you".
而不是使用直接的功能,我们通过运营code作为一个匿名方法连同所有参数。
现在范围检查问题是包含在切片&LT内; T&GT;
架
Instead of using a direct function, we pass the operational code as an anonymous method together with all parameters.Now the range check problem is contained within the Slice<T>
frame.
Slice<Integer>.Execute(
procedure(const arr: array of Integer)
begin
WriteLn(Math.SumInt(arr));
end, dArr, 2, 7);
unit uGenericSlice;
interface
type
Slice<T> = record
private
type
PGenericArr = ^TGenericArr;
TGenericArr = array [0..0] of T;
public
type
TConstArrProc = reference to procedure(const anArr: array of T);
class procedure Execute( aProc: TConstArrProc;
const anArray: array of T;
startIndex,Count: Integer); static;
end;
implementation
class procedure Slice<T>.Execute(aProc: TConstArrProc;
const anArray: array of T; startIndex, Count: Integer);
begin
if (startIndex <= 0) then
aProc(Slice(anArray, Count))
else
begin
// The expression PGenericArr(@anArray[startIndex]) can trigger range check error
{$IFOPT R+}
{$DEFINE RestoreRangeCheck}
{$R-}
{$ENDIF}
Assert((startIndex <= High(anArray)) and (Count <= High(anArray)-startIndex+1),
'Range check error');
aProc(Slice(PGenericArr(@anArray[startIndex])^, Count));
{$IFDEF RestoreRangeCheck}
{$UNDEF RestoreRangeCheck}
{$R+}
{$ENDIF}
end;
end;
end.
下面是一些示例使用情况:
Here are some example use cases:
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.
这篇关于通过启动和长度说明引用传递静态/动态数组片的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!