Two records sharing the same values? - arrays

I asked another question about using records with operators. During testing, I've discovered an anomaly where two instances of this type seem to share the same memory.
The record has an array of Integer...
type
TVersion = record
Values: array of Integer;
function Count: Integer;
class operator implicit(aVersion: TVersion): String;
class operator implicit(aVersion: String): TVersion;
end;
class operator TVersion.implicit(aVersion: TVersion): String;
var
X: Integer;
begin
Result:= '';
for X := 0 to Length(aVersion.Values)-1 do begin
if X > 0 then Result:= Result + '.';
Result:= Result + IntToStr(aVersion.Values[X]);
end;
end;
class operator TVersion.implicit(aVersion: String): TVersion;
var
S, T: String;
I: Integer;
begin
S:= aVersion + '.';
SetLength(Result.Values, 0);
while Length(S) > 0 do begin
I:= Pos('.', S);
T:= Copy(S, 1, I-1);
Delete(S, 1, I);
SetLength(Result.Values, Length(Result.Values)+1);
Result.Values[Length(Result.Values)-1]:= StrToIntDef(T, 0);
end;
end;
function TVersion.Count: Integer;
begin
Result:= Length(Values);
end;
Now I try to implement this...
var
V1, V2: TVersion;
begin
V1:= '1.2.3.4';
V2:= V1;
ShowMessage(V1);
ShowMessage(V2);
V2.Values[2]:= 8;
ShowMessage(V1);
ShowMessage(V2);
end;
I expect that the V2 should be 1.2.8.4 and V1 to remain 1.2.3.4. However, V1 also changes to 1.2.8.4.
How do I keep these records independent when I assign them?

This behaviour is by design. A dynamic array variable is a pointer. When you assign a dynamic array variable you take a copy of the pointer and increase the reference count. Of course this also happens when you assign compound structures that contain dynamic arrays.
The documentation covers this:
If X and Y are variables of the same dynamic-array type, X := Y points X to the same array as Y. (There is no need to allocate memory for X before performing this operation.) Unlike strings and static arrays, copy-on-write is not employed for dynamic arrays, so they are not automatically copied before they are written to.
To get a mental model for this, this of dynamic arrays as being references in the same way as classes and interfaces. This is in contrast to simple types (integer, double etc.), strings, records, etc.
The standard way to deal with this is as follows:
Make the dynamic array private.
Expose the contents of the array through properties.
In the element setter, make sure that the reference to the dynamic array is unique.
That last step is the tricky part. Make a dynamic array reference unique by calling SetLength:
SetLength(arr, Length(arr));
One of the promises that SetLength makes is that it will always make its first argument be unique.
This has the effect of implementing copy-on-write. To the best of my knowledge it is not possible to implement copy-on-assign because the compiler gives you no hook into the assignment operator.
So the answer to:
How do I keep these records independent when I assign them?
is that you cannot. You need to use copy-on-write instead.

Related

Arrays in Delphi (object Pascal) using variables?

I have these 10 numbers (one in each line) in a Text File and I need them to be sorted in a chronological order starting with the highest number. I wrote a code which works just fine, but the problem is that the code isn't flexible, because once I add another number to the Text File it won't work since the code is set to sort 10 numbers only...this is due to my array of integers which is supposed to read the values before the sorting process starts, but won't allow me to add a variable to the array's properties so it will be able to read and sort any-size text file...I know there has to be a way of making a program which can sort any-size file of this structure, just please tell me how I could improve my code. (If you think my way isn't too efficient, it's because that's my homework from high school and I need to use these arrays to implement a bubblesort).
program Project2;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
numbers, sortednumbers : TextFile;
count : integer=0;
number : array[1..10] of integer;
I : integer;
Procedure Swap(var X, Y : Integer);
var
Temp : integer;
begin
Temp := X;
X := Y;
Y := Temp;
end;
procedure Assign;
var I : Integer;
begin
reset(numbers);
for I := 1 to count do ReadLn(numbers, number[I]);
end;
procedure BubbleSort;
var I, J : integer;
begin
for I := 2 to count do
begin
for J := count downto I do
if (number[J] > number[J - 1]) then
Swap(number[J - 1], number[J]);
end;
end;
begin
AssignFile(numbers, 'Numbers.txt');
AssignFile(sortednumbers, 'Sorted-Numbers.txt');
Reset(numbers);
While not EoF(numbers) do
begin
ReadLn(numbers);
Inc(count);
end;
Reset(numbers);
ReWrite(sortednumbers);
Assign;
BubbleSort;
For I := 1 to count do writeln(sortednumbers, number[I]);
CloseFile(numbers);
CloseFile(sortednumbers);
end.
Use a dynamic array. This is an array that can change the number of elements it holds.
Instead of declaring:
number : array[1..10] of integer;
instead skip the bounds declaration:
Number : array of integer;
Then, before you start using it, set its length:
SetLength(Number, 10);
Once you are done, free the memory by setting it to have length 0:
SetLength(Number, 0);
Note that:
Indexes of a dynamic array start from 0, not 1. That is, Number[0] is the first element and Number[9] is the tenth. This is quite common in programming but can be confusing if it's not something you've come across before.
There are other 'quality' changes you can make to your code too. As commenter David Heffernan said, stop using global variables. I would also suggest using try/finally for resource allocation and cleanup, and using streams (and another link, wrap this over TFileStream) instead of the old-style file IO you're currently doing. Also, consider your variable names - Number for an array of numbers is odd - why not add the S and call it Numbers, for example?
That might be a bit much all at once, so go slowly in small steps, save and backup often (preferably into source control), and have fun!
And if it had not been a school project:
function Comparer(List: TStringList; index1, index2: integer): integer;
begin
try
Result:= StrToInt(List[index1]) - StrToInt(List[Index2]);
except
raise Exception.Create('Your file does not contain numbers');
end;
end;
function SortNumbers
var
lStringList: TStringlist;
begin
lStringList := TStringlist.create;
try
lStringList.LoadFromFile('Numbers.txt');
lStringList.CustomSort(Comparer);
lStringList.SaveToFile('Sorted-Numbers.txt');
finally
lStringList.Free;
end;
end;
However the Delphi RTL uses Quicksort, not Bubblesort.

"Copy" does not create independent copy of Dynamic Array

With reference to on-line documentation found at http://docwiki.embarcadero.com/RADStudio/XE6/en/Structured_Types#Dynamic_Arrays.
It is quite clearly written that to make an independent copy of a dynamic array, use the Copy() function. Example code found at that link also illustrates that after the copy, if you change one array, it is not reflected into the other array as both are independent copies. It does not work for me, however. After the copy as well, if I change one array, the other array automatically changes to receive the same value thus implying that Copy() just did what X := Y would have done. Per documentation, "X := Y" and "X := Copy(Y)" are NOT same.
Here is my code:
type
TTestArray = array of TMyType; //Note: TMyType is a class and not a record.
var
X, Y, Z: TTestArray;
begin
SetLength(X, 1); //create first array
X[0].testString := 'Test Y';
Y := copy(X);
X[0].testString := 'Test Z'; //re-assign another value
Z := copy(X);
X[0].testString := 'Test X';
at this time, testString field should contain different text. So,
X[0].testString should be 'Test X'
Y[0].testString should be 'Test Y'
Z[0].testString should be 'Test Z'
However, all three just have 'Test X' as the value in testString thus implying that Copy() did not create independent copies of the array X. Rather, all three arrays are pointing to the same memory location.
Any way to reliably create independent copies of dynamic arrays (i.e. accomplish what I am trying to do above)?
NOTE (added later): TMyType is a class and not a record. So, per very helpful comments below, this is an expected behavior in case of a CLASS. So, how would I make independent copy of the X into Y and Z in this case?
NOTE 2: Removed "Bug" from subject line. Sorry, Embarcadero...
Note 3: TMyType has to be a class. I have no control over it. It is being created from a web service definition (WSDL) which exposes some functionality of PeopleSoft.
The problem is that you've defined TMyType as:
type
TMyType = class(SomeObject)
....
public
testString: string;
end;
When you create the initial TestArray, it does not get filled with the objects itself, but only with pointers to these objects.
What's going on...
The array thus looks like this:
Array contents of array contents of the heap
-------------+----------------------+-----------------------
X[0] |-> pointer_to_MyType1 |-> MyType1
X[1] |-> pointer_to_MyType2 |-> MyType2
When you copy the array this is what happens:
Array contents of array contents of the heap
-------------+------------------------------+-----------------------
Y[0] |-> copy_of_pointer_to_MyType1 |-> MyType1
Y[1] |-> copy_of_pointer_to_MyType2 |-> MyType2
What you would like to see...
This is what you expected to happen:
Array contents of array
-------------+-------------------
X[0] or Y[0] |-> (copy of)MyType1
X[0] or Y[0] |-> (copy of)MyType2
How to make that happen
However in order for that to be possible you have to define TMyType as:
type
TMyType = record
public
TestString: string;
end;
Now the record itself will be stored in the array and not a pointer to data stored elsewhere.
how would I make independent copy of the X into Y and Z in this case?
If you want to retain the use of classes then you'd have to write your own version of Copy; something like:
uses
System.SysUtils, system.classes, Generics.Collections;
type
TMyList<T: TPersistent, constructor > = class(TList<T>)
public
function CloneArray: TMyList<T>;
end;
implementation
function TMyList<T>.CloneArray: TMyList<T>;
var
i: integer;
temp: T;
begin
Result:= TMyList<T>.Create;
for i:= 0 to SizeOf(self) -1 do begin
temp:= T.Create;
temp.assign(self.items[i]);
Result.Add(temp);
end; {for i}
end;
Obviously the above code assumes that you can get away with using a parameterless constructor.
See also: Correct way to duplicate Delphi object
You should rather create a new question instead of changing or adding to your question/
To answer the second part of your question, the easiest thing would be to create you own CopyMyTypeArray function. Your code for the this would probably be something like this:
function CopyMyTypeArray(ASource: TTestArray): TTestArray;
var
newObject: TMyType;
begin
SetLength(Result, Length(ASource));
for cntr := Low(ASource) to High(ASource) do
begin
// You could put the lines below into a clone method
newObject := TMyType.Create;
newObject.testString := ASource[cntr].testString;
Result[cntr] := newObject;
end;
end;
Please note that you would need to free the objects if you free the dynamic array.
If you are happy with this solution please accept Johan's solution as that answers your initial question.
If you need independent copies of objects, the easiest way is to write a Copy Constructor. A copy constructer takes one argument - a reference to the existing object to be copied - and then initializes its internal fields to the same values as in the original object. This can be done with simple code, or using RTTI.
See also: Correct way to duplicate Delphi object
A configurable TMyTypeFactory (or TMyTypeBuilder) might be also an alternative solution.

Populate multidimensional array

I'm trying populate a multidimensional array on PostgreSQL, but it not work. Below my code:
CREATE OR REPLACE FUNCTION teste()
RETURNS void AS
$BODY$
DECLARE
tarifas NUMERIC[7][24];
a INTEGER;
b INTEGER;
BEGIN
FOR a IN 0..6 LOOP
RAISE NOTICE 'TESTE TESTE %', a;
FOR b IN 0..23 LOOP
RAISE NOTICE 'tarifas[%][%] = 0;', a, b;
tarifas[a][b] = 0;
END LOOP;
END LOOP;
END
$BODY$
LANGUAGE plpgsql VOLATILE;
Postgres has a dedicated function for that purpose exactly: array_fill():
returns an array initialized with supplied value and dimensions,
optionally with lower bounds other than 1
Use it:
CREATE OR REPLACE FUNCTION teste()
RETURNS void AS
$func$
DECLARE
tarifas numeric[7][24] := array_fill(0, ARRAY[7,24]);
a int;
b int;
BEGIN
-- do something
END
$func$ LANGUAGE plpgsql;
Notes
Array dimensions in numeric[7][24] are just documentation. The manual:
The current implementation does not enforce the declared number of
dimensions either. Arrays of a particular element type are all
considered to be of the same type, regardless of size or number of
dimensions. So, declaring the array size or number of dimensions in
CREATE TABLE is simply documentation; it does not affect run-time behavior.
About the assignment operator in plpgsql: := or =:
The forgotten assignment operator "=" and the commonplace ":="
It's generally not possible to write to an array element directly. You can concatenate or append / prepend elements. Or assign an array as a whole. Details in the manual. But a statement like this is not possible:
tarifas[%][%] = 0
Default lower bound of an array is 1, not 0. But you can define arbitrary array dimension. Example:
SELECT '[2:3][2:4]={{7,7,7},{7,7,7}}'::int[]

Is any way to add 2 arrays into one?

Is there any simple univesal way to add 2 arrays into one? In the case below it is not possible simply use C := A + B statement...
I would like to avoid making algorhytm for it everytime .
TPerson = record
Birthday: Tdate;
Name, Surname:string;
end;
Tpeople = array of TPerson;
var A, B, C:Tpeople;
C:=A+B; // it is not possible
thanx
Due to the two string fields in each TPerson record, you can't just use binary "move", since you'll mess the reference counting of string - especially in a multi-threaded environment.
You can do it manually - this is fast and nice:
TPerson = record
Birthday: TDate;
Name, Surname: string;
end;
TPeople = array of TPerson;
var A, B, C: TPeople;
// do C:=A+B
procedure Sum(const A,B: TPeople; var C: TPeople);
begin
var i, nA,nB: integer;
begin
nA := length(A);
nB := length(B);
SetLength(C,nA+nB);
for i := 0 to nA-1 do
C[i] := A[i];
for i := 0 to nB-1 do
C[i+nA] := B[i];
end;
Or you can use our TDynArray wrapper, which has a method for handling such cases:
procedure AddToArray(var A: TPeople; const B: TPeople);
var DA: TDynArray;
begin
DA.Init(TypeInfo(TPeople),A);
DA.AddArray(B); // A := A+B
end;
The AddArray method can add a sub-port of the original array:
/// add elements from a given dynamic array
// - the supplied source DynArray MUST be of the same exact type as the
// current used for this TDynArray
// - you can specify the start index and the number of items to take from
// the source dynamic array (leave as -1 to add till the end)
procedure AddArray(const DynArray; aStartIndex: integer=0; aCount: integer=-1);
Note that with such records, it will use the System._CopyRecord RTL function, which is not so optimized for speed. I've written a faster version - see this blog article or this forum thread.
If you use dynamic arrays in functions/procedures, don't forget to use explicitly const or var parameters (as I coded above), otherwise it will make a temporary copy at each call, therefore it may be slow.
There is nothing built in that allows dynamic arrays to be concatenated.
You may consider using one of the generic container classes found in Generics.Collections, TList.
In your case you would have 3 instances of TList, say A, B and C. Then you could write
A.Clear;
A.AddRange(B);
A.AddRange(C);
I think this is as close as you can get to what you want with what is delivered out of the box.
If you are prepared to do a bit of coding yourself then you could make use of operator overloading to use the exact syntax you requires. Declare a record containing an array of TPerson with private visibility. You then need to implement an Add operator, a Count property and a default Items[] property. This could be made generic too so you only need write it once.
TTurboArray = record<T>
private
FItems: array of T;
//property accessors here
public
class operator Add(a, b: TTurboArray<T>): TTurboArray<T>;
property Count: Integer read GetCount write SetCount;
property Items[Index: Integer]: T read GetItem write SetItem; default;
end;
This idea can be extended into a very powerful data structure as you see fit.
There is a quick-and-dirty way to do this. It is a terrible hack, but it should work and even take care of reference counting:
function ConcatPeople(const A, B: TPeople): TPeople;
var
Temp: TPeople;
ALen, BLen: Integer;
begin
Result := Copy(A);
BLen := Length(B);
if BLen = 0 then
Exit;
ALen := Length(A);
Temp := Copy(B);
SetLength(Result, ALen + BLen);
Move(Temp[0], Result[ALen], BLen * SizeOf(B[0]));
FillChar(Temp[0], BLen * SizeOf(B[0]), 0);
end;
In effect, the data in Temp are "swapped" with the empty records in Result, so the balance is maintained and refcounting will keep on working.
Update
For what it is worth: This is aparently the same technique as used in this accepted SO answer and in, e.g. TList<T>.Insert. I had deleted this answer, but I still think it is valid, so I undeleted it again. It could do with a lock around the Move/FillChar block, so no one accesses the items when they are being "swapped". I'll add that.
Here's how I handled it, though it required a slight (but hopefully immaterial to you) modification to your original code to use TArray:
(tested in XE2)
uses
Generics.Collections;
type
TArrayExt = class(TArray)
class function Concat<T>(const First, Second: array of T): TArray<T>; overload;
end;
class function TArrayExt.Concat<T>(const First, Second: array of T): TArray<T>;
var
i: Integer;
l: Integer;
begin
l := Length(First);
SetLength(Result, l + Length(Second));
for i := Low(First) to High(First) do
Result[i] := First[i];
for i := Low(Second) to High(Second) do
Result[l + i] := Second[i];
end;
type
TPerson = record
Birthday: TDate;
Name, Surname: String;
end;
TPeople = TArray<TPerson>;
var
A, B, C: TPeople;
begin
C := TArrayExt.Concat<TPerson>(A, B);
The main difference here is that I use "TArray" rather than "array of TPerson". This can be used for arrays strings, records, etc. I find the main benefit of doing it this way is that it's truly making a copy rather than a move. And I am using the "normal" Delphi functions instead of things like bulk memory copies, which can give me the willies.
Of course, if you were doing this in a tight loop and needed the performance, this way might not be best for you. But I think this is the best for most other situations, especially in terms of maintenance and readability.
(Unless someone posts a comment about how there's some horrible hidden memory leak here. Hopefully not!)
You code works fine in the newest version of delphi C := A+B;.
But for dynamic arrays in older versions you can use the function concat. Example:
C := Concat(A, B);

Array begin from 0 or 1 in Delphi 5.0 Pascal?

I want to do an ArrayList in Delphi 5.0. So I found a solution doing this code:
var arr: array of String;
OK, but every time I add something I do this:
var
Form1: TForm1;
var arr : array of String;
procedure TForm1.Button1Click(Sender: TObject);
var aux :string;
var len:integer;
begin
len := Length(arr) + 1;
SetLength(arr, len);
arr[len-1] := 'abc' + IntToStr(len);
Button1.Caption := arr[len-1]; // just to writeout something
end;
I'm a C++ programmer, and I do not know anything about Pascal. I always heard a Pascal index begins from 1, not 0. As in the above procedure I do arr[len-1] because of 0 index begin.
Is there a better way than Pascal arrays? Like with C++'s std::vector?
Dynamic arrays' indexes begin with zero
var
a: array of Integer;
begin
SetLength(a, 500);
a[0] := 0;
Static arrays can have arbitrary indexes
var
i: Integer;
b: array [50..100] of Integer;
c: array[-10..10] of Integer;
begin
for i := 50 to 100 do b[i] := i * i;
// Note negative starting index above in declaration
for i := -10 to 10 do c[i] := i * i;
Strings' indexes begin with one
var
c: String;
begin
c := 'Zap!';
c[1] := 'W';
ShowMessage(c); /// shows 'Wap!'
Anyway you can always use Low() and High() functions which return the lower and higher index of an array.
For handling a list of strings the most commonly used class is TStringList which is found in unit Classes.
What you're using is known as a dynamic array which is different from a Pascal classic array. Dynamic arrays are variable in size and the index is 0 based.
Classic Pascal arrays are not 0 nor 1 based... It's up to the programmer where the index start or ends. The only compiler restriction is that the index must be an ordinal type. You can declare
procedure x;
var
IntArr: array[50..75] of Integer;
StrArr: array[0..49] of string;
DblArr: array[1..10] of Double;
Delphi Pascal also has a nice feature that helps iterating through an array of any dimension:
Simply use for i:= Low(Array) to High(Array) do....
which is completely transparent to starting offset i.e. 0,1 or 5 or whatever.
I tried to edit the above answer to improve it but the editor keeps rejecting my posting. Arrays can have negative indexes.
var
A:array[-20..9] of integer;
B:array[-30..-10] of integer;
These are both the same, an array of 20 integers but will not be treated the same by the compiler because the index range is different. Allows you to make the data fit the problem domain, not the other way around.
Now, a string like var S:string[200]; is technically equivalent to var s:packed array[0..200] of char where byte 0 is the length except when you use a string with no length or the specified length is greater than 255, then the string is 1 to whatever current size it is. Because strings can be dynamic it's not good to depend on the 0th element to contain length.
When you use SetLength(array, length) it is worth mentioning that it has indexes starting from 0 as mentioned up to length-1. Also in pascal index on array can be character from ANSI table. So you can define array like a:array['A'..'Z'] of integer. This comes in handy when you need to count all characters in your Strings or Char Array.

Resources