2012-08-08 17 views
6

私はいくつかのFortran 90コードを翻訳しなければならず、面白い言語機能を発見しました。レコードの配列が与えられた場合、それぞれのフィールドを表す配列を取得するにはどうすればよいですか?

例として、それらは次のタイプおよび動的配列変数を定義:

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

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

後でコードで、それらは関数を呼び出す:Delphiのプログラマーとして

CALL HeatFlow(Wall%Area, Wall%Azimuth) 

、これは投げウォールはレコードの配列なので、私は少し私!

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

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

デルファイ(これはバージョン2010を使用しています)でこれを行う方法があるかどうかは知りませんか?

レコードの値を配列として抽出する関数を書くことができましたが、これはちょっと面倒です。フィールドごとに専用のルーチンを書く必要があります(かなりの数があります)。

Delphi 2010の一部の言語機能が欠けていると思います。

+0

ArrayInfoのケース配列で配列やレコードを試しましたか? Delphiはダイナミックアレイをサポートしています。最初に新しい値を入力する前にSetLengthの配列のサイズを設定します。 –

+0

RemyのRTTIの答えが涼しいので、上記のレコードタイプを別々の線形配列に変換したいと思うでしょう: 'Name:String of Array;方位角:Doubleの配列。 ... 'そして、私はこの素敵なRTTIハックを使ってデータを集める必要はないでしょう。すでに集められているからです。 –

答えて

8

、入力として配列やフィールド名をとり、一般的な関数を作成することが可能であると配列の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; 
+0

あなたの助けてくれてありがとう、私はこの方法を試してみるつもりです。私は、直接翻訳がそれほど簡単ではないことに気付き始めています。 Fortranには素敵な行列の機能があるようですが、Delphiでこれらをシミュレートしようとしてもそれほど効率的ではありません。配列を渡す代わりに、私は単純に単一のパラメータで壁配列全体を渡しました。 – bruce

+0

Remmy、私はループをI:= 0〜Length(Arr)-1に変更してDelphi 2010で動作させる必要がありました。私は感心しています。問題は解決していて、きれいです。 – bruce

+0

コードはXE2で作成され、テストされました。 'High()'はD2010の動的配列では動作しないと言っていますか?私はその変更で私の答えを更新しました。 –

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; 

あなたが動的配列を使用する場合です:

は、私は次のようなものをお勧めします。あなたがこれを移植している場合は個人的に、私はのように、リストおよびリストを使用したい:

拡張RTTIを使用して
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; 
+0

拡張RTTIを使用すると、配列とフィールド名を入力として受け取り、配列のRTTIを使用してそのフィールドの値を抽出し、それらの新しい配列を作成する汎用関数を作成することができます。 –

+0

私は実際に拡張RTTIのデモで回答を掲載しました。 –

7

コメントはこのフレーズにはあまりにも限定されているため、これを回答として投稿しています。

この答えは、FORTRANやDelphiで配列やレコードのメモリレイアウトの違いを説明しようとしてTodd Grigsbyanswerを改訂し、Remy Lebeauによってanswer(私は両方をupvoted)。

FORTRANといくつかの他の計算中心言語には、ネストされた配列がcolumn major orderに格納されています。 Delphiなどの多くの言語ではrow major orderを使用しています。

メモリの観点から、レコードはフィールドの配列よりも何もありません:

  • が名前を持っていないインデックス
  • は、計算集約型のために、異なるタイプ

を持っているかもしれませんあなたのアルゴリズムが列を好むときには、ネストされた配列の列の主な順序を格納するのが理にかなっています。行の主要な順序と同じです。したがって、ループではmatch the order of your indexes with the order of your storageが必要です。

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

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

とデルファイの機能的等価物定義:FORTRAN、このレコードと配列の定義が与えられる

type 
    WallInfo = record 
    Name: array[0..39] of Char; 
    Azimuth: Real; 
    Tilt: Real; 
    Area: Real; 
    Height: Real; 
    end; 

var 
    Wall: array of WallInfo; 

3個のWallInfo素子のアレイ、 これは、どのようにメモリレイアウトであります(すべて連続したメモリ領域になるはずですが、読みやすくするために行を分けています):

in FORTRAN:

Delphiで
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], 

に:

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], 

ので、このFORTRANコール:

CALLの熱流(ウォール%エリア、ウォール%方位)

だけのエリアへのポインタを渡します[ 0]とAzimuth [0]のメモリ位置と関数に対するメモリ領域の長さです。 Delphiで

、それができない、あなたは

  1. に持っているように、新しいエリアを構築し、方位arays
  2. ウォール
  3. 呼ばWallInfoレコードインスタンスの配列の情報からそれらをコピーしてそれらを送信機能
  4. 場合にこれらは、VARパラメータです:

ウォールに戻って二つの配列からの変更をコピーTodd GrigsbyRemy Lebeauは、ストレートのDelphiコードまたはDelphiのレコードRTTIを使用して最初の3つのステップを示しました。
手順4も同様です。

両方のソリューションは、Delphi 2009で導入されたジェネリックを使用しています。
Until Delphi 2010, RTTI on records was very minimal)、両方の回答で適切なデルファイバージョンを取得しました。

メモ:(もう一度):アルゴリズムをFORTRANからDelphiに変換するときは、列/行の大きな変更のために配列内のループやその他のインデックス付けを監視してください。

+0

Jeroen、ループ索引付けについてのご意見は高く評価されています。私はすでにこの問題につまづいていました。私はDelphiとFortranの配列順序の違いを認識していませんでした。今はもっと意味をなさない。 – bruce

+0

私はそれがより多くの洞察を得るのを助けてくれてうれしいです。良い質問を続けてください! –

+0

行のメジャーとメジャーな問題に関連する質問には何も表示されません。彼らはどこで問題になるのでしょうか?プロジェクションをサポートするFortranの施設に関する質問が私には現れます。これは、おそらく、数多くの配列のストライド技法で実装されます。 –

関連する問題