E.g. I have this array:
type
OptionRange = array[ 1..9 ] of integer;
How do I check if array[x] exists?
Actually, I want to limit user input with the array index. Am I doing the wrong thing? Is there a better practical solution?
In Free Pascal and the Borland dialects (and perhaps elsewhere as well), you can use the Low and High functions on the array type or a variable of the array type. I see this used most often to determine the bounds for for loops:
var
range: OptionRange;
i: Integer;
begin
for i := Low(range) to High(range) do begin
range[i] := GetOptionRangeElement(i);
end;
end;
You can also define a subrange type and then use it to define both the array and the index variables you use on the array:
type
OptionRangeIndex = 1..9;
OptionRange = array[OptionRangeIndex] of Integer;
var
range: OptionRange;
i: OptionRangeIndex;
Then, when you have range checking enabled (assuming your compiler offers such a feature) and you use a value that's outside the range for OptionRange indices, you'll get a run-time error that you can catch and handle however you want.
I'm not really sure what an option range is or why an array of nine integers would be used to represent one, but I figure that's a name-selection issue.
Related
How can we implement a function to average all elements of a dynamic multidimensional array in Delphi 6? Such as:
Function Average(arr:any_array):extended;
Where arr is a dynamic array and may have 1 or more dimensions.
Linked question: how can we linearize a multidimension array? Do you have any example?
I ask this because I have many different arrays which I must average, if possible, with the one same function.
If you have one dimensional array you can simply use Mean function located in Math unit.
But for multidimensional array I'm afraid there is no already available function. The main reason is that Delphi treats multidimensional arrays in a rather specific way.
While in many other programming languages multidimensional array are in fact just one dimensional array with modified mechanism for accessing array elements which works something like this:
Actual Array Item Index = First parameter + (Second parameter * Size of first dimension)
In Delphi multidimensional arrays are represented as multiple one dimensional array referenced by another array like so:
Array of One Dimensional Array
EDIT 2: As mentioned by David in his comment these type of arrays are also known as Jagged arrays or Ragged arrays.
Why is this so different? Why is it so important? Because those One Dimensional Arrays that from multidimensional array in Delphi don't need to be of the same size. So data in your multidimensional array can actually look like this (where each X represents one array item):
XXXX
XXXXXXX
XXXX
XXXXX
So I'm afraid you will have to write your own function for this. But that should not be so hard.
All you need is a mechanism to iterate through every item in your arrays (I bet you have this already implemented in your code somewhere) so you can sum up the value of all elements in your arrays. And then you divide that sum with the number of elements in your arrays in order to get the arithmetical average of all items.
EDIT 3: I'm not sure if you could have single code to iterate through all of your dynamic arrays. I mean at least you need to know how many dimensions does your array have. This is required because for iterating through multiple dimensions of an array you actually need to recursively iterate through each separate array that represents separate dimensions.
The code for summing up elements in a 3D dynamic array would look like this:
type
TDyn3Darray = array of array of array of Single;
implementation
function SumOf3DArray(Dyn3DArray: TDyn3DArray): Single;
var X,Y,Z, a: Integer;
ArrItem: Single;
begin
Result := 0;
SetLength(Dyn3Darray,5,8,40);
for X := Low(Dyn3Darray) to High(Dyn3Darray) do
begin
for Y := Low(Dyn3Darray[X]) to High(Dyn3Darray[X]) do
begin
for Z := Low(Dyn3Darray[X,Y]) to High(Dyn3Darray[X,Y]) do
begin
Result := Result + Dyn3Darray[X,Y,Z];
end;
end;
end;
end;
I believe you will be capable to modify the code accordingly for different number of dimensions yourself.
EDIT 1: As for linearizing of multidimensional arrays in which I believe you mean changing the multidimensional array in one dimensional array you would just copy elements of separate dimensions and add them into one dimensional array.
But doing that just in order to get average value of all elements would not be practical because for first you would need double the amount of memory and as second in order to copy all the elements from multiple dimension into one you would probably have to iterate through every element in the first place. And then whatever method would you use for calculating average value would also have to iterate through all those copied elements again and thus just slowing down the process for no good reason.
And if you are interested in replacing multidimensional arrays with one dimensional arrays while still threating its elements as if they are in multidimensional array check my explanation of how some other programming languages treat multidimensional array.
NOTE that this would require you accessing all items of such arrays through special methods and will thus result in large changes of your code where you interact with these arrays. Unless if you would wrap these arrays in a class with default multi indexed property that will allow you accessing the array items. Such approach would require additional code for initialization and finalization of such classes and require a slightly modified approach when resizing of such arrays.
Delphi does not provide easy ways for you to inspect the dimensions of an arbitrary dynamic array. Indeed it provides no means for you to pass around an arbitrary dynamic array. However, in practice, you will encounter one and two dimensional arrays commonly, and seldom anything of higher dimensions. So you need to write only a handful of functions:
type
TDoubleArray1 = array of Double;
TDoubleArray2 = array of TDoubleArray1;
TDoubleArray3 = array of TDoubleArray2;
function Mean(const arr: TDoubleArray1): Double; overload;
function Mean(const arr: TDoubleArray2): Double; overload;
function Mean(const arr: TDoubleArray3): Double; overload;
Implement like this:
function Mean(const arr: TDoubleArray1): Double;
var
i: Integer;
begin
Result := 0.0;
for i := low(arr) to high(arr) do
Result := Result + arr[i];
Result := Result / Length(arr);
end;
function Mean(const arr: TDoubleArray2): Double;
var
i, j, N: Integer;
begin
Result := 0.0;
N := 0;
for i := low(arr) to high(arr) do
for j := low(arr[i]) to high(arr[i]) do
begin
Result := Result + arr[i,j];
inc(N);
end;
Result := Result / N;
end;
function Mean(const arr: TDoubleArray3): Double;
var
i, j, k, N: Integer;
begin
Result := 0.0;
N := 0;
for i := low(arr) to high(arr) do
for j := low(arr[i]) to high(arr[i]) do
for k := low(arr[i,j]) to high(arr[i,j]) do
begin
Result := Result + arr[i,j,k];
inc(N);
end;
Result := Result / N;
end;
It would astound me if you really needed higher dimensions than this, but it is easy to add.
Some points:
I'm using Double rather than Extended for reasons of performance, as the latter has a size of 10 which leads to a lot of mis-aligned memory access. If you cannot bring yourself to avoid Extended you can readily adapt the code above.
You could probably find low-level methods using RTTI to write a single function that iterates over any dynamic array, however, the RTTI would likely impact performance. I personally don't see any point in going that route. Use a handful of functions like this and be done.
I would like to make a procedure that take array of shortstring as argument
procedure f(const a, b: Array of shortstring);
I would like to call this with arrays of known length and shortstrings of known length e.g.
var
A, B: array[1..2] of string[5];
C, D: array[1..40] of string[12];
begin
f(A,B);
f(C,D);
end;
This result in an compiler error E2008 Incompatible types.
Why is that? Can I write a procedure that can take arrays of shortstring (any length of arrays/strings)?
Why use shortstring?
The shortstings are fields in an existing record. There are alot of these record with thousand of shortstrings. In an effort to migrate data from turbo power B-Tree Filer to SQL databases one step is to convert the record to a dataset, and the back to a record, to confirm all fields are converted correctly both directions. I have been using CompareMem on the records to check this, but it does not provide enough information as to which field a conversion error is in. Thus a small program was created, which from the record definition can generate code to compare the two records. It was for this code generator I needed a function to compare shortstrings. It ended up using CompareMem on the shortstrings.
A ShortString is 0 to 255 characters long. The length of a ShortString can change dynamically, but memory is a statically allocated 256 bytes, the first byte stores the length of the string, and the remaining 255 bytes are available for characters, whilist string[5] declared in this way allocate only as much memory as the type requires (5 byte + 1 byte for length).
you could use type
type
MyString = string[5];
...
procedure f(const a, b: Array of MyString);
...
var
A, B: array[1..2] of MyString;
begin
f(A,B);
end;
In a similar situation I've used the following:
type
TOpenArrayOfOpenString = record
strict private
FSizeOfString: Integer;
FpStart: PChar;
FArrayLength: Integer;
function GetItemPtr(AIndex: Integer): PShortString;
public
constructor Init(var AFirstString: Openstring; AArrayLength: Integer);
function Equals(const AArray: TOpenArrayOfOpenString): Boolean;
property SizeOfString: Integer read FSizeOfString;
property pStart: PChar read FpStart;
property ArrayLength: Integer read FArrayLength;
property ItemPtrs[AIndex: Integer]: PShortString read GetItemPtr; default;
end;
{ TOpenArrayOfOpenString }
constructor TOpenArrayOfOpenString.Init(var AFirstString: Openstring; AArrayLength: Integer);
begin
FSizeOfString := SizeOf(AFirstString);
FpStart := #AFirstString[0]; // incl. length byte!
FArrayLength := AArrayLength;
end;
function TOpenArrayOfOpenString.Equals(const AArray: TOpenArrayOfOpenString): Boolean;
begin
Result := CompareMem(pStart, AArray.pStart, SizeOfString * ArrayLength);
end;
function TOpenArrayOfOpenString.GetItemPtr(AIndex: Integer): PShortString;
begin
Result := PShortString(pStart + AIndex * SizeOfString);
end;
You could use it like this:
procedure f(const a: TOpenArrayOfOpenString);
var
i: Integer;
begin
for i := 0 to Pred(a.ArrayLength) do
Writeln(a[i]^);
end;
procedure Test;
var
A: array[1..2] of string[5];
C: array[1..40] of string[12];
begin
f(TOpenArrayOfOpenString.Init(A[1], Length(A)));
f(TOpenArrayOfOpenString.Init(C[1], Length(C)));
end;
It's not as elegant as a solution built into the language could be and it is a bit hacky as it relies on the fact/hope/... that the strings in the array are laid out contiguously. But it worked for me for some time now.
type
shortStrings =array[1..2] of string[5];
...
a,b : shortString;
..
procedure rock(a,b : shortStrings);
..
You are combining two different kinds of open array.
First, there is the classic Turbo Pascal "string" (also called "openstring" in Delphi IIRC) which is essentially string[255]. As string[255] is a superset of all shortstrings, the open array aspect simply converts all shortstring types to it.
The "array of xx" syntax is the Delphi (4+?) open array. It is an open array of any type, not just strings, and the syntax to call it is f(nonarrayparam,[arrayelement0,arrayelement1]);
Somehow you seem to mix both syntaxes, and even aggrevate it by adding CONST which sollicits pass by reference and excludes conversions.
I think you assume shortstring has an performance advantage. It has, in some cases. Open array is not one of those cases. Not even in TP :-)
I am writing a windows application in Lazarus/FreePascal (like Delphi). I have a TDataset object that is populated by 5000 rows, 2 columns of numerical values.
I need to pass this data to a C function that I am importing statically from a .dll library.
Here is an excerpt from the library's manual that explains what format its parameters should be in:
flan_index_t flann_build_index(float* dataset,
int rows,
int cols,
float* speedup,
struct FLANNParameters* flann_params);
This function builds an index and return a reference to it. The
arguments expected by this function are as follows: dataset, rows and
cols - are used to specify the input dataset of points:
dataset is a pointer to a rows cols matrix stored in row-major
order (one feature on each row)
Can I simply pass the TDataSet object? Do I have to do something to it first so that the pointer is in the right form?
Obviously you can't pass the TDataSet object. It is a FreePascal object, and the function seems to expect a pointer to a float (which is probably a pointer to a Single in FreePascal). It probably expects a two-dimensional array of float's. You have to pass another pointer to float and a pointer to a FLANNParameters structure as well.
Move() won't work either. A TDataSet is not an array.
I guess you'll have to declare an array like Uwe said, populate it using your dataset and pass the array:
type
PMyFloatArray = ^TFloatArray;
TMyFloatArray = array[0..4999, 0..1] of Single;
var
MyArray: PMyFloatArray;
idx: flan_index_t;
begin
New(MyArray);
try
// Fill array using your TDataSet...
// set up other parameters...
idx := flann_build_index(MyArray, 5000, 2, &speedup, etc...);
// ...
finally
Dispose(MyArray);
end;
end;
Shameless plug
Please read my Pitfalls of Conversion article about converting function declarations from C to Delphi (and probably FreePascal on Win32). Now I'm at it, you may want to read my article Addressing Pointers too.
No, you can't pass the dataset directly. The naming "dataset" might imply that, but the meaning is totally different. You have to pass a pointer to a float matrix to the function. To implement this you should declare an array[0..4999, 0..1] of float (probably double) and fill it from the dataset.
Using Rudy's solution as a base (Thanks by the way!) I came up with this:
with Datasource1.DataSet do
begin
Open;
First;
field_count := FieldCount;
record_count := RecordCount;
row := 0;
while not EOF do
begin
for col := 0 to field_count - 1 do
MyArray[row, col] := Fields.Fields[col].AsFloat;
row := row + 1; //Shift to next row in array
Next; //Shift to next row in dataset
end;
end;
Seems to work great; and a lot faster than I expected.
I have 2 or more dynamic string array that fill with some huge data , i want to merge this 2 array to one array , i know i can do it with a for loop like this :
var
Arr1, Arr2, MergedArr: Array of string;
I: Integer;
begin
// Arr1:= 5000000 records
// Arr2:= 5000000 records
// Fill MergedArr by Arr1
MergedArr:= Arr1;
// Set length of MergedArr to length of ( Arra1 + Arr2 )+ 2
SetLength(MergedArr, High(Arr1)+ High(Arr2)+2);
// Add Arr2 to MergedArr
for I := Low(Arr2)+1 to High(Arr2)+1 do
MergedArr[High(Arr1)+ i]:= Arr2[i-1];
end;
but it is slow on huge data , is there faster way like copy array memory data ?
First of all string is special, so it should be treated specially: Don't try outsmarting the compiler, keep your code unchanged. String is special because it's reference counted. Every time you copy a string from one place to an other it's reference count is incremented. When the reference count reaches 0, the string is destroyed. Your code plays nice because it lets the compiler know what you're doing, and in turn the compiler gets the chance to properly increment all reference counts.
Sure, you can play all sorts of tricks as suggested in the comments to gabr's answer, like filling the old arrays with zero's so the reference count in the new array remains valid, but you can't do that if you actually need the old arrays as well. And this is a bit of a hack (albeit one that will probably be valid for the foreseeable future). (and to be noted, I actually like this hack).
Anyway, and this is the important part of my answer, your code is most likely not slow in the copying of the strings from one array to the other, it's most likely slowly somewhere else. Here's a short console application that creates two arrays, each with 5M random strings, then merges the two arrays into a third and displays the time it took to create the merge. Merging only takes about 300 milliseconds on my machine. Filling the array takes much longer, but I'm not timing that:
program Project26;
{$APPTYPE CONSOLE}
uses SysUtils, Windows;
var a, b, c: array of string;
i: Integer;
Freq: Int64;
Start, Stop: Int64;
Ticks: Cardinal;
const count = 5000000;
begin
SetLength(a,count);
SetLength(b,count);
for i:=0 to count-1 do
begin
a[i] := IntToStr(Random(1));
b[i] := IntToStr(Random(1));
end;
WriteLn('Moving');
QueryPerformanceFrequency(Freq);
QueryPerformanceCounter(Start);
SetLength(c, Length(a) + Length(b));
for i:=0 to High(a) do
c[i] := a[i];
for i:=0 to High(b) do
c[i+Length(a)] := b[i];
QueryPerformanceCounter(Stop);
WriteLn((Stop - Start) div (Freq div 1000), ' milliseconds');
ReadLn;
end.
You can use built-in Move function which moves a block of memory to another location. Parameters are source and target memory blocks and size of data to be moved.
Because you are copying strings, source arrays must be destroyed after the merging by filling them with zeroes. Otherwise refcounts for strings will be all wrong causing havoc and destruction later in the program.
var
Arr1, Arr2, MergedArr: Array of string;
I: Integer;
begin
SetLength(Arr1, 5000000);
for I := Low(Arr1) to High(Arr1) do
Arr1[I] := IntToStr(I);
SetLength(Arr2, 5000000);
for I := Low(Arr2) to High(Arr2) do
Arr2[I] := IntToStr(I);
// Set length of MergedArr to length of ( Arra1 + Arr2 )+ 2
SetLength(MergedArr, High(Arr1)+ High(Arr2)+2);
// Add Arr1 to MergedArr
Move(Arr1[Low(Arr1)], MergedArr[Low(MergedArr)], Length(Arr1)*SizeOf(Arr1[0]));
// Add Arr2 to MergedArr
Move(Arr2[Low(Arr2)], MergedArr[High(Arr1)+1], Length(Arr2)*SizeOf(Arr2[0]));
// Cleanup Arr1 and Arr2 without touching string refcount.
FillChar(Arr1[Low(Arr1)], Length(Arr1)*SizeOf(Arr1[0]), 0);
FillChar(Arr2[Low(Arr2)], Length(Arr2)*SizeOf(Arr2[0]), 0);
// Test
for I := Low(Arr1) to High(Arr1) do begin
Assert(MergedArr[I] = IntToStr(I));
Assert(MergedArr[I] = MergedArr[Length(Arr1) + I]);
end;
// Clear the array to see if something is wrong with refcounts
for I := Low(MergedArr) to High(MergedArr) do
MergedArr[I] := '';
end;
An excellent maxim is that the fastest code is that which never runs. Since copying is expensive you should look to avoid the cost of copying.
You can do this with a virtual array. Create a class which holds an array of array of string. In your example the outer array would hold two string arrays.
Add a Count property that returns the total number of strings in all of the arrays.
Add a default indexed property that operates by working out which of the outer arrays the index refers to and then returns the appropriate value from the inner array.
For extra points implement an enumerator to make for in work.
Say I have the variable:
Var question : array[1..50] of char;
When I do:
question := 't'; //What is the correct way to change the value?
It returns an error:
Incompatible types: 'array[1..50] of Char' and 'Char'
Note: I want to have a max string size of 50 chars, not 50 different chars.
The reason for this question is that I have a record in another unit(This is just a basic example, not what I actually have written above) In that unit I have a Record, which I can't use the string data type in(Or is there a way? please explain if there is). I just need to know how to give an array of chars a value.
While Delphi strings and arrays of char are related, they are not the same.
Delphi overloads assignment of strings and literals (char and string) to array of chars, but only when the lower array bound is zero.
The following code works for me in D2007 and Delphi XE:
var x : array[0..49] of char;
begin
x:='b'; // char literal
x:='bb'; // string literal
end.
If I change the [0 to [1 it fails. This limitation probably simplifies the language helper that takes care of this, and probably the feature is only meant for dealing with converted C structs where arrays always have lower bound 0.
Are you sure that you can't use string data type in a record?
Anyways...
type
TCharArray = array[Char] of Char;
function StringToArray(Str: string): TCharArray;
begin
FillChar(Result, High(Byte), #0);
Move(Str[1], Result, length(Str));
end;
procedure TestCharArray;
var
question: TCharArray;
begin
question := StringToArray('123');
ShowMessage(PChar(#question));
end;
Also take a look at StrPCopy function.
If you don't need unicode characters, you should just define your string like string[50].
After that you don't need any functions or conversions to work with that string, and it'll be just as easy to read and write it to a file.
Hscores = record
var
_topscore : integer;
_topname : string[50];
end;
I'm pretty sure you can use strings in record types.
This blog entry shows an example: http://delphi.about.com/od/beginners/a/record_type.htm
In order to assign a value to the Char array, you have to index it, like any other array:
question[1] := 't';