Samoalokująca tablica. Tablice dynamiczne. / Self-allocating array. Dynamic arrays.

Jeden z dwóch artykułów poświęconych zmiennym wariantowym i samoalokującym strukturom danych. Artykuł prezentuje rozwiązanie w Delphi i tworzy parę z artykułem poświęconym rozwiązaniu w C#. Link do artykułu z kodem C# znajduje się na końcu. One of two articles about variant variables and self-allocating data structures. The article presents a solution in Delphi and is a pair with the article about the solution in C#. You can find the link to article in C# at the end.

***

  • Uwaga:

  • Jeśli nie chcesz tego czytać, a zobaczyć, jak to działa, pobierz pełny spakowany (.zip) projekt Delphi z kodem źródłowym, klikając tutaj SelfAllocArr.zip

  • Uwagi do projektu (prototypowego) na końcu tego artykułu.

  • Note:

  • If you do not want to read it, but you want to see how it works, download full zipped Delphi project with enclosed source code by clicking here SelfAllocArr.zip

  • Notes to project (prototype) at the end of this article.

Wstęp Introduction
Panie Protasewicz, czy jak pan chce zaorać pole, to musi pan używać czołgu?

Jeden z moich wykładowców
Mr. Protasewicz, or how you want to plow the field, then you must use a tank?

One of my lecturers
Jak stworzyć tablicę, która sama będzie dopasowywała rozmiar do indeksu, którego użyję, tzn. nie trzeba będzie deklarować jej rozmiaru na początku? Pascal jest bardzo restrykcyjny, ale na szczęście w Delphi są tablice dynamiczne. Chciałem to zrobić w bardzo uniwersalny sposób, jednak okazało się, że czasami w optymalizacji najlepszą metodą jest dychotomia. Inteligentna metoda nie pomoże, kiedy liczy się czas wykonania zadania. Więc zdecydowałem się zrobić kilka mniejszych zadań dla 1, 2, 3, 4-wymiarowych tablic. Nie sądzę, żebyś kiedyś spotkał więcej niż 4-wymiarową tablicę w zwykłej praktyce programistycznej. How to create an array that will fit the same size to the index, which I use, that does not need to declare its size at the beginning? Pascal is very restrictive, but fortunately there were dynamic arrays in Delphi. I wanted to do it in a very universal way, but it turned out that sometimes the best way to optimize is the dichotomy. Intelligent method does not help when the time counts for the task. So I decided to do some minor tasks for 1, 2, 3, 4-dimensional arrays. I do not think you ever met more than a 4-dimensional array in ordinary programming.
W zależności od użytych indeksów tablica sama przyjmie odpowiedni rozmiar - wymagane jest tylko określenie typu: jedno-, dwu-, trzy- czy czterowymiarowa (w mojej praktyce spotkałem najwyżej tablice trójwymiarowe). Tablica (indeksowana od zera) jest zbiorem elementów typu Variant, co jeszcze zwiększa jej elastyczność. Należy ją utworzyć metodą Create, a zwalniać metodą Destroy (twój program zwolni ją na końcu procedury, w której została zadeklarowana) lub przy zamknięciu programu. Można też użyć metody Clear, która zwolni z pamięci wszystkie elementy tablicy, jednak nie zniszczy jej jako obiektu. Depending on used indexes the array will set its size to necessary one – you must decide about the type only: one-, two-, three-, or four-dimmensional (in my prctice I have used maximium three-dimmensional arrays). The array (zero-base indexed) is the set of values of type Variant, what does give its greater flexibility. You must use method Create to establish one, and free it using Destroy method (your program will free the allocated array at the end of procedure being its scope of declaration) or during the end of program. You could use the method Clear, which free all allocated values of the array, but the object will still exist.

Typ Variant i koncepecja alokacji | Variant type and allocation concept ---------------- | ---------------- Typ Variant jest idealny do tego, by nie deklarować typów danych oprócz niego samego. Moja tablica jest jak lista, drzewo albo las, w którym mogą występować różne typy danych. Przy czym wszystkie te struktury są bardzo niewygodne w użyciu w przeciwieństwie do tablicy. Moja tablica też oszczędza pamięć - alokuje tylko indeksy od zera do maksymalnego użytego w programie i tak się dzieje we wszystkich wymiarach, co pokazuje rysunek poniżej. Typ Variant jest również na tyle wygodny, że coś, co nie zostało wcześniej podstawione daje się odczytać jako 0 lub pusty string, a zadbałem o to, żeby z rozmiarami tablic wszystko było w porządku. W debuggerze możesz zobaczyć coś takiego, jak Unassigned. Ale można to odczytywać jako 0 lub pusty string. | Variant type is the ideal not to declare data types except Variant. My array is like a list, tree or forest, where there may be different types of data. At the same time, all these structures are very awkward to use, in contrast to the array. My array also saves memory - allocates only indexes from zero to the maximum used in the program and this happens in all dimensions, as shown in the figure below. Variant type is also comfortable enough that something has not previously been assigned can be read as a 0 or an empty string, and I made sure that the size of the array was in order. In the debugger you can see something like Unassigned. But it can be read as a 0 or an empty string. Ważną jej cechą jest to, że tworzy tylko niezbędne wektory np.: | An important feature is, that the only necessary vectors are created eg.: A[2, 3] := 15 | A[2, 3] := 15 zaalokuje 6 bajtów | will alloc 6 bytes only (0, 0, (0, 0, 0, 15)) | (0, 0, (0, 0, 0, 15)) a nie - 12 bajtów (dokładniej mówiąc zamiast zer pojawią się wartości Unassigned) | but not a 12 bytes (preciselly in places of zeros there will Unassigned values appeare) ((0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 15)) | ((0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 15)) Do niezainicjowanych pól można się odwoływać bez obawy o działanie programu: | You can call not initiated fields (cells) being not afraid of program errors: odwołanie (do nieistniejącego elementu A[5, 7]) - string1 := A[5, 7] - spowoduje zaalokowanie elementu A[5, 7] i podstawienie pod string1 wartości (pusty string). | Getting (the non-existent element A[5, 7]) – string1 := A[5, 7] will cause allocating element A[5, 7] and will assign string1 the empty string. odwołanie (do nieistniejącego elementu A[6, 8]) - int1 := A[6, 8] - spowoduje zaalokowanie elementu A[6, 8] i podstawienie pod int1 wartości 0. | Getting (the non-existent element A[6, 8]) – int1 := A[6, 8] – will allocate element A[6, 8] and assign int1 value 0. etc. | etc. Samoalokująca tablica przeznaczona jest raczej dla doświadczonych programistów, którym nieobcy jest debugger, ponieważ wykrycie błędów w indeksach i błędów w typach danych jest trudne. | Self-resizing array is dedicated to advanced programmers rather, to whom the debbugger is well known, because finding errors in the indexes and data types is difficult.
Konwersje w typie Variant | Conversion in Variant type ---------------- | ---------------- Wszystkie typy całkowite, zmiennoprzecinkowe i boolowskie oraz niektóre przypadki string są kompatybilne, jeśli chodzi o podstawienia, z typem Variant, co obrazuje poniższy przykład. | All integer, floating-point and boolean types and some cases of strings are compatibile, if you mean assignment to type Variant, what is shown in the example below.
#### Przykład / Example
var
  v1, v2, v3, v4, v5: Variant;
  i: integer;
  d: double;
  s: string;
begin
  v1 := 1;              { wartość integer / integer value                                         }
  v2 := 1234.5678;      { wartość real / real value                                               }
  v3 := 'Hello world!'; { wartość string / string value                                           }
  v4 := '1000';         { wartość string / string value                                           }
  v5 := v1 + v2 + v4;   { wartość real 2235.5678 / real value 2235.5678                           }
  i  := v1;             { i = 1 (wartość integer) / i = 1 (integer value)                         }
  d  := v2;             { d = 1234.5678 (wartość real) / d = 1234.5678 (real value)               }
  s  := v3;             { s = 'Hello world!' (wartość string) / s = 'Hello world!' (string value) }
  i  := v4;             { i = 1000 (wartość integer) / i = 1000 (integer value)                   }
  s  := v5;             { s = '2235.5678' (wartość string) / s = '2235.5678' (string value)       }
end;

#### Tabela konwersji / Table of conversion
Typ źródłowy / Source type Typ docelowy / Destination type Konwersja Conversion
integer real integer na real integer to real
integer string IntToStr IntToStr
integer boolean 0 na false, pozostałe na true 0 to false, others to true
real integer Round Round
real string FloatToStr FloatToStr
real boolean 0 na false, pozostałe na true 0 to false, others to true
string integer StrToInt, może spowodować wyjątek StrToInt, possible exception occurrence
string real StrToFloat, może spowodować wyjątek StrToFloat, possible exception occurrence
string boolean 'false' na false, 'true' na true (nie zależy od wielkości znaków), może spowodować wyjątek 'false' to false, 'true' to true (not case-sensitive), possible exception occurrence
boolean integer false na 0, true: ustawia wszystkie bity na 1 (np. integer = -1, byte = 255) false to 0, true: sets all bits to 1 (eg. integer = 1, byte = 255)
boolean real false na 0, true na 1 false to 0, true to 1
boolean string false na 'False', true na 'True' (wielkość liter zależy od ustawień środowiska) false to 'False', true to 'True' (character case depentant on environment settings
Unassigned Integer 0 0
Unassigned string (pusty string) (empty string)
Unassigned boolean false false

Konwersja typu char dokonywana jest tak jak dla typu string.
Conversion of type char is done like for type string string.


Jak tutaj działa alokacja? | How does allocation work in this case ---------------- | ---------------- Można tu albo wcale nie wstawiać komentarzy albo rozpisywać się bez końca. | One could put here no comments either write endless comments Dla lepszego zobrazowania o co tu chodzi posłużę się zdaniem: | I will say what is below so that you could understand it better: "Tu się nic nie dzieje niepotrzebnie." | "Anything is going here unnecessarily." i poniższym obrazkiem pokazującym 3 kolejne alokacje dla tej samej tablicy trójwymiarowej | and the following picture showing 3 succesive allocations for the same three-dimensional (Uwaga: elementy, których nie widać nie istnieją, natomiast elementy, które widać jako puste prostokąty mają wartość Unassigned): | (Note: boxes which are not visible doesn't exist, and the boxes which are empty are Unassigned):
Słownik dla obrazka poniżej | Vocabulary for picture below ---------------- | ---------------- Polski | English Tablica[...] | Array[...]
![Alokacja.JPG](https://4programmers.net/Download/4901/1199)
Interfejs / Interface
unit UAutoAllocArray;

uses
  Variants;

type
  TAutoAllocArrayDim1 = class(TObject) //tablica jednowymiarowa
                                       //one-dimensional array
    private
      FArr1: array of Variant;
      function  GetArr(aIndex: integer): Variant;
      procedure SetArr(aIndex: integer; aValue: Variant);
      procedure ReDim (aIndex: integer);
    public
      CannotAlloc: boolean; //jeśli true, alokacja się nie powiodła (zwykle
                            //przekroczono 2GB)
                            //
                            //if true, allocation failed (ussualy 2GB
                            //overallocated means over type capacity)
      procedure    Clear;
      property     Arr[aIndex: integer]: Variant
                     read GetArr write SetArr; default;
            //ponieważ użyto default można się odwołać np. A[5] := 1, a nie
            //A.Arr[5] := 1
            //
            //because of use of the 'default' directive you can call eg.
            //A[5] := 1, and you do not need A.Arr[5] := 1
      constructor  Create;
      destructor   Destroy; override;
  end;
 
type
  TAutoAllocArrayDim2 = class(TObject) //tablica dwuwymiarowa
                                       //two-dimensional array
    private
      FArr2: array of array of Variant;
      function  GetArr(aIndex1, aIndex2: integer): Variant;
      procedure SetArr(aIndex1, aIndex2: integer; aValue: Variant);
      procedure ReDim (aIndex1, aIndex2: integer);
    public
      CannotAlloc: boolean; //jeśli true, alokacja się nie powiodła (zwykle
                            //przekroczono 2GB)
                            //if true, allocation failed (ussualy 2GB
                            //overallocated means over type capacity)
      procedure    Clear;
      property     Arr[aIndex1, aIndex2: integer]: Variant
                     read GetArr write SetArr; default;
            //ponieważ użyto default można się odwołać np. A[5, 6] := 1, a nie
            //A.Arr[5, 6] := 1
            //
            //because of use of the 'default' directive you can call eg.
            //A[5, 6] := 1, and you do not need A.Arr[5, 6] := 1
      constructor  Create;
      destructor   Destroy; override;
  end;

type
  TAutoAllocArrayDim3 = class(TObject) //tablica trójwymiarowa
                                       //three-dimensional array
    private
      FArr3: array of array of array of Variant;
      function  GetArr(aIndex1, aIndex2, aIndex3: integer): Variant;
      procedure SetArr(aIndex1, aIndex2, aIndex3: integer; aValue: Variant);
      procedure ReDim (aIndex1, aIndex2, aIndex3: integer);
    public
      CannotAlloc: boolean; //jeśli true, alokacja się nie powiodła (zwykle
                            //przekroczono 2GB)
                            //
                            //if true, allocation failed (ussualy 2GB
                            //overallocated means over type capacity)
      procedure    Clear;
      property     Arr[aIndex1, aIndex2, aIndex3: integer]: Variant
                     read GetArr write SetArr; default;
            //ponieważ użyto default można się odwołać np. A[5, 6, 2] := 1, a
            //nie A.Arr[5, 6, 2] := 1
            //
            //because of use of the 'default' directive you can call eg.
            //A[5, 6, 2] := 1, and you do not need A.Arr[5, 6, 2] := 1
      constructor  Create;
      destructor   Destroy; override;
  end;
 
type
  TAutoAllocArrayDim4 = class(TObject) //tablica czterowymiarowa
                                       //four-dimensional array
    private
      FArr4: array of array of array of array of Variant;
      function  GetArr(aIndex1, aIndex2, aIndex3, aIndex4: integer): Variant;
      procedure SetArr(aIndex1, aIndex2, aIndex3, aIndex4: integer;
                         aValue: Variant);
      procedure ReDim (aIndex1, aIndex2, aIndex3, aIndex4: integer);
    public
      CannotAlloc: boolean; //jeśli true, alokacja się nie powiodła (zwykle
                            //przekroczono 2GB)
                            //
                            //if true, allocation failed (ussualy 2GB
                            //overallocated means over type capacity)
      procedure    Clear;
      property     Arr[aIndex1, aIndex2, aIndex3, aIndex4: integer]: Variant
                     read GetArr write SetArr; default;
            //ponieważ użyto default można się odwołać np. A[5, 6, 2, 12] := 1,
            //a nie A.Arr[5, 6, 2, 12] := 1
            //
            //because of use of the 'default' directive you can call eg.
            //A[5, 6, 2, 12] := 1, and you do not need A.Arr[5, 6, 2, 12] := 1
      constructor  Create;
      destructor   Destroy; override;
  end;

implementation

Funkcje tablicy jednowymiarowej / One-dimmensional array functions

{Funkcje tablicy jednowymiarowej / One-dimensional array functions}

procedure TAutoAllocArrayDim1.Clear;
begin
  SetLength(FArr1, 0);
end;

constructor TAutoAllocArrayDim1.Create;
begin
  inherited;
  SetLength(FArr1, 0);
end;

destructor TAutoAllocArrayDim1.Destroy;
begin
  Clear;
  inherited;
end;

procedure TAutoAllocArrayDim1.ReDim(aIndex: integer);
{
Procedura sprawdza, czy użyty indeks jest większy od największego użytego do tej
pory.

Jeżeli nie jest, nic nie robi w danym wymiarze tablicy.

Jeżeli jest, zwiększa rozmiar tablicy do użyty_index + 1 w danym wymiarze
tablicy.

Tu: w wymiarze pierwszym.
}
{
The routine tests if used input parameter index is greater than greatest till
particular moment.

If one isn't does nothing to array size.

If one is, the size of the array is incremented  used_index + 1 in a single
array dimension.

Here: in the first dimension.
}
begin
    try
      if Length(FArr1) < aIndex + 1 then
        SetLength(FArr1, aIndex + 1);
      CannotAlloc := false;
    except
      CannotAlloc := true;
    end;
end;

function TAutoAllocArrayDim1.GetArr(aIndex: integer): Variant;
begin
  ReDim(aIndex);
  Result := FArr1[aIndex];
end;

procedure TAutoAllocArrayDim1.SetArr(aIndex: integer; aValue: Variant);
begin
  ReDim(aIndex);
  FArr1[aIndex] := aValue;
end;

Funkcje tablicy dwuwymiarowej / Two-dimmensional array functions

{Funkcje tablicy dwuwymiarowej / Two-dimensional array functions}

procedure TAutoAllocArrayDim2.Clear;
var
  i: integer;
begin
  for i := 0 to Length(FArr2) - 1 do
    SetLength(FArr2[i], 0);
  SetLength(FArr2, 0);
end;

constructor TAutoAllocArrayDim2.Create;
begin
  inherited;
  SetLength(FArr2, 0);
end;

destructor TAutoAllocArrayDim2.Destroy;
begin
  Clear;
  inherited;
end;

procedure TAutoAllocArrayDim2.ReDim(aIndex1, aIndex2: integer);
{
Procedura sprawdza, czy użyty indeks jest większy od największego użytego do tej
pory.

Jeżeli nie jest, nic nie robi w danym wymiarze tablicy.

Jeżeli jest, zwiększa rozmiar tablicy do użyty_index + 1 w danym wymiarze
tablicy.

Tu: w wymiarze pierwszym, drugim.

Uwaga: Istotna jest kolejność tej operacji:
      najpierw pierwszy wymiar,
      potem drugi wymiar.
}
{
The routine tests if used input parameter index is greater than greatest till
particular moment.

If one isn't does nothing to array size.

If one is, the size of the array is incremented  used_index + 1 in a single
array dimension.

Here: in the first one and the second one.

Note: The order of successive operations is important:
      firstly first dimension,
      next second dimension.
}
begin
    try
      if Length(FArr2) < aIndex1 + 1 then
        SetLength(FArr2, aIndex1 + 1);
      if Length(FArr2[aIndex1]) < aIndex2 + 1 then
        SetLength(FArr2[aIndex1], aIndex2 + 1);
      CannotAlloc := false;
    except
      CannotAlloc := true;
    end;
end;

function TAutoAllocArrayDim2.GetArr(aIndex1, aIndex2: integer): Variant;
begin
  ReDim(aIndex1, aIndex2);
  Result := FArr2[aIndex1, aIndex2];
end;

procedure TAutoAllocArrayDim2.SetArr(aIndex1, aIndex2: integer; aValue: Variant)
  ;
begin
  ReDim(aIndex1, aIndex2);
  FArr2[aIndex1, aIndex2] := aValue;
end;

Funkcje tablicy trójwymiarowej / Three-dimmensional array functions

{Funkcje tablicy trójwymiarowej / Three-dimensional array functions}

procedure TAutoAllocArrayDim3.Clear;
var
  i, j: integer;
begin
  for i := 0 to Length(FArr3) - 1 do
  begin
    for j := 0 to Length(FArr3[i]) - 1 do
      SetLength(FArr3[i, j], 0);
    SetLength(FArr3[i], 0);
  end;
  SetLength(FArr3, 0);
end;

constructor TAutoAllocArrayDim3.Create;
begin
  inherited;
  SetLength(FArr3, 0);
end;

destructor TAutoAllocArrayDim3.Destroy;
begin
  Clear;
  inherited;
end;

procedure TAutoAllocArrayDim3.ReDim(aIndex1, aIndex2, aIndex3: integer);
{
Procedura sprawdza, czy użyty indeks jest większy od największego użytego do tej
pory.

Jeżeli nie jest, nic nie robi w danym wymiarze tablicy.

Jeżeli jest, zwiększa rozmiar tablicy do użyty_index + 1 w danym wymiarze
tablicy.

Tu: w wymiarze pierwszym, drugim, trzecim.

Uwaga:Istotna jest kolejność tej operacji:
      najpierw pierwszy wymiar,
      potem drugi wymiar,
      potem trzeci wymiar.
}
{
The routine tests if used input parameter index is greater than greatest till
particular moment.

If one isn't does nothing to array size.

If one is, the size of the array is incremented  used_index + 1 in a single
array dimension.

Here: in the first one, and the second one, and the third one

Note: The order of successive operations is important:
      firstly first dimension,
      secondly second dimension,
      finally third dimension.
}
begin
    try
      if Length(FArr3) < aIndex1 + 1 then
        SetLength(FArr3, aIndex1 + 1);
      if Length(FArr3[aIndex1]) < aIndex2 + 1 then
        SetLength(FArr3[aIndex1], aIndex2 + 1);
      if Length(FArr3[aIndex1, aIndex2]) < aIndex3 + 1 then
        SetLength(FArr3[aIndex1, aIndex2], aIndex3 + 1);
      CannotAlloc := false;
    except
      CannotAlloc := true;
    end;
end;

function TAutoAllocArrayDim3.GetArr(aIndex1, aIndex2, aIndex3: integer)
  : Variant;
begin
  ReDim(aIndex1, aIndex2, aIndex3);
  Result := FArr3[aIndex1, aIndex2, aIndex3];
end;

procedure TAutoAllocArrayDim3.SetArr(aIndex1, aIndex2, aIndex3: integer;
                                       aValue: Variant);
begin
  ReDim(aIndex1, aIndex2, aIndex3);
  FArr3[aIndex1, aIndex2, aIndex3] := aValue;
end;

Funkcje tablicy czterowymiarowej / Four-dimmensional array functions

{Funkcje tablicy czterowymiarowej / Four-dimensional array functions}

procedure TAutoAllocArrayDim4.Clear;
var
  i, j, k: integer;
begin
  for i := 0 to Length(FArr4) - 1 do
  begin
    for j := 0 to Length(FArr4[i]) - 1 do
    begin
      for k := 0 to Length(FArr4[i, j]) - 1 do
        SetLength(FArr4[i, j, k], 0);
      SetLength(FArr4[i, j], 0);
    end;
    SetLength(FArr4[i], 0);
  end;
  SetLength(FArr4, 0);
end;

constructor TAutoAllocArrayDim4.Create;
begin
  inherited;
  SetLength(FArr4, 0);
end;

destructor TAutoAllocArrayDim4.Destroy;
begin
  Clear;
  inherited;
end;

procedure TAutoAllocArrayDim4.ReDim(aIndex1, aIndex2, aIndex3, aIndex4: integer)
  ;
{
Procedura sprawdza, czy użyty indeks jest większy od największego użytego do tej
pory.

Jeżeli nie jest, nic nie robi w danym wymiarze tablicy.

Jeżeli jest, zwiększa rozmiar tablicy do użyty_index + 1 w danym wymiarze
tablicy.

Tu: w wymiarze pierwszym, drugim, trzecim, czwartym.

Uwaga: Istotna jest kolejność tej operacji:
      najpierw pierwszy wymiar,
      potem drugi wymiar,
      potem trzeci wymiar,
      potem czwarty wymiar.
}
{
The routine tests if used input parameter index is greater than greatest till
particular moment.

If one isn't does nothing to array size.

If one is, the size of the array is incremented  used_index + 1 in a single
array dimension.

Here: in the first one, and the second one, and the third one

Note: The order of successive operations is important:
      firstly first dimension,
      secondly second dimension,
      thirdly third dimension,
      finally fourth dimension.
}
begin
    try
      if Length(FArr4) < aIndex1 + 1 then
        SetLength(FArr4, aIndex1 + 1);
      if Length(FArr4[aIndex1]) < aIndex2 + 1 then
        SetLength(FArr4[aIndex1], aIndex2 + 1);
      if Length(FArr4[aIndex1, aIndex2]) < aIndex3 + 1 then
        SetLength(FArr4[aIndex1, aIndex2], aIndex3 + 1);
      if Length(FArr4[aIndex1, aIndex2, aIndex3]) < aIndex4 + 1 then
        SetLength(FArr4[aIndex1, aIndex2, aIndex3], aIndex4 + 1);
      CannotAlloc := false;
    except
      CannotAlloc := true;
    end;
end;

function TAutoAllocArrayDim4.GetArr(aIndex1, aIndex2, aIndex3, aIndex4: integer)
  : Variant;
begin
  ReDim(aIndex1, aIndex2, aIndex3, aIndex4);
  Result := FArr4[aIndex1, aIndex2, aIndex3, aIndex4];
end;

procedure TAutoAllocArrayDim4.SetArr(aIndex1, aIndex2, aIndex3,
                                       aIndex4: integer; aValue: Variant);
begin
  ReDim(aIndex1, aIndex2, aIndex3, aIndex4);
  FArr4[aIndex1, aIndex2, aIndex3, aIndex4] := aValue;
end;

Przykład użycia / Example of use

procedure TFormMain.Button1Click(Sender: TObject);
var
  a: TAutoAllocArrayDim4;
  b: TAutoAllocArrayDim2;
  s: string;
  i: integer;
begin
  try
    a := TAutoAllocArrayDim4.Create;
    b := TAutoAllocArrayDim2.Create;
    a[2, 1, 3, 1] := 'Artur';
    s := a[2, 1, 3, 1]; //s = 'Artur'
    s := a[4, 2, 0, 3]; //s = ''
    b[10, 7] := 33;
    i := b[10, 7]; // 33
    i := b[21, 8]; // 0
    MessageDlg('Operacja się powiodła / Operation successful',
      mtInformation, [mbOK], 0);
  except
    MessageDlg('Operacja się nie powiodła / Operation error', mtError, [mbOK],
      0);
  end;
end;

Podobny artykuł w C# / Similar article in C#

Artur Protasewicz, 4programmers.net, Własny typ Variant

Link do pobrania projektu / Project download link

SelfAllocArr.zip

9 komentarzy

Na razie muszę się wyleczyć z csharp.

artykuł dobry, wielki + za chęci oraz rezultat. Tylko dlaczego Debian + Chromium AOKP wywala artykuł przedzielony jakimś syfem chińskim?
na win7 + Chrome jest wszystko ok.

Tytułem uzupełnienia w odpowiedzi na komentarz łapczaka...
Jeżeli napiszesz for i := 0 to 100000 do a[i] := 5, to alokacja ma miejsce w każdej iteracji.
Jeżeli napiszesz for i := 100000 to 0 do a[i] := 5, to alokacja ma miejsce tylko jeden raz.

Bardzo to wygodne, ale STRASZNIE WOLNE ... alokacja pamięci dla tablicy jest zapisywana przy dodaniu każdego nowego rekordu. Zapisanie 10 000+ rekordów już mija się z celem.

Dziękuję madmike. Twoja wersja jest lepsza.

Świetny artykuł

Dodałem tabelkę z typami konwersji - autor zdecyduje czy pozostawić - które rozwiązanie jest bardziej czytelne: spis czy tabela

W metodach Clear poprawiłem na Length(Arr) - 1. 10 miesięcy tu jest ten artykuł i wydaje mi się, że był w miarę popularny, a nikt tego nie zauważył? Chyba, że wszyscy, którzy korzystali poprawili sobie sami.
Od niedawna mam stronę:
http://protasewicz.pl
na której akurat ten kod chciałem zamieścić i znalazłem ten błąd. Na stronie jest również trochę matematyczne rozwinięcie na wypadek, gdybyś akurat potrzebował tablicy np. 10-wymiarowej, chociaż regułę budowania kolejnych wymiarów można zaobserwować i tutaj i sądzę, że większość nie będzie miała problemu, ale może ktoś jednak będzie tego potrzebował.
Ostatnio rzadko tu bywam, więc pozdrawiam wszystkich.

MSM, wielkie, wielkie dzięki za uporządkowanie tego tematu i zmobilizowanie mnie do roboty. Praca zespołowa się opłaca.

Ciekawy pomysł z tą biblioteką, ale jak z wydajnością? I straszny bałagan w tym kodzie, dodałbyś chociaż jakieś komentarze, bo ciężko coś zrozumieć :/