Kopiowanie dynamicznej tablicy - wyniku pracy funkcji.

0

Witam
Chyba mi ten tytuł nie wyszedł :/
W skrócie :
Mam funkcje która oddaje wynik w formie dynamicznej tablicy rekordów.
Funkcja w swym "ciele" trochę miesza więc nie chciał bym jej zbyt często odpytywać.
I problem :
W procedurze potrzebuje danych z w/w funkcji Więc ją wywołuje i ... musiał bym jakoś sklonować sam wynik tej funkcji - ale jak to zrobić nie znając jej długości, którą muszę znać by utworzyć dynamiczną tablice przetrzymującą ten wynik (jego kopie).

Coś w tym style:

type
dane = record
    dane0:integer;
    dane1:string;
  end;
  dane_ar = array of dane;  
  
function GetData(argumenty):dane_ar;
 var costam:integer;
	begin
	 costam:= jakas_liczba_uzyskana_z_jakichs_operacji; 
	 SetLenght(Result,costam);
	 for i:=0 to costam do 
		begin
			Result[i].dane0 := jakies_dane0;
			Result[i].dane1 := jakies_dane1;
		end;
end;                                      

procedure KtoraToWykorzystuje();
var 
	kopia : dane_ar;
begin
	kopia:=GetData(argumenty); //????
	// i tu robie coś na tej kopi
end;
0

Yyy... o ile dobrze rozumiem twój problem, to chyba po prostu umknęło Ci, że oprócz SetLength() do ustawienia rozmiaru dynamicznej tablicy, jest też Length() do odpytania, jaki ma obecnie rozmiar. :D

0

@Sensacyjny Sebastian: Ja to wiem - ale to wymagało by dwukrotnego "uruchomienia" tej funkcji - raz by zbadać ilość elementów (dla Lenght) a drugi by znając jej długość wyciągnąć z niej dane. A jak pisałem funkcja "troche miesza" więc nie chce jej co chwila odpalać. Zależy mi by zrobiś to "jednorazowo". Bez użycia zmiennych globalnych (bo to by rozwiązało problem ale nieładnie i mi nie pasuje).

0
titako napisał(a):

to wymagało by dwukrotnego "uruchomienia" tej funkcji - raz by zbadać ilość elementów (dla Lenght) a drugi by znając jej długość wyciągnąć z niej dane.

Yyy, że jak? W funkcji GetData() nie operujesz na żadnej zmiennej globalnej, tylko zwracasz tablicę dynamiczną. Tablica dynamiczna przechowuje jakiś z góry ustalony typ i ma długość, którą można zmieniać przez SetLength() oraz ją "wyciągnąć" przez Length(). Po tym, jak w funkcji wołającej zrobisz kopia := GetData(argumenty), możesz zrobić Length(kopia) i to nic Ci nie "namiesza" w tej tablicy. Jak nie chcesz wołać Length() parę razy, to zawsze możesz przypisać tę wartość do zmiennej.

0

Wyglądało by to tak:

dlugosc:=Lenght(GetData(argumenty)); // pierwsze wywołanie wnętrza funkcji
SetLenght(kopia,dlugosc);
kopia:=GetData(argumenty); // drugie wywołanie wnętrza funkcji

Czyli wywołam wnętrze funkcji 2 razy - A wnętrze jej jest bazodanowe i gęste od zapytań - więc nie chciał bym budzić smoka dwa razy.

1

Ale zdajesz sobie sprawę, że Result wewnątrz GetData() nie jest statyczne, tzn. jest za każdym razem nową tablicą - i kolejne wywołania zwracają tablice niezależne od siebie?

a := GetData(argumenty);
b := GetData(argumenty);
a[0] := jakas_wartosc;

W takim kodzie zmiana a[0] nie spowoduje zmiany wartości b[0] - tablice są od siebie zupełnie niezależne.


Poza tym - zawsze przecież możesz zrobić:

oryginal := GetData(argumenty);
SetLength(kopia, Length(oryginal));
for i := 0 to Length(oryginal) - 1 do kopia[i] := oryginal[i];
0
kopia:=GetData(argumenty);
dlugosc:=Lenght(kopia);

Ale pamiętaj że następuje kopiowanie wyniku, więc rozważ:

procedure GetData(argumenty;var ret:dane_ar);
1

Ale jestem głupi ! - Moge przepisać dynamiczną tablice bezpośrednio :D. Założyłem z góry że tak łatwo nie będzie. I bez sensu główkowałem. Temat do śmieci :)

1

Zastanów się też nad takim rozwiązaniem:

program ideone;

uses sysutils,classes;

type
dane=record
	dane0:integer;
	dane1:string;
end;
dane_ar=array of dane;  

var tb:dane_ar;
var i:Integer;
var tm:TDateTime;
var ts:TStringList;
const str:String='ala ma kota';
const Size:Integer=30000;

begin
	tm:=now;
	SetLength(tb,Size);
	for i:=0 to Size-1 do
	begin
		tb[i].dane0:=i;
		tb[i].dane1:=str;
	end;
	WriteLn(FormatDateTime('nn:ss.zzz',now-tm));

    tm:=now;
    ts:=TStringList.Create;
	for i:=0 to Size-1 do
	begin
		ts.AddObject(str,TObject(Pointer(i)));
	end;
	WriteLn(FormatDateTime('nn:ss.zzz',now-tm));
end.
0

@_13th_Dragon: Dzięki - ja podałem czysto przykładowe wnętrze funkcji, tam tego jest więcej i przyszła wielkość tablicy daje się oszacować, a dodatkowo cześć danych będzie przeliczana wiec Tstringlist troche nie pasuje. Ale dzięki ;)

0
titako napisał(a):

W procedurze potrzebuje danych z w/w funkcji Więc ją wywołuje i ... musiał bym jakoś sklonować sam wynik tej funkcji - ale jak to zrobić nie znając jej długości, którą muszę znać by utworzyć dynamiczną tablice przetrzymującą ten wynik (jego kopie).

Długość przecież możesz zwrócić za pomocą argumentu w postaci ”ślepej” referencji:

function GetData({tu argumenty wejściowe}, out ACount: Integer): TDataArr;

Funkcja uzupełnia macierz w Result, a na koniec przypisuje jej długość do argumentu ACount. Takie rozwiązanie pozwala uniknąć wielokrotnego wywołania funkcji, tworzenia kopii i innych cudów – dwie pieczenie na jednym ogniu.

Problemem nie jest dobranie konstrukcji nagłówka funkcji, a korzystanie z archaicznych typów danych. Gdybyś dane przechowywał w liście generycznej (np. TFPGList) zamiast w badziewnych tablicach, to zawsze miałbyś dostęp do właściwości Count, zwracającej liczbę rekordów listy. I zamiast zwracać macierz w rezultacie funkcji, mógłbyś uzupełniać listę na podstawie przekazanej w parametrze referencji listy:

procedure FillList({tu argumenty wejściowe}, AList: TDataList);

gdzie TDataList to wyspecjalizowany TFPGList.

Natomiast jeśli potrzebujesz przechowywać pary danych, z czego jedna informacja w parze zawsze ma unikalną wartość, to spokojnie możesz skorzystać z generycznej mapy (np. TFPGMap).

1 użytkowników online, w tym zalogowanych: 0, gości: 1