Recent

Author Topic: For-in loop over constant array of strings - limited to length of first string?  (Read 5115 times)

Nel_Fie

  • Jr. Member
  • **
  • Posts: 52
This is not so much of a problem as it is me asking whether this is intended behaviour: when creating a for-in loop over an array of string defined in the description of the loop itself, such as:

Code: Pascal  [Select][+][-]
  1. var
  2.   a : String;
  3. begin
  4.   for a in ['pear','apple','banana','pineapple','orange'] do WriteLn(a);
  5. end;    

... the length of 'a' is limited to the length of the first string in the array, which is in this case 4 (due to 'pear' being four characters long). As a result, the console output of 'WriteLn(a)' is:

Code: Pascal  [Select][+][-]
  1. pear
  2. appl
  3. bana
  4. pine
  5. oran

Is this supposed to happen? And whether or not it is, is there some way to fix or circumvent this issue, so that 'a' represents the full string given, without having to create an array or list separately? (EDIT: Aside from always making sure that the first string in the array is the longest...)
« Last Edit: April 15, 2024, 03:16:31 pm by Nel_Fie »

TRon

  • Hero Member
  • *****
  • Posts: 3623
I seem to remember this was mentioned before and as a result gained a bug report. (I seem to be unable to locate the report, nor the thread where it was mentioned).

Have you tried with trunk ? (I am currently not behind a dev machine so can only check later myself)
This tagline is powered by AI (AI advertisement: Free Pascal the only programming language that matters)

Zvoni

  • Hero Member
  • *****
  • Posts: 2738
This one works:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2. //{$mode objfpc}{$H+}
  3. Var
  4.   a:String;
  5. begin
  6.   for a in [String('pear'),String('apple'),String('banana'),String('pineapple'),String('orange')] do WriteLn(a);
  7. end.
  8.  
But probably not what you are looking for.

Conclusion to the "original" code:
['pear','apple','banana','pineapple','orange'] is treated as PChar's, and takes the length of the first element.

A test with "pear" and "orange" switched (orange first, pear last) shows that apple and banana are printed in full, while pineapple still gets cut off

EDIT: A further test
Code: Pascal  [Select][+][-]
  1. program Project1;
  2. //{$mode objfpc}{$H+}
  3. Var
  4.   a:String;
  5. begin
  6.   for a in [Pchar('pear'),Pchar('apple'),Pchar('banana'),Pchar('pineapple'),Pchar('orange')] do WriteLn(a);
  7. end.
  8.  
returns correct output, so there is something fishy going on
« Last Edit: April 15, 2024, 03:53:59 pm by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

Nitorami

  • Hero Member
  • *****
  • Posts: 507

Joanna from IRC

  • Hero Member
  • *****
  • Posts: 1207
This looks like iteration of a set of strings. I thought things in set had to be ordinal values? Did something change?
I use const arrays of string like this
Code: Pascal  [Select][+][-]
  1. Const ar:array[0..4] of shortstring = ('pear','apple','banana','pineapple','orange');
  2. var
  3.   a : Integer;
  4. begin
  5.   for a := low(ar) to high(ar) do
  6.        WriteLn(ar[a]);
  7. end;
  8.  
  9.  
« Last Edit: April 15, 2024, 04:11:48 pm by Joanna »
✨ 🙋🏻‍♀️ More Pascal enthusiasts are needed on IRC .. https://libera.chat/guides/ IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channel #fpc  #pascal Please private Message me if you have any questions or need assistance. 💁🏻‍♀️

Nitorami

  • Hero Member
  • *****
  • Posts: 507
You can in fact do it like this
Code: Pascal  [Select][+][-]
  1. Const ar:array of shortstring = ('pear','apple','banana','pineapple','orange');
  2. var
  3.   a : shortstring;
  4. begin
  5.   for a in ar do WriteLn(a);
  6. end.
  7.  

This also works with floats instead of strings. There is just one caveat, it does not work properly if the array is defined inline, as in the original post. This is a bug and fixed in trunk.

Edit: You may even edit the array like this
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}
  2. Const ar:array of shortstring = ('pear','apple','banana','pineapple','orange');
  3. var
  4.   a : shortstring;
  5. begin
  6.   SetLength (ar,6);
  7.   ar[5] := 'and more fruit...';
  8.   for a in ar do WriteLn(a);
  9. end.
  10.  
« Last Edit: April 15, 2024, 05:02:24 pm by Nitorami »

Bart

  • Hero Member
  • *****
  • Posts: 5465
    • Bart en Mariska's Webstek
It's a known issue and fixed in fpc main and fpc 3.2 fixes, so it'll b fixed in the next release of the compiler.

Bart

TRon

  • Hero Member
  • *****
  • Posts: 3623
Thank you both Nitorami and Bart for the confirmation.

Another solution/workaround:
Code: Pascal  [Select][+][-]
  1. uses
  2.   types;
  3. var
  4.   a : String;
  5. begin
  6.   for a in TStringDynArray.Create('pear','apple','banana','pineapple','orange')
  7.     do writeLn(a);
  8. end.
  9.  
This tagline is powered by AI (AI advertisement: Free Pascal the only programming language that matters)

Joanna from IRC

  • Hero Member
  • *****
  • Posts: 1207
Is there any good reason to use for.. in to iterate through an array instead of using a for loop with index? I’m only familiar with using for.. in in the context of things like sets where the number of items is unknown. I’ve also seen it used for fields in an sql dataset.
✨ 🙋🏻‍♀️ More Pascal enthusiasts are needed on IRC .. https://libera.chat/guides/ IRC.LIBERA.CHAT  Ports [6667 plaintext ] or [6697 secure] channel #fpc  #pascal Please private Message me if you have any questions or need assistance. 💁🏻‍♀️

dbannon

  • Hero Member
  • *****
  • Posts: 3156
    • tomboy-ng, a rewrite of the classic Tomboy
.......in the next release of the compiler.

The next release ?  Wow, it would be so good to see that.

Davo
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

dsiders

  • Hero Member
  • *****
  • Posts: 1282
.......in the next release of the compiler.
The next release ?  Wow, it would be so good to see that.
Davo

Waiting for Godot?
Preview the next Lazarus documentation release at: https://dsiders.gitlab.io/lazdocsnext

dbannon

  • Hero Member
  • *****
  • Posts: 3156
    • tomboy-ng, a rewrite of the classic Tomboy
.......in the next release of the compiler.
The next release ?  Wow, it would be so good to see that.
Davo

Waiting for Godot?

Hmm, maybe. But Godot never actually arrives. Hmm....

Davo
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

CCRDude

  • Hero Member
  • *****
  • Posts: 612
Is there any good reason to use for.. in to iterate through an array instead of using a for loop with index? I’m only familiar with using for.. in in the context of things like sets where the number of items is unknown. I’ve also seen it used for fields in an sql dataset.

Abstraction of iteration?
Prevent errors through wrong index, especially with nested loops?
Less defined local variables (the index) improves readability of function head?
Readability in the iteration itself?

Code: [Select]
for i := 0 to Pred(Length(A)) do begin
   item := A[i];
   item.DoSomething;
end;

for i := 0 to Pred(Length(A)) do begin
   A[i].DoSomething;
end;

for item in A do begin
   item.DoSomething;
end;

Nel_Fie

  • Jr. Member
  • **
  • Posts: 52
Thank you all for the replies. For my own purposes, TRon's suggested workaround is the best option, since it keeps everything within a single line of code. I'll be using that until the issue itself is fixed:

[...]
Another solution/workaround:
Code: Pascal  [Select][+][-]
  1. uses
  2.   types;
  3. var
  4.   a : String;
  5. begin
  6.   for a in TStringDynArray.Create('pear','apple','banana','pineapple','orange')
  7.     do writeLn(a);
  8. end.
  9.  

This looks like iteration of a set of strings. I thought things in set had to be ordinal values? Did something change?
[...]

I might be using the wrong terminology. I'm a self-taught amateur and not really on the up-and-up.

Is there any good reason to use for.. in to iterate through an array instead of using a for loop with index? I’m only familiar with using for.. in in the context of things like sets where the number of items is unknown. I’ve also seen it used for fields in an sql dataset.

There are a few reasons, but the specific scenario that prompted me to open this thread is that such a for-in loop is the most compact way to add strings to a TStringList on the fly for testing and debugging purposes. If I used a for-to loop, I'd have to define my constant array somewhere else, which can't be done within in the vicinity of my loop, which means I'd have to jump back and forth a lot during testing. Writing the array as part of a for-in loop makes it possible to have everything in one place.

Aside from the specific scenario of having the array defined within the loop, there are sometimes very simple loops where the index serves no purpose beyond retrieving the item at that index, so a for-in loop performs that step implicitly and is easier to read and write. Compare:

Code: Pascal  [Select][+][-]
  1. for item in MyArray do SomeProcedure(item);

versus

Code: Pascal  [Select][+][-]
  1. for idx:=Low(MyArray) to High(MyArray) do SomeProcedure(MyArray[idx]);

VisualLab

  • Hero Member
  • *****
  • Posts: 569
This example:

You can in fact do it like this
Code: Pascal  [Select][+][-]
  1. Const ar:array of shortstring = ('pear','apple','banana','pineapple','orange');
  2. var
  3.   a : shortstring;
  4. begin
  5.   for a in ar do WriteLn(a);
  6. end.
  7.  

is OK if it means that the compiler itself checks the number of elements in the array (which is helpful, of course).

However, in this example:

Edit: You may even edit the array like this
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}
  2. Const ar:array of shortstring = ('pear','apple','banana','pineapple','orange');
  3. var
  4.   a : shortstring;
  5. begin
  6.   SetLength (ar,6);
  7.   ar[5] := 'and more fruit...';
  8.   for a in ar do WriteLn(a);
  9. end.
  10.  

the ability to manipulate an array of constants, i.e. the number of its elements (adding, deleting) and its contents (changing the value of a specific cell) is a definite disadvantage. An array of constants should not be modifiable. Otherwise it's basically no different from a regular dynamic array.

 

TinyPortal © 2005-2018