### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: Nice feature of sets  (Read 1467 times)

#### simsee

• Jr. Member
• Posts: 67
##### Nice feature of sets
« on: May 02, 2020, 09:36:19 pm »
I have always used sets where the type of elements is defined in the type section. I had never noticed that it's possible to build sets on the fly that contain variable elements, for example to be used in a for..in loop Eg:

Code: Pascal  [Select][+][-]
1.
2. var
3.   ind,k,l : integer;
4. begin
5.   k:=3;
6.   l:=50;
7.   for ind in [1..k,l] do
8.     writeln(ind);
9. end.

Interesting feature, not explicitly documented, or at least not obvious to me. For this reason I thought of sharing it in the beginners section.
« Last Edit: May 02, 2020, 09:39:31 pm by simsee »

#### Leledumbo

• Hero Member
• Posts: 8266
• Programming + Glam Metal + Tae Kwon Do = Me
##### Re: Nice feature of sets
« Reply #1 on: May 02, 2020, 09:56:59 pm »
Interesting feature, not explicitly documented, or at least not obvious to me. For this reason I thought of sharing it in the beginners section.
https://www.freepascal.org/docs-html/ref/refse79.html, part of https://www.freepascal.org/docs-html/ref/refch12.html.

#### ASerge

• Hero Member
• Posts: 1671
##### Re: Nice feature of sets
« Reply #2 on: May 02, 2020, 11:58:14 pm »
https://www.freepascal.org/docs-html/ref/refse79.html, part of https://www.freepascal.org/docs-html/ref/refch12.html.
I think this is more appropriate The For..in..do statement 2. A set value. The loop will then be over all elements in the set, the control variable must be of the base type of the set.

#### simsee

• Jr. Member
• Posts: 67
##### Re: Nice feature of sets
« Reply #3 on: May 03, 2020, 12:18:44 am »
Thanks. I know these documents. This is the key point: "A set constructor is a comma separated list of expressions, enclosed in square brackets." In my mind was the wrong idea that elements of a set must be constant. An expression can have variables.

#### 440bx

• Hero Member
• Posts: 2001
##### Re: Nice feature of sets
« Reply #4 on: May 03, 2020, 01:32:50 am »
I just wanted to mention that when using variables to specify the bounds of a set, the results may not be as expected as the sample code shows:
Code: Pascal  [Select][+][-]
1. {\$APPTYPE CONSOLE}
2.
3. program TestSets;
4.
5. var
6.   a, b : cardinal;
7.
8.   c    : cardinal;
9.
10. begin
11.   { case 1}
12.
13.   a := 1;
14.
15.   b := high(cardinal);
16.
17.   c := 23000;
18.
19.   writeln('a = ', a, ' b = ', b, ' c = ', c);
20.   write  ('c in [a..b] ? : ');
21.   if c in [a..b] then writeln('yes') else writeln('no - wrong');
22.   writeln;
23.
24.   b := high(cardinal) div 8;
25.
26.   c := b * 2;
27.
28. { case 2 }  { correct but, just happenstance }
29.
30.   writeln('a = ', a, ' b = ', b, ' c = ', c);
31.   write  ('c in [a..b] ? : ');
32.   if c in [a..b] then writeln('yes') else writeln('no - pure happenstance');
33.   writeln;
34.
35. { case 3 }
36.
37.   b := 25;
38.   c := 12;
39.
40.   writeln('a = ', a, ' b = ', b, ' c = ', c);
41.   write  ('c in [a..b] ? : ');
42.   if c in [a..b] then writeln('yes - correct') else writeln('no');
43.   writeln;
44.
45. { case 4 }
46.
47.   a := 200;
48.   b := 400;
49.   c := 301;
50.
51.   writeln('a = ', a, ' b = ', b, ' c = ', c);
52.   write  ('c in [a..b] ? : ');
53.   if c in [a..b] then writeln('yes - correct') else writeln('no - wrong');
54.   writeln;
55. end.
56.

ETA:
and there won't be any compiler warnings about the range not being in the bounds of what the compiler expects.
« Last Edit: May 03, 2020, 01:35:36 am by 440bx »
FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

#### simsee

• Jr. Member
• Posts: 67
##### Re: Nice feature of sets
« Reply #5 on: May 03, 2020, 10:00:43 am »
Are these incorrect behaviors documented? The sets used in this way do not seem reliable ...

#### howardpc

• Hero Member
• Posts: 3523
##### Re: Nice feature of sets
« Reply #6 on: May 03, 2020, 10:39:02 am »
I was not aware that FPC allowed you to construct a set of variables in code in this way.
However, whoever implemented it clearly left a bug, since the compiler should at least check that the variables in the set are Byte or Char (or some subrange, or an enumeration within Byte range), and balk if this condition is not met.

#### munair

• Hero Member
• Posts: 697
• keep it simple
##### Re: Nice feature of sets
« Reply #7 on: May 03, 2020, 11:00:57 am »
..to specify the bounds of a set..

Strictly speaking, in your example there is no set.

My question in this thread regarding the 'in' operator is related to your example. I've seen code before where the operator was plainly used like that without overload or set definition. Not good.
« Last Edit: May 03, 2020, 11:04:50 am by munair »

#### simsee

• Jr. Member
• Posts: 67
##### Re: Nice feature of sets
« Reply #8 on: May 03, 2020, 11:05:22 am »
I was not aware that FPC allowed you to construct a set of variables in code in this way.
However, whoever implemented it clearly left a bug, since the compiler should at least check that the variables in the set are Byte or Char (or some subrange, or an enumeration within Byte range), and balk if this condition is not met.

This confirms my initial thesis: this is an aspect not explicitly documented. In light of the 440bx example, I add that it is a seriuos bug in a core feature of the language.

#### simone

• Sr. Member
• Posts: 356
##### Re: Nice feature of sets
« Reply #9 on: May 03, 2020, 11:23:04 am »
Consider the output of the following example:

Code: Pascal  [Select][+][-]
1. program Project1;
2. var
3.   N,I : integer;
4.
5. begin
6.   N:=256;
7.   for I in [0..N] do
8.     writeln(I);
10. end.

The program shows only one line with 0.

This leads me to think that when an element of a set has more than 8 bits (i.e. is not a char, byte etc), the least significant byte is taken into account.
Microsoft Windows 10 64 bit - Lazarus 2.0.10

#### munair

• Hero Member
• Posts: 697
• keep it simple
##### Re: Nice feature of sets
« Reply #10 on: May 03, 2020, 11:36:29 am »
The program shows only one line with 0.

Did you expect 257 iterations?

Quote
This leads me to think that when an element of a set has more than 8 bits (i.e. is not a char, byte etc), the least significant byte is taken into account.

Obviously only the first 8 bits are considered. But FPC should check the word size definitely.
« Last Edit: May 03, 2020, 12:06:37 pm by munair »

#### eljo

• Sr. Member
• Posts: 408
##### Re: Nice feature of sets
« Reply #11 on: May 03, 2020, 11:38:49 am »
This leads me to think that when an element of a set has more than 8 bits (i.e. is not a char, byte etc), the least significant byte is taken into account.

Obviously only the first 8 bits are considered. But FPC should check the word size definitely.
No the first 255 values are considered. A set with 255 values is 255 bits long.

But yeah an out of bounds or variable to big or something should be raised I guess.

#### munair

• Hero Member
• Posts: 697
• keep it simple
##### Re: Nice feature of sets
« Reply #12 on: May 03, 2020, 11:52:39 am »
This leads me to think that when an element of a set has more than 8 bits (i.e. is not a char, byte etc), the least significant byte is taken into account.

Obviously only the first 8 bits are considered. But FPC should check the word size definitely.
No the first 255 values are considered. A set with 255 values is 255 bits long.

But yeah an out of bounds or variable to big or something should be raised I guess.

Yeah, that was explained before, but it's confusing.

#### PascalDragon

• Hero Member
• Posts: 2138
• Compiler Developer
##### Re: Nice feature of sets
« Reply #13 on: May 03, 2020, 03:23:34 pm »
Seems like no one checked with enabled overflow and range checks... The example by 440bx extended by exception handling code:

Code: Pascal  [Select][+][-]
1. {\$APPTYPE CONSOLE}
2.
3. program tsettest;
4.
5. {\$mode objfpc}
6.
7. {\$OverflowChecks On}
8. {\$RangeChecks On}
9.
10. uses
11.   SysUtils;
12.
13. var
14.   a, b : cardinal;
15.
16.   c    : cardinal;
17.
18. begin
19.   { case 1}
20.
21.   a := 1;
22.
23.   b := high(cardinal);
24.
25.   c := 23000;
26.
27.   writeln('a = ', a, ' b = ', b, ' c = ', c);
28.   write  ('c in [a..b] ? : ');
29.   try
30.     if c in [a..b] then writeln('yes') else writeln('no - wrong');
31.   except
32.     on e: Exception do
33.       Writeln('Exception (', e.ClassName, '): ', e.Message);
34.   end;
35.   writeln;
36.
37.   b := high(cardinal) div 8;
38.
39.   c := b * 2;
40.
41. { case 2 }  { correct but, just happenstance }
42.
43.   writeln('a = ', a, ' b = ', b, ' c = ', c);
44.   write  ('c in [a..b] ? : ');
45.   try
46.     if c in [a..b] then writeln('yes') else writeln('no - pure happenstance');
47.   except
48.     on e: Exception do
49.       Writeln('Exception (', e.ClassName, '): ', e.Message);
50.   end;
51.   writeln;
52.
53. { case 3 }
54.
55.   b := 25;
56.   c := 12;
57.
58.   writeln('a = ', a, ' b = ', b, ' c = ', c);
59.   write  ('c in [a..b] ? : ');
60.   try
61.     if c in [a..b] then writeln('yes - correct') else writeln('no');
62.   except
63.     on e: Exception do
64.       Writeln('Exception (', e.ClassName, '): ', e.Message);
65.   end;
66.   writeln;
67.
68. { case 4 }
69.
70.   a := 200;
71.   b := 400;
72.   c := 301;
73.
74.   writeln('a = ', a, ' b = ', b, ' c = ', c);
75.   write  ('c in [a..b] ? : ');
76.   try
77.     if c in [a..b] then writeln('yes - correct') else writeln('no - wrong');
78.   except
79.     on e: Exception do
80.       Writeln('Exception (', e.ClassName, '): ', e.Message);
81.   end;
82.   writeln;
83. end.

Output:

Code: [Select]
`a = 1 b = 4294967295 c = 23000c in [a..b] ? : Exception (ERangeError): Range check errora = 1 b = 536870911 c = 1073741822c in [a..b] ? : Exception (ERangeError): Range check errora = 1 b = 25 c = 12c in [a..b] ? : yes - correcta = 200 b = 400 c = 301c in [a..b] ? : Exception (ERangeError): Range check error`
I was not aware that FPC allowed you to construct a set of variables in code in this way.
However, whoever implemented it clearly left a bug, since the compiler should at least check that the variables in the set are Byte or Char (or some subrange, or an enumeration within Byte range), and balk if this condition is not met.

This confirms my initial thesis: this is an aspect not explicitly documented. In light of the 440bx example, I add that it is a seriuos bug in a core feature of the language.

The limits of sets are documented here:

Quote
Each of the elements of SetType must be of type TargetType. TargetType can be any ordinal type with a range between 0 and 255. A set can contain at most 255 elements.

The compiler checks for these bounds either at compile time (if you're dealing with constants) or at runtime (if the values aren't constant and range checks are enabled).

#### 440bx

• Hero Member
• Posts: 2001
##### Re: Nice feature of sets
« Reply #14 on: May 03, 2020, 05:36:54 pm »
The compiler checks for these bounds either at compile time (if you're dealing with constants) or at runtime (if the values aren't constant and range checks are enabled).
Even when dealing with constants, it doesn't always check at compile time, e.g, it allows a constant array subscript out of the array's declared bounds, which is incorrect.  Delphi doesn't... it does it right.   You and Jonas have already declined to correct that error, therefore, I am not asking for it to be corrected, just pointing out that FPC does less than it could do (and should do) at compile time.

I'll give you another opportunity to say "no" again to a different thing here it is... in case 4, which is:
Code: Pascal  [Select][+][-]
1. { case 4 }
2.
3.   a := 200;
4.   b := 400;
5.   c := 301;
6.
7.   writeln('a = ', a, ' b = ', b, ' c = ', c);
8.   write  ('c in [a..b] ? : ');
9.   if c in [a..b] then writeln('yes - correct') else writeln('no - wrong');
10.   writeln;
11. end.
12.
The number of elements in the set bound by "a" to "b" (200..400) is less than 256, therefore, that is a case where the compiler could use its set algorithms to determine if "c" is in the set but, for that, it would need to "rebase" the set (on the fly when generating code).  That's one of the unfortunate limitations of sets in both FPC and Delphi which is, sets are not only limited to 256 elements but, the first element's ordinal _must_ be zero.

That last limitation doesn't really need to be there.  It could be removed (hint, hint)

NOTE: even though I am reusing the example where the bounds are variables, FPC would refuse to compile that code even if "a" and "c" where constants because the base of the set (its first element is not zero (0)).

FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.