Niechciana modyfikacja w innej tablicy

0

Cześć,
pojawił mi się ostatnio dość dziwny problem.
Mam tablice przechowujaca dane w postaci rekordow. Stworzylem procedure, ktora paruje jej elementy na podstawie jakis zasad. Przekazuje do procedury tymczasowa tablice stworzona na postawie tej wlasciwej zeby mogla swobodnie na niej operowa, usuwac elementy itp.

Problem w tym, ze w momencie usuwania elementow tej tymczasowej, modyfikaja sie tez wartosci w tej glownej. Pierwotnie operowalem na wskaznikach ale myslalem, ze moze gdzies cos zle przekazalem. Zmodyfikowalem wszystko i problem dalej wystepuje.

Mianowicie:

mam typy:

TRekord=record
id:string;
end;

TTablica=array of TRekord

Stworzyłem procedurę, która paruje elementy tablicy po jakiś danych, które są jeszcze w elementach TRekord. Wygląda ona tak:


procedure SparujElementy(tab:TTablica);
var
tmpA,tmpB:string;    
begin

if Length(tab)=0 then
Exit;

{cos tam wykonuje, licze, dodaje, odejmuje i po sparowaniu pozycji usuwam je z tymczasowej tablicy zeby nie bylu juz brane pod uwage, pary zapisywane sa do bazy i wszystko smiga}

             tmpA:=tab[0].id; //pierwszy parowany
             tmpB:=tab[i].id; //drugi parowany

             tab:=UsunElementTablicy(tmpA, tab);
             tab:=UsunElementTablicy(tmpB, tab);

             SparujElementy(tab);

             Exit;

 end;

Tu jest jeszcze wszystko ok, przy debugowaniu znalazlem, ze problem pojawia sie w funkcji UsunElementTablicy:

function UsunElementTablicy(id:string; tablica:TTablica):TTablica;
var
i,j:integer;
begin

//szukamy pozycji elementu o danym id
for j:=0 to Length(tablica)-1 do
  begin
       if tablica[j].id=id then
       begin
           Break;
       end;

  end;

//wywalamy
for i:=j to High(tablica)-1 do
  tablica[i]:= tablica[i+1];

SetLength(tablica, High(tablica));

Result:=tablica;

end; 

Wszystko to wywolywane jest w ten sposob:

//TablicaElementow to wlasciwa tablica 

tmpTablicaElementow:=TablicaElementow;

SparujElementy(tmpTablicaElementow);

Problem pojawia sie dokladnie w tym miejscu przy usuwaniu elementow w funkcji UsunElementyTablicy

  tablica[i]:= tablica[i+1]; //TUTAJ

Elementy tablicy tymczasowej sa poprawnie przesuwane a rozmiar tablicy zmniejszany ale modyfikuja sie wartosci tez w tej glownej (TablicaElementow) - sa rowniez przesuwane!!

Jezeli sparuje elementy 2 razy od poczatku w petli to majac na poczatku TablicaElementow:

[0].id=5;
[1].id=6;
[2].id=7;
[3].id=8;

to po tej operacji dostaje jakies dziwne, gdzie nie powinny byc w ogole ruszone.

[0].id=5;
[1].id=8;
[2].id=8;
[3].id=8;

Prosze o pomoc bo juz nie mam pojecia co zle robie. Moze ktos zaproponuje lepsze rozwiazanie takiej sytuacji, bardziej optymalne skoro tutaj popelnilem gdzies blad?

2

Nie chce mi się tego analizować ale mała podpowiedź jeżeli masz tablicę dynamiczną to do usuwania elementów stosuj procedurę Delete i znaleziony index tablicy zapamiętaj w innej zmiennej niż ta którą modyfikuje pętla (w twoim wypadku "j").

1

@karpov - na litość boską, tyle czasu już programujesz i dalej nie formatujesz kodu jak należy... To tylko macierz dynamiczna - ciekawe co by było, gdybyś musiał użyć listy jedno- lub dwukierunkowej :]

A tak na poważnie - macierze do metod przekazuj przez referencję i na nich operuj, bez zwracania całości w rezultacie funkcji.

0

Generalnie mam jakis problem. Nie wiem czy to tak powinno dzialac, czy juz jestem totalnym glabem...

Zrobilem testy:

ttab=array of string;
(...)

var
  tab,tab1:ttab;
begin                

SetLength(tab, 3);
  tab[0]:='2';
  tab[1]:='3';
  tab[2]:='4';

  tab1:=tab;

  tab1[1]:='10'; 

i... zarowno w tab jak i tab1 zostala zmieniona wartosc z 3 na 10...
No chyba nie tak to powinno dzialac :(

1

@karpov - to się nazywa refcounting i jest to prawidłowe zachowanie :]

Spróbuj tak (pisane z palca):

TTab = array of String;

{...}

var
  Tab, Tab1: TTab;
begin
  SetLength(Tab, 3);
  Tab[0] := '2';
  Tab[1] := '3';
  Tab[2] := '4';
 
  Tab1 := Tab;
  UniqueString(Tab1[1]);

  Tab1[1] := '10';

Do poczytania - http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/[email protected]

0

Ja chce po prostu uzyskac szybka kopie pierwszej tablicy. Myslalem do tej pory, ze wystarczy proste przypisanie

Tab1:=Tab;

ale okazalo sie, ze swiat programowania znowu mnie zaskoczyl :D

Zrobilem rozwiazanie 'na sile' i dziala:

 
var
  Tab, Tab1: TTab;
  i:integer;
begin
  SetLength(Tab, 3);
  Tab[0] := '2';
  Tab[1] := '3';
  Tab[2] := '4';

  setLength(tab1, Length(tab));

  for i:=0 to Length(tab)-1 do
  tab1[i]:=tab[i];


  Tab1[1] := '10';

O dziwo zawsze mi sie wydawalo, ze

  for i:=0 to Length(tab)-1 do
  tab1[i]:=tab[i];

jest rowne

tab1:=tab;

.

1

Patrząc z zewnąrz faktycznie jest to to samo, bo obie tablice będą posiadać taką samą zawartość;

Natomiast od wewnątrz to zupełnie coś innego - w pierwszym przykładzie licznik referencji brany jest pod uwagę, w drugim nie jest; W pierwszym przykładzie łańcuchy nie są kopiowane i obie macierze wskazują na tą samą pamięć, w drugim zaś łańcuchy w pamięci istnieją podwójnie.

2
karpov napisał(a):

Ja chce po prostu uzyskac szybka kopie pierwszej tablicy.

a po ludzku?

tab2:= Copy(tab1, 0, Length(tab1)); //nie trzeba wcześniej ustalać wielkości tab2 

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