bru*_*uce 6 arrays delphi record projection delphi-2010
我必须翻译一些Fortran 90代码并找到一个有趣的语言功能.
例如,它们定义以下类型和动态数组变量:
TYPE WallInfo
CHARACTER(len=40) :: Name
REAL :: Azimuth
REAL :: Tilt
REAL :: Area
REAL :: Height
END TYPE WallInfo
TYPE(WallInfo), ALLOCATABLE, DIMENSION(:) :: Wall
Run Code Online (Sandbox Code Playgroud)
稍后在代码中,他们调用一个函数:
CALL HeatFlow(Wall%Area, Wall%Azimuth)
Run Code Online (Sandbox Code Playgroud)
作为一名Delphi程序员,这让我感到有些不同,因为Wall是一系列记录!
从例程中的用法来看,很明显Fortran可以将记录数组中的字段作为自己的数组进行投影.
SUBROUTINE HeatFlow( Area, Azimuth )
REAL, INTENT(IN), DIMENSION(:) :: Area
REAL, INTENT(IN), DIMENSION(:) :: Azimuth
Run Code Online (Sandbox Code Playgroud)
有没有人知道是否有办法用Delphi(我使用的是2010版)?
我可以编写一个函数来将记录值提取为数组,但这有点单调乏味,因为我必须为每个字段编写一个专用例程(并且有很多).
我希望Delphi 2010中有一些我错过的语言功能.
使用扩展RTTI,可以创建一个通用函数,该函数将数组和字段名称作为输入,并使用数组的RTTI仅提取该字段的值,并使用正确的数据类型创建具有它们的新数组.
以下代码适用于XE2:
uses
System.SysUtils, System.Rtti;
type
FieldArray<TArrElemType, TFieldType> = class
public
class function Extract(const Arr: TArray<TArrElemType>; const FieldName: String): TArray<TFieldType>;
end;
class function FieldArray<TArrElemType, TFieldType>.Extract(const Arr: TArray<TArrElemType>; const FieldName: String): TArray<TFieldType>;
var
Ctx: TRttiContext;
LArrElemType: TRttiType;
LField: TRttiField;
LFieldType: TRttiType;
I: Integer;
begin
Ctx := TRttiContext.Create;
try
LArrElemType := Ctx.GetType(TypeInfo(TArrElemType));
LField := LArrElemType.GetField(FieldName);
LFieldType := Ctx.GetType(TypeInfo(TFieldType));
if LField.FieldType <> LFieldType then
raise Exception.Create('Type mismatch');
SetLength(Result, Length(Arr));
for I := 0 to Length(Arr)-1 do
begin
Result[I] := LField.GetValue(@Arr[I]).AsType<TFieldType>;
end;
finally
Ctx.Free;
end;
end;
Run Code Online (Sandbox Code Playgroud)
.
type
WallInfo = record
Name: array[0..39] of Char;
Azimuth: Real;
Tilt: Real;
Area: Real;
Height: Real;
end;
procedure HeatFlow(const Area: TArray<Real>; const Azimuth: TArray<Real>);
begin
// Area contains (4, 9) an Azimuth contains (2, 7) as expected ...
end;
var
Wall: TArray<WallInfo>;
begin
SetLength(Wall, 2);
Wall[0].Name := '1';
Wall[0].Azimuth := 2;
Wall[0].Tilt := 3;
Wall[0].Area := 4;
Wall[0].Height := 5;
Wall[1].Name := '6';
Wall[1].Azimuth := 7;
Wall[1].Tilt := 8;
Wall[1].Area := 9;
Wall[1].Height := 10;
HeatFlow(
FieldArray<WallInfo, Real>.Extract(Wall, 'Area'),
FieldArray<WallInfo, Real>.Extract(Wall, 'Azimuth')
);
end;
Run Code Online (Sandbox Code Playgroud)
我发布这个作为答案,因为评论有点过于局限于此.
这个答案试图解释在FORTRAN和Delphi数组和记录的内存布局的区别,并修改了答案由托德·格雷斯比和答案由雷米勒博(我upvoted两者).
FORTRAN和其他一些以计算为中心的语言以列主要顺序存储嵌套数组.Delphi和许多其他语言使用行主要顺序.
从内存的角度来看,记录只不过是一个字段数组:
对于计算密集型操作,当您的算法支持列时,存储嵌套数组列主要顺序是有意义的.行主要订单相同.因此,在循环中,您需要将索引的顺序与存储顺序相匹配.
在FORTRAN中给出此记录和数组定义:
TYPE WallInfo
CHARACTER(len=40) :: Name
REAL :: Azimuth
REAL :: Tilt
REAL :: Area
REAL :: Height
END TYPE WallInfo
TYPE(WallInfo), ALLOCATABLE, DIMENSION(:) :: Wall
Run Code Online (Sandbox Code Playgroud)
和Delphi中的功能等价定义:
type
WallInfo = record
Name: array[0..39] of Char;
Azimuth: Real;
Tilt: Real;
Area: Real;
Height: Real;
end;
var
Wall: array of WallInfo;
Run Code Online (Sandbox Code Playgroud)
和一个由3个WallInfo元素组成的数组,这就是内存布局的外观(它们都是连续的内存区域,我将它们分成几行以保持可读性):
在FORTRAN:
Name[0,0]...Name[0,39], Name[1,0]...Name[1,39], Name[2,0]...Name[2,39],
Azimuth[0], Azimuth[1], Azimuth[2],
Tilt[0], Tilt[1], Tilt[2],
Area[0], Area[1], Area[2],
Height[0], Height[1], Height[2],
Run Code Online (Sandbox Code Playgroud)
在德尔福:
Name[0,0]...Name[0,39], Azimuth[0], Tilt[0], Area[0], Height[0],
Name[1,0]...Name[1,39], Azimuth[1], Tilt[1], Area[1], Height[1],
Name[2,0]...Name[2,39], Azimuth[2], Tilt[2], Area[2], Height[2],
Run Code Online (Sandbox Code Playgroud)
所以这个FORTRAN电话:
CALL HeatFlow(墙壁%面积,墙壁%方位角)
只会将指针传递给Area [0]和Azimuth [0]内存位置以及这些内存区域到函数的长度.
在Delphi中,这是不可能的,所以你必须这样做
Todd Grigsby和Remy Lebeau使用直接Delphi代码或Delphi记录RTTI显示了他们答案的前三个步骤.
第4步以类似的方式工作.
这两种解决方案都使用了Delphi 2009中引入的泛型.
在Delphi 2010之前,记录上的RTTI非常小,所以你得到了两个答案的正确的Delphi版本.
注意(再次):在将算法从FORTRAN转换为Delphi时,请确保在列中/行主要更改时观察数组中的循环和其他索引.
归档时间: |
|
查看次数: |
1708 次 |
最近记录: |