Recent

Author Topic: Modern For Loop question  (Read 5232 times)

lucamar

  • Hero Member
  • *****
  • Posts: 3595
Re: Modern For Loop question
« Reply #15 on: January 26, 2021, 05:09:13 pm »
BTW, this construct is not very useful, it can only count the number of matches but cannot retrieve a pointer to the location or an index or build a list.

For simple types and complex algotihms it might not always be adecuate (depending on what you're doing) but I can assure you it's a gods-send when you're traversing lists of records or objects. For example, if you want to change some property of all components in a form, or modify strings in a StringList, or ... well, lots of things like that.

Of course, if you really need the index then a standard for...to loop must be used but when you care only for the accessed element (and not its position), for...in is a jwell to have. And it happens quite a lot ...
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.10/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Peter H

  • Full Member
  • ***
  • Posts: 195
Re: Modern For Loop question
« Reply #16 on: January 26, 2021, 05:15:35 pm »
I deleted my last post, it was wrong.
Sorry.

Peter H

  • Full Member
  • ***
  • Posts: 195
Re: Modern For Loop question
« Reply #17 on: January 26, 2021, 05:33:20 pm »
This is, how it works:
Code: Pascal  [Select][+][-]
  1. ........
  2.   ar: array [1 ..10] of char;
  3.   c: char= 'a';             // this initialization doesnt matter, it is overwritten
  4. begin
  5.   fillchar(ar,sizeof(ar),#0);
  6.   for c in ar do begin    // c is successively assigned each value of ar 10 times here
  7.     if c = #0 then {dosomething} ;
  8.   end;
  9. .......
  10.  
« Last Edit: January 26, 2021, 05:35:26 pm by Peter H »

Thaddy

  • Hero Member
  • *****
  • Posts: 10704
« Last Edit: January 26, 2021, 06:10:26 pm by Thaddy »

bytebites

  • Sr. Member
  • ****
  • Posts: 381
Re: Modern For Loop question
« Reply #19 on: January 26, 2021, 06:11:52 pm »
Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. {$mode delphi}
  4. type
  5.   tintarray=array of integer;
  6.  
  7.   { tenum }
  8.  
  9.   tenum=class
  10.     fa:tintarray;
  11.     fcurrent:integer;
  12.     constructor create(a:tintarray);
  13.     function movenext:boolean;
  14.     function getcurrent:PInteger;
  15.     property Current:PInteger read getcurrent;
  16.     function getenumerator:tenum;
  17.   end;
  18.  
  19.   { tarrhlp }
  20.  
  21.   tarrhlp = record helper for tintarray
  22.     function getenumerator:tenum;
  23.   end;
  24.  
  25. { tarrhlp }
  26.  
  27. function tarrhlp.getenumerator: tenum;
  28. begin
  29.   result:=tenum.create(self);
  30. end;
  31.  
  32. { tenum }
  33.  
  34. constructor tenum.create(a: tintarray);
  35. begin
  36.   fa:=a;
  37.   fcurrent:=-1;
  38. end;
  39.  
  40. function tenum.movenext: boolean;
  41. begin
  42.   fcurrent:=fcurrent+1;
  43.   result:=fcurrent<Length(fa);
  44. end;
  45.  
  46. function tenum.getcurrent: PInteger;
  47. begin
  48.   result:=@fa[fcurrent];
  49. end;
  50.  
  51. function tenum.getenumerator: tenum;
  52. begin
  53.   result:=self;
  54. end;
  55.  
  56. var
  57.   i:PInteger;
  58.   nums:tintarray;
  59. begin
  60.    SetLength(nums,10);
  61.    for i in nums.getenumerator do i^:=random(20);
  62.    for i in nums.getenumerator do write(i^:5);
  63. end.
  64.  
« Last Edit: January 26, 2021, 06:24:10 pm by bytebites »

Peter H

  • Full Member
  • ***
  • Posts: 195
Re: Modern For Loop question
« Reply #20 on: January 28, 2021, 02:32:51 am »
Ok, I did my homework  ;D
One Question: This "inherited create" at line 18 is it necessary or not? It doesnt make a difference, when I test.

Code: Pascal  [Select][+][-]
  1. type
  2.   pWorkerthread = ^TMyWorkerthread;
  3.  
  4.   TWorkers = class
  5.   private
  6.     Fpos: pworkerthread;
  7.     Fidx:integer;
  8.   public
  9.     w: array of TMyWorkerThread;
  10.     constructor create(count:integer);
  11.     function GetEnumerator: TWorkers;
  12.     function MoveNext:Boolean;
  13.     property current: pWorkerThread read Fpos;
  14.   end;
  15.  
  16. constructor TWorkers.create(count:integer);
  17. begin
  18.   //inherited create;
  19.   SetLength(w,count);
  20.   Fpos := @w[0]; Fidx := 0;
  21. end;
  22.  
  23. function TWorkers.GetEnumerator:TWorkers;   begin result := self; end;
  24.  
  25. function TWorkers.MoveNext:boolean; begin
  26.    if Fidx>High(w) then begin result := false; exit; end;
  27.    inc(Fpos); inc(Fidx);
  28.    result := true;
  29. end;
  30.  
  31.  
  32. procedure MultiThread(FromIndex, ToIndex: Integer; Proc: DoProcProcedure; ThreadCount: Integer = 8);
  33. var
  34.     p: pWorkerthread;
  35.     workers:TWorkers;
  36. begin
  37.   CurrentIndex := FromIndex;    MaxIndex :=  ToIndex;
  38.   workers :=      TWorkers.create(Threadcount);
  39.  
  40.   for p in workers do
  41.     p^ := TMyWorkerthread.Create(Proc);
  42.  
  43.   while CurrentIndex < MaxIndex do
  44.     sleep(100);
  45.  
  46. end;

BTW, this is not for practical use. Is just a programming exercise for me. Of course in this case it is overkill.
« Last Edit: January 28, 2021, 11:32:19 am by Peter H »

ccrause

  • Sr. Member
  • ****
  • Posts: 349
Re: Modern For Loop question
« Reply #21 on: January 28, 2021, 07:28:22 am »
Ok, I did my homework  ;D
One Question: This "inherited create" at line 18 is it necessary or not? It doesnt make a difference, when I test.

Code: Pascal  [Select][+][-]
  1. type
  2.   pWorkerthread = ^TMyWorkerthread;
  3.  
  4.   TWorkers = class
  5.   private
  6.     Fpos: pworkerthread;
  7.     Fidx:integer;
  8.   public
  9.     w: array of TMyWorkerThread;
  10.     constructor create(count:integer);
  11.     function GetEnumerator: TWorkers;
  12.     function MoveNext:Boolean;
  13.     property current: pWorkerThread read Fpos;
  14.   end;
  15.  
  16. constructor TWorkers.create(count:integer);
  17. begin
  18.   //inherited create;
  19.   SetLength(w,count);
  20.   Fpos := @w[0]; Fidx := 0;
  21. end;
  22.  
  23. function TWorkers.GetEnumerator:TWorkers;   begin result := self; end;
  24.  
  25. function TWorkers.MoveNext:boolean; begin
  26.    if Fidx>High(w) then begin result := false; exit; end;
  27.    inc(Fpos); inc(Fidx);
  28.    result := true;
  29. end;
  30.  
  31.  
  32. procedure MultiThread(FromIndex, ToIndex: Integer; Proc: DoProcProcedure; ThreadCount: Integer = 8);
  33. var
  34.     p: pWorkerthread;
  35.     workers:TWorkers;
  36. begin
  37.   CurrentIndex := FromIndex;    MaxIndex :=     ToIndex;
  38.   workers :=      TWorkers.create(Threadcount);
  39.  
  40.   for p in workers do
  41.     p^ := TMyWorkerthread.Create(Proc);
  42.  
  43.   while CurrentIndex < MaxIndex do
  44.     sleep(100);
  45.  
  46. end;

Your TWorkers class is effectively at the base of the class hierarchy and derives from TObject, which doesn't implement anything in Create.  In Lazarus you can right click on inherited (or the create following inherited) and click on Find declaration...) to view the inherited method.

Peter H

  • Full Member
  • ***
  • Posts: 195
Re: Modern For Loop question
« Reply #22 on: January 28, 2021, 11:42:20 am »
Your TWorkers class is effectively at the base of the class hierarchy and derives from TObject, which doesn't implement anything in Create.  In Lazarus you can right click on inherited (or the create following inherited) and click on Find declaration...) to view the inherited method.

Thank you. Couldnt it be that "Create" does some magic stuff under the hood, e.g. setting up VMT's? In this case I would get problems as soon as I use a virtual method, e.g. "Destroy". This is empty too, but maybe it removes the VMT under the hood? Do I get a memory leak, if I dont explicitely call the destructor of TObject ?
There must be a reason why it is there, this is what I suspect.
Its just a question, simply spoken, I dont know it but want to know.
« Last Edit: January 28, 2021, 12:35:23 pm by Peter H »

PascalDragon

  • Hero Member
  • *****
  • Posts: 2747
  • Compiler Developer
Re: Modern For Loop question
« Reply #23 on: January 28, 2021, 01:32:35 pm »
The "magic stuff" is done thanks to you declaring it as constructor. The compiler does the necessary initialization before it calls your constructor and after it returns. The call to the inherited constructor is just a plain method call. Analogous for calling the inherited destructor by the way.

Peter H

  • Full Member
  • ***
  • Posts: 195
Re: Modern For Loop question
« Reply #24 on: January 28, 2021, 02:15:05 pm »
Thank you very much it is clear now.

As for the compiler and For x in ... loop, I have a proposal:
If  "x" is a pointer to the array element or whatever it could return a pointer automatically. This would be very useful, because this gives write access to the individual elements and avoids a copy.

example:
Code: Pascal  [Select][+][-]
  1. ............
  2. var s: string[10]; p: ^char;
  3.  
  4. begin
  5.  
  6. for p in s do
  7.    p^ := #32;
  8. ............
  9.  

Because the pointer itself cannot be written inside the loop, this is secure.
« Last Edit: January 28, 2021, 04:01:49 pm by Peter H »

Peter H

  • Full Member
  • ***
  • Posts: 195
Re: Modern For Loop question
« Reply #25 on: January 28, 2021, 04:45:29 pm »
I tested, if this might work and got errormessages
The error messages are not correct. (Lazarus 2.1.0 r64417 FPC 3.3.1 x86_64-win64-win32/win64)
Code: Pascal  [Select][+][-]
  1. program Project1;
  2. type
  3.     Ts = array of char;
  4.     Tp = ^char;
  5.  
  6. var s:   array of char = Nil;
  7.     p:   ^char;
  8.     s2:  Ts;   // Initialisation to Nil results in compile error here
  9.     p2:  Tp;
  10.  
  11. begin
  12.  
  13. for p in s do;   //project1.lpr(13,5) Error: Incompatible types: got "Char" expected "^Char"
  14.  
  15. for p2 in s2 do; //project1.lpr(15,5) Error: Incompatible types: got "Char" expected "Tp"
  16.                  // Note these error messages refer to pos5 in the line, this means to p and p2
  17. end.
  18.  

I registered at mantis and posted it: https://bugs.freepascal.org/view.php?id=38416
« Last Edit: January 28, 2021, 05:51:01 pm by Peter H »

lucamar

  • Hero Member
  • *****
  • Posts: 3595
Re: Modern For Loop question
« Reply #26 on: January 28, 2021, 06:17:18 pm »
How are they incorrect? You're trying to use a pointer as if it were a char, so it complains, logically enough, about that situation.

The "index" of a for...in must match the type it's expecting to receive: in this case, since you're traversing an array of char it must be a char!
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.10/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Peter H

  • Full Member
  • ***
  • Posts: 195
Re: Modern For Loop question
« Reply #27 on: January 28, 2021, 07:07:22 pm »
BTW, this construct is not very useful, it can only count the number of matches but cannot retrieve a pointer to the location or an index or build a list.

For simple types and complex algotihms it might not always be adecuate (depending on what you're doing) but I can assure you it's a gods-send when you're traversing lists of records or objects. For example, if you want to change some property of all components in a form, or modify strings in a StringList, or ... well, lots of things like that.

Of course, if you really need the index then a standard for...to loop must be used but when you care only for the accessed element (and not its position), for...in is a jwell to have. And it happens quite a lot ...

The problem is, you can modify an object, but not the object variable, because an object variable is implicitly a reference variable. You cannot Create it, because for this you need to assign a new value to the object variable.
If an array or vector contains only plain records or plain datatypes, you get a copy of the element and not a reference to it. In this case you cannot even modify it, because you will modify a temporary copy instead.

BTW, sorry if i often edit my posts, my english is not so good and almost always I see room for improvements later  :-[
« Last Edit: January 28, 2021, 08:24:54 pm by Peter H »

Peter H

  • Full Member
  • ***
  • Posts: 195
Re: Modern For Loop question
« Reply #28 on: January 28, 2021, 07:19:34 pm »
How are they incorrect? You're trying to use a pointer as if it were a char, so it complains, logically enough, about that situation.

The "index" of a for...in must match the type it's expecting to receive: in this case, since you're traversing an array of char it must be a char!

p and p2 are pointers to char. I hoped the "for p in s..." loop would assign the adress of the items to p, but obviously such a feature is not implemented.
The error message is wrong, because it says: "expected a pointer and got a char".
The correct error message would be: "expected a char and got a pointer"

For clarification see the last loop (I added this now) which tries to get a boolean out of a char array and the corresponding error message:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2. program Project1;
  3. uses gvector;
  4. type
  5.     Ts = array of char;
  6.     Tp = ^char;
  7.  
  8. var s:   array of char = Nil;
  9.     p:   ^char;
  10.     s2:  Ts;   // Initialisation to Nil results in compile error here
  11.     p2:  Tp;
  12.     b:   boolean;
  13. begin
  14.  
  15. for p in s do;   //project1.lpr(13,5) Error: Incompatible types: got "Char" expected "^Char"
  16. if p in s then;
  17.  
  18. for p2 in s2 do; //project1.lpr(15,5) Error: Incompatible types: got "Char" expected "Tp"
  19.                  // Note these error message refers to pos5 in the line, this means to p and p2
  20.  
  21. for b in s do;   //project1.lpr(20,5) Error: Incompatible types: got "Char" expected "Boolean"
  22. end.
  23.  

« Last Edit: January 28, 2021, 08:12:59 pm by Peter H »

ccrause

  • Sr. Member
  • ****
  • Posts: 349
Re: Modern For Loop question
« Reply #29 on: January 28, 2021, 08:14:08 pm »
The error message is wrong, because it says: "expected a pointer and got a char".
The error refers to the variable p as indicated by the column number. p is a pointer type, but the in s enumeration returns a char type.

 

TinyPortal © 2005-2018