How to slice a dynamic array? - delphi

How can I slice a dynamic array to multiple sub arrays? Slice() function in Delphi does not support dynamic arrays. So how can it be done? A generic solution would be welcome.
program Project10;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
var
A: Array of Integer;
begin
SetLength(A, 4);
Slice(A, 2); // [dcc32 Error] Project10.dpr(15): E2193 Slice standard function only allowed as open array argument
end.

Use Copy(A, 0, 2) instead of Slice(A, 2).
The point is that either you need an "open array parameter" (in which case you need Slice) or you need a regular array, in which case Copy will provide a good solution.

It is possible to use pointers in order to access a Dynamic Array from different "start points"
Consider the code:
TYPE
tListArray = ARRAY[0..0] OF CARDINAL; {Or any other type you need}
tDynamicCardinalArray = ARRAY OF CARDINAL;
VAR
MyList : tDynamicCardinalArray;
pArrPos1, pArrPos2 : ^tListArray;
BEGIN
SetLength(MyList,100);
pArrPos1 := #MyList[0];
pArrPos2 := #MyList[50];
...
...
END;
The benefit is that you have direct access, no copying or moving of data involved.
The drawback is that functions such as "high", "low" and "length" CAN NOT be used on these array-pointers. Or at least not if you want a result to use.
You have to make sure that you never go beyond the scope of the SourceArray MyList when you address the array-pointers.
The benefit is that it brings versatility to Dynamic Arrays.
As we know, a Dynamic Array can only be created from 0 .. and up to the size.
pArrPos2 on the other side has effectively rendered the Dynamic Array into an Array-Pointer which also accept negative addressing:
pArrPos2^[-50] := 0; // Equals: MyList[ 0] := 0
pArrPos2^[ 0] := 50; // Equals: MyList[50] := 50
pArrPos2^[ 49] := 99; // Equals: MyList[99] := 99
Using pointers, you can "slice" a Dynamic Array into several pieces, but you have to keep track of the "High", "Low", and "Length" separately, at least if you want a fully dynamic solution that can be changed on the fly in software.
The needed extra information can be passed as an extra parameter to a procedure or function and takes less CPU-time than actually copying or moving a dataset into a new array.
I know. the post was old, but I still think my answer would/could be relevant here, in particular for new readers.

Related

Using constants to access Delphi Array

I'm working on a vintage code base and I am very new to delphi, so apologies in advance if my syntax is off a bit (or just plain wrong).
I have things like a pre defined (at compile time) array. arr and we used all over the place
file1.pas: arr[1] := 3.14
file2.pas: pi := arr[1]
And I've noticed that we never,ever use constants to access elements in the array. In C I would write:
int arr[100] = {0};
const int MY_INDEX=1;
arr[MY_INDEX] = 3.14
etc. But I don't see that in my delphi code base. Is that because Delphi/Pascal doesn't support it or because the original authors decided not to do it?
Thanks
Delphi allows you to use a constant as an array index. So if you don't see such a thing in your code, it's because the authors elected not to declare and use constants for array indices.
Yes constants can be used along with any expression that evaluates to an appropriate and valid index for the array. You should also note that an array in Delphi could be declared with a non-zero based index range:
var
MonthlyTotals: array[1..12] of Integer; // Jan = 1, Feb = 2 etc etc
You can even specify the index of an array as an enum type and use enum members for the indices which provides even tighter safety (where possible and appropriate), as per this contrived example:
type
TFileFormat = (ffXML, ffCSV, ffText, ffJSON);
var
sExtensions: array[TFileFormat] of String;
sExtensions[ffXML] := 'xml';
sExtensions[ffCSV] := 'csv';
sExtensions[ffText] := 'txt';
sExtensions[ffJSON] := 'json';
In such cases the array might only have members for certain (contiguous) values in the enum:
var
sExtensions: array[ffXML..ffCSV] of String;
For this reason, and the fact that array indices may not be zero based, unless you are 110% certain of the index range of an array it is a good idea to always use Low() and High() to determine the index bounds when iterating over the contents of an array and not assume the index basis:
// This will not work properly:
for i := 0 to 11 do
MonthlyTotals[i] := ....
// Neither will this, even though it looks more safe
for i := 0 to Pred(Length(MonthlyTotals)) do
MonthlyTotals[i] := ....
// This will be safe:
for i := Low(MonthlyTotals) to High(MonthlyTotals) do
MonthlyTotals[i] := ....
// And it works for enum indices as well:
for ext := Low(sExtensions) to High(sExtensions) do
sExtensions[ext] := ....
The exact conversion of your C code in Delphi would be along the lines of:
var
arr: array[100] of integer;
const
MY_INDEX = 1;
begin
arr[MY_INDEX] := 3.14;
end;
As others have said, perfectly feasible and the enum method is arguably a better alternative. One gotcha here is that this is relying on Delphi having the array initialised zeroed rather than setting it explicitly as in C.

What is the canonical way to write a hasher function for TEqualityComparer.Construct?

Consider the following record:
TMyRecord = record
b: Boolean;
// 3 bytes of padding in here with default record alignment settings
i: Integer;
end;
I wish to implement IEqualityComparer<TMyRecord>. In order to do so I want to call TEqualityComparer<TMyRecord>.Construct. This needs to be supplied with a TEqualityComparison<TMyRecord> which presents no problems to me.
However, Construct also requires a THasher<TMyRecord> and I would like to know the canonical method for implementing that. The function needs to have the following form:
function MyRecordHasher(const Value: TMyRecord): Integer;
begin
Result := ???
end;
I expect that I need to call BobJenkinsHash on both fields of the record value and then combine them some how. Is this the right approach, and how should I combine them?
The reason I don't use TEqualityComparison<TMyRecord>.Default is that it uses CompareMem and so will be incorrect due to the record's padding.
The Effective Java (by Joshua Bloch) section about overriding hashCode could be useful. It shows how the individual parts of the object (or record) can be combined to efficiently construct a hashCode.
A good hash function tends to produce unequal hash codes for unequal
objects. This is exactly what is meant by the third provision of the
hashCode contract. Ideally, a hash function should distribute any
reasonable collection of unequal instances uniformly across all
possible hash values. Achieving this ideal can be extremely difficult.
Luckily it is not too difficult to achieve a fair approximation. Here
is a simple recipe:
Store some constant nonzero value, say 17, in an int variable called result.
For each significant field f in your object (each field taken into account by the equals method, that is), do the following:
a. Compute an int hash code c for the field: ..... details omitted ....
b. Combine the hash code c computed in step a into
result as follows: result = 37*result + c;
Return result.
When you are done writing the hashCode method, ask yourself whether equal instances have equal hash codes. If not, figure out why
and fix the problem.
This can be translated into Delphi code as follows:
{$IFOPT Q+}
{$DEFINE OverflowChecksEnabled}
{$Q-}
{$ENDIF}
function CombinedHash(const Values: array of Integer): Integer;
var
Value: Integer;
begin
Result := 17;
for Value in Values do begin
Result := Result*37 + Value;
end;
end;
{$IFDEF OverflowChecksEnabled}
{$Q+}
{$ENDIF}
This then allows the implementation of MyRecordHasher:
function MyRecordHasher(const Value: TMyRecord): Integer;
begin
Result := CombinedHash([IfThen(Value.b, 0, 1), Value.i]);
end;

Move() to Insert/Delete item(s) from a dynamic array of string

Using System.Move() to insert/delete item(s) from an array of string is not as easy as insert/delete it from other array of simple data types. The problem is ... string is reference counted in Delphi. Using Move() on reference-counted data types needs deeper knowledge on internal compiler behaviour.
Can someone here explain the needed steps for me to achieve that, or better with some snippet codes, or direct me to a good reference on the internet?
Oh, Please don't tell me to use the "lazy-but-slow way", that is, for loop, I know that.
I've demonstrated how to delete items from a dynamic array before:
Delphi Q&A: How do I delete an element from an array?
In that article, I start with the following code:
type
TXArray = array of X;
procedure DeleteX(var A: TXArray; const Index: Cardinal);
var
ALength: Cardinal;
i: Cardinal;
begin
ALength := Length(A);
Assert(ALength > 0);
Assert(Index < ALength);
for i := Index + 1 to ALength - 1 do
A[i - 1] := A[i];
SetLength(A, ALength - 1);
end;
You cannot go wrong with that code. Use whatever value for X you want; in your case, replace it with string. If you want to get fancier and use Move, then there's way to do that, too.
procedure DeleteX(var A: TXArray; const Index: Cardinal);
var
ALength: Cardinal;
TailElements: Cardinal;
begin
ALength := Length(A);
Assert(ALength > 0);
Assert(Index < ALength);
Finalize(A[Index]);
TailElements := ALength - Index;
if TailElements > 0 then
Move(A[Index + 1], A[Index], SizeOf(X) * TailElements);
Initialize(A[ALength - 1]);
SetLength(A, ALength - 1);
end;
Since X is string, the Finalize call is equivalent to assigning the empty string to that array element. I use Finalize in this code, though, because it will work for all array-element types, even types that include records, interfaces, strings, and other arrays.
For inserting, you just shift things the opposite direction:
procedure InsertX(var A: TXArray; const Index: Cardinal; const Value: X);
var
ALength: Cardinal;
TailElements: Cardinal;
begin
ALength := Length(A);
Assert(Index <= ALength);
SetLength(A, ALength + 1);
Finalize(A[ALength]);
TailElements := ALength - Index;
if TailElements > 0 then begin
Move(A[Index], A[Index + 1], SizeOf(X) * TailElements);
Initialize(A[Index]);
A[Index] := Value;
end;
Use Finalize when you're about to do something that's outside the bounds of the language, such as using the non-type-safe Move procedure to overwrite a variable of a compiler-managed type. Use Initialize when you're re-entering the defined part of the language. (The language defines what happens when an array grows or shrinks with SetLength, but it doesn't define how to copy or delete strings without using a string-assignment statement.)
You don't state if it is important for you to keep the array elements in the same order or not.
If the order is not relevant, you can so something really really fast like this:
procedure RemoveRecord(Index: integer);
begin
FRecords[Index]:= FRecords[High(FRecords)]; { Copy the last element over the 'deleted' element }
SetLength(FRecords, Length(FRecords)-1); { Cut the last element }
end;
{ I haven't tested the code to see it compiles, but you got the idea anyway... }
Sorting the list
If you have a HUGE list that needs to be modified by the user, you can use methods similar to the one above (break the list order). When the user its done editing (after multiple deletes), you present it with a button called "Sort list". Now he can do the lengthy (sort) operation.
Of course, I assume above that your list can be sorted by a certain parameter.
Sorting the list automatically
An alternative is to automate the sorting process. When the user deleted stuff from the list, start a timer. Keep resetting the timer if the user keeps deleting items. When the timer manages to trigger an event, do the sorting, stop the timer.
To insert a string, simply add a string (the lazy way) to the end of the array (which is an array of pointers), and then use Move to change the order of the elements of this array (of pointers).
If I wanted to insert a string into the middle of a list of strings, I'd use TStringList.Insert. (It does it quickly using System.Move.)
Any particular reason why you're using an array instead of a TStringList?
Call UniqueString() on it, before messing with it.
http://docwiki.embarcadero.com/VCL/en/System.UniqueString
Then you have a string with a single reference.
Fat chance that that is what delete and insert do too, and I doubt you'll be faster.
Just wanting to add this for any people that come here in the future.
Modifying Rob's code, I came up with this way of doing it that uses the newer TArray<T> type constructions.
type
TArrayExt = class(TArray)
class procedure Delete<T>(var A: TArray<T>; const Index: Cardinal; Count: Cardinal = 1);
end;
implementation
class procedure TArrayExt.Delete<T>(var A: TArray<T>; const Index: Cardinal;
Count: Cardinal = 1);
var
ALength: Cardinal;
i: Cardinal;
begin
ALength := Length(A);
Assert(ALength > 0);
Assert(Count > 0);
Assert(Count <= ALength - Index);
Assert(Index < ALength);
for i := Index + Count to ALength - 1 do
A[i - Count] := A[i];
SetLength(A, ALength - Count);
end;
A similar thing can be done for the insert.
(Not looking for this to get marked as the answer, just looking to provide an example that was too long to fit in the comments on Rob's excellent answer.)
(Fixed to address Rob's comments below.)
Move() works fine with reference counted types like strings or interfaces, and actually used internally in Delphi's arrays and lists. But, now, in general case, Move() is no longer valid because of managed records feature.
If you use System.Move to put items into an array of string, you should be aware that the strings that where there before the Move (and now overwritten), had a reference count of either -1 for constant strings, or > 0 for variable strings. Constant strings should not be altered, but variable strings should be treated accordingly: You should manually lower their reference-count (before they're overwritten!). To do that, you should try something like this:
Dec(PStrRec(IntPtr(SomeString)-12).refCnt);
But if the reference-count reached zero, you should also finalize the associated memory - something Delphi itself does a whole lot better if you let it work it's compiler-magic for strings. Oh, and also : if the strings you're copying come from the same array as your writing into, the needed administration becomes very cumbersome, very quickly!
So if it's in some way possible to avoid all this manual housekeeping, I would advise to let Delphi handle it itself.

Defining types from other units in Delphi

Var
A : Array [1..4] of Integer;
B : Array [1..4] of Integer;
Begin
A := B;
Won't work as loren-pechtel said here
the problem is A and B for me are in different units. So, is there a way to define a type definition from a existing one in another class?
Define type in interface block of some unit and then include that unit via uses clause in other units where you need that type.
unit A;
interface
type
TMyArray = array [1..4] of Integer;
...
When you need to use TMyArray in another unit:
unit B;
interface
uses A;
...
var x : TMyArray;
Alternatively, define your type in the interface section of unit C and use this unit in both A and B.
Array types in Delphi are a bit odd. It looks like A and B are of exactly the same type, but Delphi doesn't consider that they are the same. "Array [1..4] of Integer" appears twice, so Delphi thinks there are two different types. That's just an oddity of Delphi. Most other languages, I think, wouldn't care. It's not a problem in practice; it's just a bit strange. Maybe there is a good reason for it. Who knows. The solution, as the others have said, is define your own type, which you can put in a unit that can be used by other units. I just mention this issue of array types because it may be confusing you.
Another approach, a bit old school but still works, is to use the ABSOLUTE keyword to force the memory of one to overlay another, and make the other type compatible. For example, in unit a lets say you have the following:
TYPE
TArrayA = Array[1..4] of integer;
then in unit b, you have the following:
TYPE
TArrayB = Array[1..4] of integer;
For compatibility, you can do the following:
VAR
InstanceA : TArrayA;
InstanceB : TArrayB;
InstanceBasA : TArrayA ABSOLUTE InstanceB;
What this does is create a variable "InstanceBasA" of type ArrayA which overlays the same memory space as the variable "InstanceB". This allows you to do the following command:
InstanceA := InstanceBasA;
One more method of moving data from variablea to variableb is to use the MOVE command. For example, to move from ArrayA to ArrayB you can do the following:
var
ArrayA : array[1..4] of Integer;
ArrayB : Array[1..4] of Integer;
begin
FillChar(ArrayB[1],SizeOf(ArrayB),#0);
ArrayA[1] := 1234;
ArrayA[2] := 3456;
ArrayA[3] := 7890;
ArrayA[4] := 9876;
// This is where the move from ArrayA to ArrayB happens.
Move( ArrayA[1], ArrayB[1], SizeOf(ArrayA) );
Assert( ArrayA[4] = ArrayB[4], 'ArrayA[4] <> ArrayB[4]');
end;
This works by the fact that the array is layed out in a linear fashion, so your copying the bytes starting at the first array position, for the length of the array.
You can force the compiler to assume they are of the same type by typecasting them:
type
TIntArray = array[1..4] of integer;
begin
Assert(SizeOf(ArrayA) = SizeOf(TIntArray));
Assert(SizeOf(ArrayB) = SizeOf(TIntArray));
TIntArray(ArrayA) := TIntArray(ArrayB);
But you should make sure that both actually are array[1..4] of otherwise you will overwrite some memory. That's why I added the two assertions.
Just use UnitA in UnitB after inteface and before the implementation ...!!!!
Never, never, NEVER use code like this:
// This is where the move from ArrayA to ArrayB happens.
Move( ArrayA[1], ArrayB[1], SizeOf(ArrayA) );
Where's the flaw? You're using the SOURCE size to get the number of bytes to move, not the DESTINATION size!!! If SizeOf(A) > SizeOf(B) you have a buffer overflow and you're overwriting memory. If you're luck you get an AV, if you're not you have an exploitable vulnerability. It's far better to always use the destination size, although this way you can end up reading memory you shouldn't if Size(B) > Size(A), maybe exposing unwanted data.
Anyway, always check structure boundaries when moving data - some companies banned operations like this (i.e. memcpy() ) from their code.

How do I declare an array when I don't know the length until run time?

I originally had an array[1..1000] that was defined as a global variable.
But now I need that to be n, not 1000 and I don't find out n until later.
I know what n is before I fill the array up but I need it to be global therefore need a way to define the size of a global array at run time.
Context is filling an array with a linear transformation of the bytes in a file.
I don't know how big the file is until someone wants to open it and the files can be of any size.
As of Delphi 4, Delphi supports dynamic arrays. You can modify their sizes at run time and they will retain the data you stored in other elements at the old size. They can hold elements of any homogeneous type, including records and other arrays. You can declare a dynamic array the same as you declare normal, "static" arrays, but simply omit the array bounds:
var
ArthurArray: array of TForm;
Although static arrays allow you to specify both the lower and upper bound, the low index of a dynamic array is always zero. The high index is given by the High function, which always returns one less than the length of the array. For any dynamic array x, High(x) = Length(x)-1.
A global variable can be accessed by any code, including local procedures.
A global variable of dynamic-array type will be initialized to be an empty array. Its length will be zero and High called on that array will be -1. Low on that array will still return zero.
At any time, you may resize a dynamic array. Use the SetLength function, just as you can do with strings:
var
NumElements: Integer;
begin
NumElements := GetNumberOfArthurForms();
SetLength(ArthurArray, NumElements);
end;
If you have a multidimensional array, you can set their lengths in a loop:
var
matrix: array of array of Double;
i: Integer;
begin
SetLength(matrix, height);
for i := 0 to height - 1 do
SetLength(matrix[i], width);
end;
There's a shortcut for that to set the lengths of all the inner arrays at once:
begin
SetLength(matrix, height, width);
end;
Like I mentioned, dynamic arrays keep their old values when you resize them:
var
data: array of string;
begin
SetLength(data, 2);
data[1] := 'foo';
SetLength(data, 20);
Assert(data[1] = 'foo');
end;
But if you shorten the array, any elements that resided beyond the new last element are gone forever:
begin
SetLength(data, 20);
data[15] := 'foo';
SetLength(data, 2);
// data[15] does not exist anymore.
SetLength(data, 16);
writeln(data[15); // Should print an *empty* line.
end;
My demonstrations above used strings. Strings are special in Delphi; they're managed by the compiler through reference counts. Because of that, new dynamic-array elements of type string are initialized to be empty. But if I had used integers instead, there would be no guarantee of the values of new elements. They might be zero, but they might be anything else, too, just like the initial values of standalone local variables.
The Delphi 7 help files are very good, I'm told. Please read more about dynamic arrays there. You can find demonstrations of their use throughout the VCL and RTL source code provided in your Delphi installation, as well as in nearly any Delphi code example produced in the last 10 years.
First, here's a general answer to the first part of your question:
If your array is no longer static, you might want to consider using a TList, a TStringList or one of the many container classes in the Contnrs unit.
They may better represent what you are doing, provide additional capabilities you might need, e.g. sorting or name/value pairs, they dynamically grow as you need them, and have been very well optimized.
Then you said:
"Context is filling an array with a linear transformation of the bytes in a file. I don't know how big the file is until someone wants to open it and the files can be of any size."
For your specific problem, I would load the bytes in a file using:
MyFileStream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
Size := MyFileStream.Size - MyFileStream.Position;
SetLength(Buffer, Size);
MyFileStream.Read(Buffer[0], Size);
Then you can easily use a PChar pointer to go through each character or even each byte in the Buffer one by one and transform them the way you need to.

Resources