Lazarus

Free Pascal => Beginners => Topic started by: justnewbie on March 25, 2018, 10:28:47 pm

Title: Populate a dynamic array
Post by: justnewbie on March 25, 2018, 10:28:47 pm
I have a dynamic array (empty in the beginning) and I want to populate it with unknown number of elements. How should it be?
My bad code:

Code: Pascal  [Select][+][-]
  1. TValue = packed record
  2.     tvVal: integer;
  3.     tvString: string;  
  4.   end;
  5.  
  6.   tvArray = array of TValue;
  7.   pTVArray = ^tvArray;
  8.  
  9. // ...
  10.  
  11. procedure PopulateArray(var myArray: pTVArray);
  12. var i: integer;
  13.     counter: integer = 0;
  14. begin
  15.   for i := 0 to 100 do
  16.     begin
  17.       if {something} then
  18.       begin
  19.          inc(counter);
  20.          SetLength(myArray,counter);
  21.          myArray[counter-1].tvVal := i;
  22.          myArray[counter-1].tvString := 'Hi friend!';
  23.       end;
  24.     end;
  25. end;
Title: Re: Populate a dynamic array
Post by: Nitorami on March 25, 2018, 10:41:05 pm
If you are a newbie then by all means use simple native pascal code and don't fuff with pointers. Why do you need pTVArray ? It's completely unnecessary.
Title: Re: Populate a dynamic array
Post by: justnewbie on March 25, 2018, 10:47:13 pm
If you are a newbie then by all means use simple native pascal code and don't fuff with pointers. Why do you need pTVArray ? It's completely unnecessary.
I have to communicate between a DLL (written in FP) and MT4. This is why I need pointers (I guess). Example here (with some formatting noise): https://www.forexfactory.com/showthread.php?t=219576 (see "3. Giving the DLL access to all bars in the chart")
Title: Re: Populate a dynamic array
Post by: furious programming on March 25, 2018, 10:53:29 pm
First off, you must allocate memory for an array, before you try to set length of this array. Then, you need to set the length of array, and then, allocate memory for every array cell, before you write some data to them.

Below is a simple example – works correctly. Change the code for your purpose.

Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}{$LONGSTRINGS ON}
  2.  
  3. uses
  4.   HeapTrc;
  5.  
  6. type
  7.   PItem = ^TItem;
  8.   TItem = record
  9.     Value: Integer;
  10.     Text: String;
  11.   end;
  12.  
  13. type
  14.   PItems = ^TItems;
  15.   TItems = array of PItem;
  16.  
  17.   procedure PopulateItems(AItems: PItems);
  18.   var
  19.     LLength: Integer = 0;
  20.   begin
  21.     SetLength(AItems^, LLength + 1);
  22.  
  23.     New(AItems^[LLength]);
  24.     AItems^[LLength]^.Value := 100;
  25.     AItems^[LLength]^.Text := 'got it';
  26.   end;
  27.  
  28. var
  29.   LItems: PItems;
  30. begin
  31.   New(LItems);
  32.   PopulateItems(LItems);
  33.  
  34.   WriteLn('Value: ', LItems^[0]^.Value);
  35.   WriteLn('Text:  ', LItems^[0]^.Text);
  36.  
  37.   Dispose(LItems^[0]);
  38.   Dispose(LItems);
  39. end.
Title: Re: Populate a dynamic array
Post by: justnewbie on March 25, 2018, 10:55:43 pm
Thank you, I will study it!
Title: Re: Populate a dynamic array
Post by: furious programming on March 25, 2018, 11:05:22 pm
No, it can works bad – I miss something.
Title: Re: Populate a dynamic array
Post by: Nitorami on March 25, 2018, 11:13:42 pm
A dynamic array IS essentially already a pointer.
And setting the length does allocate the memory, no need to use "new" or GetMem.
Title: Re: Populate a dynamic array
Post by: furious programming on March 25, 2018, 11:21:42 pm
And setting the length does allocate the memory, no need to use "new" or GetMem.

No, using the New procedure is necessary. If you do not do this, you will get an SIGSEGV in SetLength.

Below is a more complex example. Works perfectly.

Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}{$LONGSTRINGS ON}
  2.  
  3. uses
  4.   HeapTrc, SysUtils;
  5.  
  6. type
  7.   PItem = ^TItem;
  8.   TItem = record
  9.     Value: Integer;
  10.     Text: String;
  11.   end;
  12.  
  13. type
  14.   PItems = ^TItems;
  15.   TItems = array of PItem;
  16.  
  17.   procedure PopulateItems(out AItems: PItems; ACount: Integer);
  18.   var
  19.     LItemIdx: Integer;
  20.   begin
  21.     New(AItems);
  22.     SetLength(AItems^, ACount);
  23.  
  24.     for LItemIdx := 0 to ACount - 1 do
  25.     begin
  26.       New(AItems^[LItemIdx]);
  27.  
  28.       AItems^[LItemIdx]^.Value := Random(100) + 1;
  29.       AItems^[LItemIdx]^.Text := Format('item no %d', [LItemIdx]);
  30.     end;
  31.   end;
  32.  
  33.   procedure DisposeItems(var AItems: PItems);
  34.   var
  35.     LItemIdx: Integer;
  36.   begin
  37.     for LItemIdx := 0 to High(AItems^) do
  38.       Dispose(AItems^[LItemIdx]);
  39.  
  40.     Dispose(AItems);
  41.     AItems := nil;
  42.   end;
  43.  
  44.   procedure PrintItems(AItems: PItems);
  45.   var
  46.     LItemIdx: Integer;
  47.   begin
  48.     for LItemIdx := 0 to High(AItems^) do
  49.       WriteLn('Value: ', AItems^[LItemIdx]^.Value, ', Text: ', AItems^[LItemIdx]^.Text);
  50.  
  51.     WriteLn();
  52.   end;
  53.  
  54. var
  55.   LItems: PItems;
  56. begin
  57.   Randomize();
  58.  
  59.   PopulateItems(LItems, 10);
  60.   PrintItems(LItems);
  61.   DisposeItems(LItems);
  62. end.

Examplary output:

Code: [Select]
Value: 76, Text: item no 0
Value: 29, Text: item no 1
Value: 59, Text: item no 2
Value: 32, Text: item no 3
Value: 87, Text: item no 4
Value: 38, Text: item no 5
Value: 16, Text: item no 6
Value: 2, Text: item no 7
Value: 98, Text: item no 8
Value: 95, Text: item no 9

Heap dump by heaptrc unit
109 memory blocks allocated : 2519/2736
109 memory blocks freed     : 2519/2736
0 unfreed memory blocks : 0
True heap size : 196608 (128 used in System startup)
True free heap : 196480
Title: Re: Populate a dynamic array
Post by: Nitorami on March 25, 2018, 11:34:33 pm
This works equally without any pointers.

Code: Pascal  [Select][+][-]
  1. type
  2.   TItem = record
  3.     Value: Integer;
  4.     Text: String;
  5.   end;
  6.  
  7. type
  8.   TItems = array of TItem;
  9.  
  10.   procedure PopulateItems(var AItems: TItems; ACount: Integer);
  11.   var
  12.     LItemIdx: Integer;
  13.   begin
  14.     SetLength(AItems, ACount);
  15.  
  16.     for LItemIdx := 0 to ACount - 1 do
  17.     begin
  18.  
  19.       AItems[LItemIdx].Value := Random(100) + 1;
  20.       AItems[LItemIdx].Text := Format('item no %d', [LItemIdx]);
  21.     end;
  22.   end;
  23.  
  24.   procedure DisposeItems(var AItems: TItems);
  25.   var
  26.     LItemIdx: Integer;
  27.   begin
  28.     for LItemIdx := 0 to High(AItems) do SetLength (AItems,0)
  29.   end;
  30.  
  31.   procedure PrintItems(const AItems: TItems);
  32.   var
  33.     LItemIdx: Integer;
  34.   begin
  35.     for LItemIdx := 0 to High(AItems) do
  36.       WriteLn('Value: ', AItems[LItemIdx].Value, ', Text: ', AItems[LItemIdx].Text);
  37.  
  38.     WriteLn();
  39.   end;
  40.  
  41. var
  42.   LItems: TItems;
  43. begin
  44.   Randomize();
  45.   PopulateItems(LItems, 10);
  46.   PrintItems(LItems);
  47.   DisposeItems(LItems);
  48. end.
  49.  
Title: Re: Populate a dynamic array
Post by: furious programming on March 25, 2018, 11:46:07 pm
 @Nitorami: yes, but @justnewbie wants to use pointers – let him play.  :D
Title: Re: Populate a dynamic array
Post by: justnewbie on March 26, 2018, 12:06:49 am
@furious: justnewbie only needs a working method for communication between a DLL and MT4.  I'm searching for the simplest way.  :)
If there is any pointer-less method, just let me know.
Title: Re: Populate a dynamic array
Post by: furious programming on March 26, 2018, 12:34:50 am
Ok, so @Nitorami change my code and show how to do it without pointers.

Edit: If you don't need pointers, you can use following:

Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}{$LONGSTRINGS ON}
  2.  
  3. uses
  4.   HeapTrc, SysUtils;
  5.  
  6. type
  7.   TItem = record
  8.     Value: Integer;
  9.     Text: String;
  10.   end;
  11.  
  12. type
  13.   TItems = array of TItem;
  14.  
  15.   procedure PopulateItems(var AItems: TItems; ACount: Integer);
  16.   var
  17.     LItemIdx: Integer;
  18.   begin
  19.     SetLength(AItems, ACount);
  20.  
  21.     for LItemIdx := 0 to ACount - 1 do
  22.     begin
  23.       AItems[LItemIdx].Value := Random(100) + 1;
  24.       AItems[LItemIdx].Text := Format('item no %d', [LItemIdx]);
  25.     end;
  26.   end;
  27.  
  28.   procedure DisposeItems(var AItems: TItems);
  29.   begin
  30.     SetLength(AItems, 0);
  31.   end;
  32.  
  33.   procedure PrintItems(const AItems: TItems);
  34.   var
  35.     LItem: TItem;
  36.   begin
  37.     for LItem in AItems do
  38.       WriteLn('Value: ', LItem.Value, ', Text: ', LItem.Text);
  39.  
  40.     WriteLn();
  41.   end;
  42.  
  43. var
  44.   LItems: TItems;
  45. begin
  46.   Randomize();
  47.  
  48.   PopulateItems(LItems, 10);
  49.   PrintItems(LItems);
  50.   DisposeItems(LItems);
  51. end.

DisposeItems does not need a loop – you only need to change the size of the array once. PrintItems can use for in loop to improve code readability.
Title: Re: Populate a dynamic array
Post by: jamie on March 26, 2018, 03:59:34 am
I think a little more reading on the use of the MT4 dll is needed here..  >:(

Does this interface comply with a COM/OLE interface ?

you really need to know..

I suppose it could also be just a callback, but still you need to know....

if there is a function you are calling that is supplying a POINTER to a block of memory within the
MT4 Library, then you need to know this too!.... Because that would require you to assign a specific pointer
to a Records with a few variant fields in it or close to that anyways!
Title: Re: Populate a dynamic array
Post by: justnewbie on March 26, 2018, 10:31:02 am

Edit: If you don't need pointers ...

To be honest I really don't know whether I need pointers or not. In the example what I saw, there was pointer, so I think I need it.
Title: Re: Populate a dynamic array
Post by: justnewbie on March 26, 2018, 10:34:22 am
@jamie: my knowledge is weak to it, I don't understand these things.
But, I could make a code that is able to send candle-data from MT4 to a DLL function and its result went back to the MT4.
Title: Re: Populate a dynamic array
Post by: jamie on March 27, 2018, 12:48:19 am
I don't know the interface you are attempting to work with however, it is customary for a remote set of code functions
to supply a function that will return data to your code..

 The function will expect the size of the block you have, if it is not enough, it will return false but return the actual size it
needs, then you can reallocate the memory and then try it again with now the amount of memory it needs for the
call..

 I am sure there are some specs somewhere that indicates this?
Title: Re: Populate a dynamic array
Post by: justnewbie on April 30, 2018, 08:46:20 pm
Earlier I got some solution, but those are not good for me, because I need a version where the array size is UNKNOWN in advance.
So, how can I dynamically populate (and increase its size) the array properly?
My insufficient code:

Code: Pascal  [Select][+][-]
  1. var myArray: array of byte;
  2. while Something1 do
  3. begin
  4.   if Something2 then
  5.   begin
  6.      myArray[]:=Random(255);
  7.   end;
  8. end;
Title: Re: Populate a dynamic array
Post by: Thaddy on April 30, 2018, 09:13:26 pm
Something like this?:
Code: Pascal  [Select][+][-]
  1. program untitled;
  2. {$ifdef fpc}{$mode delphi}{$H+}{$endif}
  3. var
  4.   myArray: array of byte;
  5.   i,b:byte;
  6. begin
  7.   Randomize;
  8.   Setlength(myArray,Random(100)); //create a dynamic array of random length
  9.   writeln('Array size is ',length(myArray));
  10.   for i:=Low(myArray) to High(myArray) do // i is index of an array element, not content
  11.      myArray[i]:=Random(255); // fill the array with random values
  12.   writeln('Array content is :');
  13.   for b in myArray do  // b is content of an array element, not an index
  14.      write(b,' ');
  15.   writeln;
  16. end.

Run it a few times...

Title: Re: Populate a dynamic array
Post by: justnewbie on April 30, 2018, 09:24:37 pm
@Thaddy: no, I no need random size array.
I have an empty array. I don't know in advance what size it will have.
When a certain "if" condition is met, then this array gets a newer value.
I know how to add the new array value, but how should I manage the array's size properly?

I think this is not good, is it?
Code: Pascal  [Select][+][-]
  1. var myArray: array of byte;    
  2. begin  
  3.     while Something1 do
  4.     begin
  5.       if Something2 then
  6.       begin
  7.         SetLength(myArray, Length(myArray)+1);
  8.         myArray[i]:=Random(255);
  9.       end;    
  10.     end;
  11. end;
Title: Re: Populate a dynamic array
Post by: Thaddy on April 30, 2018, 09:37:46 pm
That would be simply
Code: Pascal  [Select][+][-]
  1. var myArray: array of byte;    
  2. begin  
  3.     while Something1 do
  4.     begin
  5.       if Something2 then
  6.       begin
  7.         SetLength(myArray, Succ(High(myArray)));  // increase size by one
  8.         myArray[High(myArray)]:=Random(255); // fill the new element
  9.       end;    
  10.     end;
  11. end;
Note this is very slow code if it happens often. Better to have a large array and maintain an index to the last element used, and increase that.
Results in much faster code.
Title: Re: Populate a dynamic array
Post by: justnewbie on April 30, 2018, 09:38:57 pm
OK, thanks!
Title: Re: Populate a dynamic array
Post by: Thaddy on April 30, 2018, 09:41:07 pm
I made a small mistake. Corrected.
Title: Re: Populate a dynamic array
Post by: howardpc on April 30, 2018, 09:48:20 pm
If you want the array to cover the full byte value range that would need to be
Code: Pascal  [Select][+][-]
  1. ...
  2. myArray[High(myArray)] := Random(256);
  3. ...
Title: Re: Populate a dynamic array
Post by: justnewbie on April 30, 2018, 09:55:46 pm
@howardpc: thanks, it was just a quick example, not the real code
Title: Re: Populate a dynamic array
Post by: ASerge on May 01, 2018, 09:58:49 am
Code: Pascal  [Select][+][-]
  1. ...
  2.         SetLength(myArray, Succ(High(myArray)));  // increase size by one
  3. ...
It's equal to SetLength(myArray, Length(myArray)); and does not increase it.
I prefer to use an additional variable:
Code: Pascal  [Select][+][-]
  1. H := Length(myArray);
  2. SetLength(myArray, H + 1);
  3. myArray[H] := ...
Title: Re: Populate a dynamic array
Post by: Thaddy on May 01, 2018, 05:30:04 pm
Since when? succ(High( increases index. just like +1. Silly conclusion and again not taken the time to test it.
Title: Re: Populate a dynamic array
Post by: bytebites on May 01, 2018, 08:30:39 pm
Code: Pascal  [Select][+][-]
  1. writeln(Succ(High(myArray))=Length(myArray));
Title: Re: Populate a dynamic array
Post by: Thaddy on May 01, 2018, 10:57:11 pm
Which is correct, but real programmers count from zero...... succ() increases by one. Always.
Title: Re: Populate a dynamic array
Post by: Bart on May 01, 2018, 11:55:27 pm
Since when? succ(High( increases index. just like +1. Silly conclusion and again not taken the time to test it.

That would be simply
Code: Pascal  [Select][+][-]
  1. ...
  2.         SetLength(myArray, Succ(High(myArray)));  // increase size by one
  3. ...
  4.  

Did you test your own claim "// increase size by one" at all?

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. {$mode objfpc}
  3. {$h+}
  4.  
  5. var
  6.   arr: array of integer;
  7. begin
  8.   setlength(arr,1);
  9.   writeln('Before: Length(arr) = ',length(arr));
  10.   setlength(arr, succ(high(arr)));
  11.   writeln('After : Length(arr) = ',length(arr));
  12. end.

Gives:
Code: [Select]
Before: Length(arr) = 1
After : Length(arr) = 1

Bart
Title: Re: Populate a dynamic array
Post by: Thaddy on May 02, 2018, 01:16:14 pm
Strange...Since the succ() should not be related to the high in any way at all, but just take the value and return High + 1.
And the inner evaluation has precedence. Or maybe I fell unto my own trap: counting from zero  8-)
Title: Re: Populate a dynamic array
Post by: Bart on May 02, 2018, 08:25:06 pm
Strange...Since the succ() should not be related to the high in any way at all, but just take the value and return High + 1.
And the inner evaluation has precedence. Or maybe I fell unto my own trap: counting from zero  8-)
Yes, you fell unto your own trap!

If Length(arr) = 1, then High(arr) = 0 and Succ(High(arr)) will be 0+1 = 1.
As bytebites pointed out: for dynamic arrays, High() will be the same as Pred(Length()).

It's elementary.

Bart
TinyPortal © 2005-2018