给定一组记录,我如何获得一个表示每个记录的数组的数组?

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中有一些我错过的语言功能.

Rem*_*eau 8

使用扩展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)


Jer*_*ers 7

我发布这个作为答案,因为评论有点过于局限于此.

这个答案试图解释在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中,这是不可能的,所以你必须这样做

  1. 构建新的区域和方位角阵列
  2. 从名为Wall的WallInfo记录实例数组中的信息中复制它们
  3. 将它们发送给该功能
  4. 如果这些是var参数:将两个数组中的更改复制回Wall

Todd GrigsbyRemy Lebeau使用直接Delphi代码或Delphi记录RTTI显示了他们答案的前三个步骤.
第4步以类似的方式工作.

这两种解决方案都使用了Delphi 2009中引入的泛型.
在Delphi 2010之前,记录上的RTTI非常小,所以你得到了两个答案的正确的Delphi版本.

注意(再次):在将算法从FORTRAN转换为Delphi时,请确保在列中/行主要更改时观察数组中的循环和其他索引.