Recent

Author Topic: for-in loop with record  (Read 589 times)

jollytall

  • Sr. Member
  • ****
  • Posts: 345
for-in loop with record
« on: October 10, 2024, 04:28:10 pm »
I try a simple for-in iteration:
Code: Pascal  [Select][+][-]
  1. type
  2.   tRec = record
  3.     i : integer;
  4.     end;
  5.   tRecs = array of tRec;
  6. var
  7.   Recs : tRecs;
  8.   Rec : tRec;
  9. begin
  10. for Rec in Recs do
  11.   Rec.i := 0;
  12. end.
It does not compile, with error Illegal assignment to for-loop variable. I wonder why. It is clear that a loop variable cannot be changed inside a loop, but in this case I do not want to change the "record" itself, "only" a field of it. Normally that should work, as I can also do an equivalent purpose version
Code: Pascal  [Select][+][-]
  1. type
  2.   tRec = record
  3.     i : integer;
  4.     end;
  5.   tRecs = array of tRec;
  6. var
  7.   Recs : tRecs;
  8.   i : integer;
  9. begin
  10. for i := Low(Recs) to High(Recs) do
  11.   Recs[i].i := 0;
  12. end.
Is it by intended design, or is it a shortage of functionality/bug?

dsiders

  • Hero Member
  • *****
  • Posts: 1258
Re: for-in loop with record
« Reply #1 on: October 10, 2024, 04:47:05 pm »
Is it by intended design, or is it a shortage of functionality/bug?

It is documented that arrays must be fixed-length for use with for-in.
Preview Lazarus 3.99 documentation at: https://dsiders.gitlab.io/lazdocsnext

jollytall

  • Sr. Member
  • ****
  • Posts: 345
Re: for-in loop with record
« Reply #2 on: October 10, 2024, 05:07:49 pm »
I think you are wrong here. There is no problem with an array not being fixed length. The following code works with an open array:
Code: Pascal  [Select][+][-]
  1. type
  2.   tRec = record
  3.     i : integer;
  4.     end;
  5.   tRecs = array of tRec;
  6. var
  7.   Recs : tRecs;
  8.   Rec : tRec;
  9. begin
  10. for Rec in Recs do
  11.   if Rec.i = 0 then
  12.     beep;
  13. end.
while the next one does not work with a fixed length array
Code: Pascal  [Select][+][-]
  1. type
  2.   tRec = record
  3.     i : integer;
  4.     end;
  5.   tRecs = array[1..10] of tRec;
  6. var
  7.   Recs : tRecs;
  8.   Rec : tRec;
  9. begin
  10. for Rec in Recs do
  11.   Rec.i := 0;
  12. end.
The problem is with assigning a value to its field.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11818
  • FPC developer.
Re: for-in loop with record
« Reply #3 on: October 10, 2024, 05:09:41 pm »
The Rec variable is write protected and a copy of the rec in the array.

I don't know an easy solution for this case. If the loopvar type is a pointer under the hood, then you can use a plain "pointer" typed variable to iterate and then cast the result to modify.

When I encountered this, is documented here: https://stackoverflow.com/questions/54825079/tdictionary-setting-a-valuetype-value-while-iterating

cdbc

  • Hero Member
  • *****
  • Posts: 1569
    • http://www.cdbc.dk
Re: for-in loop with record
« Reply #4 on: October 10, 2024, 05:19:51 pm »
Hi
In lieu of Marcov's post, I'd say, write an enumerator that emits a PRec in its 'GetCurrent' method...
I think it should work...  %)
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11818
  • FPC developer.
Re: for-in loop with record
« Reply #5 on: October 10, 2024, 05:37:28 pm »
cdbc: Hmm, yes.  The trick is to do it in a reusable/generic way, otherwise it defeats the purpose (to have a very short for, if you have to write a complex iterator each time)

So probably something like a generic helper or so that provides an enumerator that returns pointers to each element that can be instantiated for multiple records.


jollytall

  • Sr. Member
  • ****
  • Posts: 345
Re: for-in loop with record
« Reply #6 on: October 10, 2024, 05:45:07 pm »
Thanks.

It is clear, it is write protected. However I think it is wrong. Probably it comes from a normal for loop where for obvious reasons (well not so obvious if one thinks of the C logic while-do like for loops) it is not allowed to change the loop variable.
When the for-in iterates through the array, it should not make it write protected as not the record itself is iterated, but - I guess - under the hood the elements of the array. So, there is nothing wrong with changing an element of the array. Even if it was not an array of records, but e.g. an array of integers, why would/should it not be possible to change the integer that is an element of an array.

Yes, probably there are many ways to do an advanced iterator, but honestly then I stick to the for i := Low() to High() do logic. That is almost as easy as my intended solution, albeit far less elegant.

dsiders

  • Hero Member
  • *****
  • Posts: 1258
Re: for-in loop with record
« Reply #7 on: October 10, 2024, 05:49:11 pm »
I think you are wrong here.

Fine. But I invite you to READ the documentation about the 5 conditions where for-in is supported.
Preview Lazarus 3.99 documentation at: https://dsiders.gitlab.io/lazdocsnext

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11818
  • FPC developer.
Re: for-in loop with record
« Reply #8 on: October 10, 2024, 05:58:46 pm »
Generic type helpers, see last post (from PascalDragon) in this thread:

https://forum.lazarus.freepascal.org/index.php?topic=65911.0

Note usage would even then (with future generic helpers) still look something like this:

Code: [Select]
    type
      tRec = record
        i : integer;
        end;
     prec  = ^Trec;
      tRecs = array of tRec;
    var
      Recs : tRecs;
      R :pRec;
    begin
    for R in genericrechelper<Trec>.iterator(Recs) do
      prec(p).i := 0;
    end.

« Last Edit: October 10, 2024, 06:03:44 pm by marcov »

Curt Carpenter

  • Hero Member
  • *****
  • Posts: 544
Re: for-in loop with record
« Reply #9 on: October 10, 2024, 07:36:59 pm »
Feeling crabby today, so I'll ask:  is it really that much trouble to declare and use an iterator? 

PascalDragon

  • Hero Member
  • *****
  • Posts: 5723
  • Compiler Developer
Re: for-in loop with record
« Reply #10 on: October 10, 2024, 09:03:12 pm »
Is it by intended design, or is it a shortage of functionality/bug?

It is documented that arrays must be fixed-length for use with for-in.

Wrong. And if it is documented, then please point to it so that it can be nuked.

It is clear, it is write protected. However I think it is wrong. Probably it comes from a normal for loop where for obvious reasons (well not so obvious if one thinks of the C logic while-do like for loops) it is not allowed to change the loop variable.
When the for-in iterates through the array, it should not make it write protected as not the record itself is iterated, but - I guess - under the hood the elements of the array. So, there is nothing wrong with changing an element of the array. Even if it was not an array of records, but e.g. an array of integers, why would/should it not be possible to change the integer that is an element of an array.

The iteration variable is not a reference into the array, but it is instead a copy. Thus the iteration variable is made readonly to avoid the user assuming that changes they make to it are in any way propagated to the iterated container. This becomes especially apparent if the enumerator generates something dynamically.

dsiders

  • Hero Member
  • *****
  • Posts: 1258
Re: for-in loop with record
« Reply #11 on: October 10, 2024, 09:53:12 pm »
Wrong. And if it is documented, then please point to it so that it can be nuked.

https://www.freepascal.org/docs-html/ref/refsu59.html


Code: Text  [Select][+][-]
  1. As of version 2.4.2, Free Pascal supports the For..in loop construction. A for..in loop is used in case one wants to calculate something a fixed number of times with an enumerable loop variable. The prototype syntax is as follows:
  2.  
  3. ...
  4.  
  5. The enumerable must be an expression that consists of a fixed number of elements.
  6.  
Preview Lazarus 3.99 documentation at: https://dsiders.gitlab.io/lazdocsnext

PascalDragon

  • Hero Member
  • *****
  • Posts: 5723
  • Compiler Developer
Re: for-in loop with record
« Reply #12 on: October 10, 2024, 10:00:08 pm »
Wrong. And if it is documented, then please point to it so that it can be nuked.

https://www.freepascal.org/docs-html/ref/refsu59.html


Code: Text  [Select][+][-]
  1. As of version 2.4.2, Free Pascal supports the For..in loop construction. A for..in loop is used in case one wants to calculate something a fixed number of times with an enumerable loop variable. The prototype syntax is as follows:
  2.  
  3. ...
  4.  
  5. The enumerable must be an expression that consists of a fixed number of elements.
  6.  

I think it means that the enumerable should not be endless, but even that wouldn't be a problem as one can still use Break. Nevertheless it might be better to adjust how this is formulated. Also the "As of version 2.4.2" should be removed, cause the documentation is version specific anyway...

 

TinyPortal © 2005-2018