2012-08-08 7 views
6

Muszę przetłumaczyć kod Fortran 90 i znalazłem interesującą funkcję językową.Biorąc pod uwagę tablicę rekordów, w jaki sposób mogę uzyskać tablicę reprezentującą pole z każdego z nich?

Przykładowo określają następujące rodzaje i dynamicznie tablicy zmiennej:

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

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

dalszej części kodu nazywają funkcję:

CALL HeatFlow(Wall%Area, Wall%Azimuth) 

jako programista Delphi to wyrzucił mnie trochę, bo Wall to zbiór rekordów!

Z użycia w procedurze jasne jest, że Fortran może wyświetlać pola z tablicy rekordów jako własną tablicę.

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

Czy ktoś wie, czy można to zrobić z Delphi (używam wersji 2010)?

Mógłbym napisać funkcję, aby wyodrębnić rekordową wartość jako tablicę, ale jest to trochę uciążliwe, ponieważ będę musiał napisać dedykowaną procedurę dla każdego pola (i jest ich sporo).

Mam nadzieję, że w Delphi 2010 jest jakiś język, którego brakowało.

+0

Czy próbowałeś z tablicą lub rekordem w swojej tablicy przypadków WallInfo? Delphi obsługuje tablice dinamiczne. Przed wprowadzeniem nowej wartości najpierw ustaw rozmiar tablicy z wartością SetLength. –

+0

Tak fajna, jak odpowiedź Remy'ego na odpowiedź RTTI, byłbym skłonny przetłumaczyć powyższy typ rekordu na osobne tablice liniowe: "Nazwa: Tablica ciągu; Azymut: Tablica podwójna; ... ", a potem nie musiałbym zbierać danych za pomocą tego cudownego hakerskiego interfejsu RTTI, ponieważ zostałby już zebrany. –

Odpowiedz

8

Korzystanie Rozszerzone RTTI, możliwe jest, aby stworzyć ogólną funkcję, która pobiera tablicę i nazwę pola jako wejścia i wykorzystuje tablicę RTTI macierzy, aby wyodrębnić tylko wartości tego pola i utworzyć z nimi nową tablicę z poprawnym typem danych.

Poniższy kod działa dla mnie w 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

Dzięki za pomoc, wypróbuję tę metodę. Zaczynam rozumieć, że bezpośrednie tłumaczenie nie jest takie proste. Wygląda na to, że Fortran ma kilka fajnych cech macierzy, ale próba ich symulacji w Delphi nie jest zbyt skuteczna. Zamiast przekazywać tablicę, po prostu przekazałem całą tablicę ścienną w jednym parametrze. – bruce

+0

Remmy, musiałem zmienić pętlę na I: = 0 na długość (Arr) -1, aby działało w Delphi 2010. Jestem pod wrażeniem, to rozwiązuje problem i jest całkiem zadbane. – bruce

+0

Kod został napisany i przetestowany w XE2. Czy mówisz, że 'High()' nie działa na dynamicznych tablicach w D2010? Zaktualizowałem swoją odpowiedź tą zmianą. –

2

Aby odpowiedzieć na twoje pytanie, nie, nie ma metody konstruowania języka ani wygody, aby podzielić pojedynczą kolumnę z tablicy rekordów na własną prostą tablicę.

Polecam coś jak następuje:

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; 

to jeśli chcesz korzystać z dynamicznych tablic. Osobiście, jeśli przenoszenie tego użyję List oraz List, jak w:

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

Korzystając z rozszerzonego interfejsu RTTI, możliwe jest utworzenie ogólnej funkcji, która pobiera tablicę i nazwę pola jako dane wejściowe i używa tablicy RTTI macierzy do wyodrębnienia wartości tego pola i utworzenia z nimi nowej tablicy. –

+0

Wysłałem odpowiedź z demonstracją Rozszerzonego RTTI w akcji. –

7

Zamieszczam to jako odpowiedź, ponieważ komentarze są nieco zbyt ograniczone, aby je wypowiedzieć.

Odpowiedź ta stara się wyjaśnić różnice w układzie pamięci tablic i rekordów w FORTRAN i Delphi i zmienia answer przez Todd Grigsby i answer przez Remy Lebeau (I upvoted oba).

FORTRAN i kilka innych centrycznych języków obliczeniowych przechowuje zagnieżdżone tablice w column major order. Delphi i wiele innych języków używa row major order.

Z perspektywy pamięci, zapis nie jest niczym innym niż tablicę pól:

  • ma nazwy, a nie indeks
  • może mieć różne rodzaje

Dla obliczenia intensywnego operacje, może mieć sens przechowywanie głównej kolejności kolumn zagnieżdżonych, kiedy twoje algorytmy faworyzują kolumny. To samo dotyczy zamówienia głównego rzędu. Więc w pętlach musisz match the order of your indexes with the order of your storage.

Biorąc pod uwagę ten rekord i tablica definicja w FORTRAN:

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

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

i funkcjonalnym odpowiednikiem definicja w Delphi:

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

var 
    Wall: array of WallInfo; 

i tablicę 3 elementów WallInfo, to jak układ pamięci wyglądałoby (wszystkie były ciągłymi obszarami pamięci, dzielę je na linie, aby były czytelne):

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

w Delphi:

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

Więc to wezwanie FORTRAN:

CALL HeatFlow (Wall% Powierzchnia, Ściana% Azymut)

po prostu przekazać wskazówki do obszaru [ 0] i położenia Azymutów [0] oraz długość tych obszarów pamięci dla funkcji.

W Delphi, że nie jest możliwe, więc trzeba

  1. skonstruować nowy obszar i Azymut arays
  2. skopiować je z informacją w tablicy WallInfo przykład rekord o nazwie Mur
  3. wyślij je do funkcji
  4. jeśli są to parametry var: skopiuj zmiany z dwóch tablic z powrotem do muru

Todd Grigsby i Remy Lebeau wykazały pierwsze trzy kroki w ich odpowiedzi, używając prostego kodu Delphi lub rejestru RTTI Delphi.
Krok 4 działa w podobny sposób.

Oba rozwiązania wykorzystują generyczne, które zostały wprowadzone w Delphi 2009. Until Delphi 2010, RTTI on records was very minimal), więc otrzymałeś odpowiednią wersję Delphi dla obu odpowiedzi.

Uwaga (ponownie): tłumacząc swoje algorytmy z FORTRAN na Delphi, upewnij się, że oglądasz pętle i inne indeksowanie w tablicach z powodu głównej zmiany kolumny/wiersza.

+0

Jeroen, twoje komentarze na temat indeksowania pętli są doceniane. Już natknąłem się na ten problem. Nie zdawałem sobie sprawy z różnicy w zamówieniach tablic między Delphi i Fortran. Teraz ma o wiele więcej sensu. – bruce

+0

Cieszę się, że pomogło uzyskać więcej informacji. Podtrzymuj dobre pytania! –

+0

Nie widzę niczego w pytaniu dotyczącym problemów z wierszem i kolorem głównym. Gdzie powstają w pytaniu? Pytanie wydaje mi się, że chodzi o instrument Fortran w celu wsparcia projekcji. Prawdopodobnie zostanie to zaimplementowane za pomocą techniki stride a la numpy array. –

Powiązane problemy