6

私はいくつかのFortran90コードを翻訳しなければならず、興味深い言語機能を見つけました。

例として、次のタイプと動的配列変数を定義します。

TYPE WallInfo
  CHARACTER(len=40) :: Name
  REAL              :: Azimuth
  REAL              :: Tilt
  REAL              :: Area
  REAL              :: Height
END TYPE WallInfo

TYPE(WallInfo), ALLOCATABLE, DIMENSION(:) :: Wall

コードの後半で、関数を呼び出します。

CALL HeatFlow(Wall%Area, Wall%Azimuth)

Wallはレコードの配列であるため、Delphiプログラマーとして、これは私を少し投げました!

ルーチンでの使用法から、Fortranがレコード配列のフィールドを独自の配列として射影できることは明らかです。

SUBROUTINE HeatFlow( Area, Azimuth )
  REAL, INTENT(IN), DIMENSION(:) :: Area
  REAL, INTENT(IN), DIMENSION(:) :: Azimuth

Delphiでこれを行う方法があるかどうか誰かが知っていますか(私はバージョン2010を使用しています)?

レコード値を配列として抽出する関数を作成することもできますが、すべてのフィールドに専用のルーチンを作成する必要があるため、これは少し面倒です(かなりの数があります)。

Delphi 2010には、見逃していた言語機能があることを期待しています。

4

3 に答える 3

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;

.

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;
于 2012-08-09T00:04:35.860 に答える
2

あなたの質問に答えるために、いいえ、レコードの配列から単一の列をそれ自体の単純な配列に分割するための言語構造や便利な方法はありません。

私は次のようなものをお勧めします:

function SplitColumn( RecordArray : Array of {recordtype} ) : Array of {columntype};
var
  column : array of {type};
  x : Integer;
begin
  setlength( result, high( RecordArray ) + 1 );
  for x := 0 to high( RecordArray ) do
    result[ x ] := RecordArray[ x ].{columnname};
end;

これは、動的配列を使用する場合です。個人的には、これを移植する場合は、次のようにListとListを使用します。

type
   TWallList = class( TList<TWallInfo> );
   TDoubleList = class( TList<Double> );

function SplitColumn( WallList : TWallList; AreaList, AzimuthList : TDoubleList ); 
var
  x : Integer;
begin
  for x := 0 to RecList.Count-1 do
  begin
    AreaList.add( RecordArray[ x ].Area );
    Azimuth.add( RecordArray[ x ].Azimuth );
  end;
end;
于 2012-08-08T21:36:32.000 に答える