I'm reading the manual ( https://nim-lang.org/docs/manual.html#set-type-bit-fields ) and i see set can be used to define bitfields.
Is it its only usage ?
I've understood set as a way of … set (ensemble in french ?) operations.
But using a set on an enum will convert its values automatically to power of two ?
I thought we could define set with ordered value (for example, a set with 0..n values, with each value increased by one until n).
The sample given in the manual doesn't ring a bell to me.
type
MyFlag* {.size: sizeof(cint).} = enum
A
B
C
D
echo type(D), " ", cast[cint](D) # element of enum MyFlag, 3
echo type({D}), " ", cast[cint]({D}) # set[MyFlag], 2^3
Thank you.
I've understood set as a way of … set (ensemble in french ?) operations.
Oui, c’est bien un ensemble.
But using a set on an enum will convert its values automatically to power of two ?
In a set, a value is present if the bit corresponding to its rank is 1. For an enum a, b, c, the set {a, c} is represented by an unsigned byte whose value is 5: bit 0 and bit 2 are 1, other bits are 0.
Due to the way they are represented, there is a limit on the upper bound of the values a set may contain (the lower bound is always 0 which may cause problems in some cases). This limit is 65536 and the corresponding set will occupy 8192 KBytes.
Thank you for the answers.
I've read again the doc and your answers with attention, but i still don't understand.
import strformat
# not allowed
# Error: type mismatch: got <set[range 0..65535(int)]> but expected 'set[int16]'
# var myset:set[int16] = {0..1024}
# expected : allocation of a set with 11 members.
# Cannot define int16 range, but it's ok for char
var setc:set[char] = {'a'..'z', '0'..'9'}
echo fmt"setc.len={setc.len}, setc.card={setc.card}"
# expected : len and cardinality are 36 , ok.
# how to get values ?
# ok (but empty)
var seti:set[int16]
echo fmt"seti.len={seti.len}, seti.card={seti.card}"
# expected : ok (but dubious utility)
type
MyFlag* = enum
A
B
C
D
MyFlags = set[MyFlag]
echo cast[int]({D}) # 8
echo cast[MyFlags](8) # {D}
# expected : a bitfield. So defining enum is the only way to do integers operation ?
Thank you.
Indeed I forget to wrote something about sets in my Nim book, sorry.
Here is an example of set of integers:
type
S = set[uint8]
var x: S
x = {1.uint8, 3u8, 5.uint8}
echo x.contains(3)
echo 2 in x
x.incl(2)
echo 2 in x
x.incl(255)
# x.incl(256) # fail
x = x + {199u8, 202u8} # union
echo 202 in x
# Error: type mismatch: got <set[range 0..65535(int)]> but expected 'set[int16]'
When you write var myset: set[int16] = {0..1024}, 0 and 1024 are ints and a set[int] is not compatible with a set[int16].
You have to write var myset: set[int16] = {0'i16..1024'i16} or var myset:set[int16] = {0'i16..1024} (the first value defines the type). See @Stefan_Salewski examples.
# how to get values ?
That depends on what you want to do.
To access to values sequentially, use a loop:
var setc: set[char] = {'a'..'z', '0'..'9'}
for c in setc:
echo c
If you want to retrieve the values in a sequence, this is the way to do:
import sequtils
var setc: set[char] = {'a'..'z', '0'..'9'}
let x = setc.toSeq()
echo x
# ok (but empty) > var seti:set[int16]
Of course it is empty. Variables are initialized at a default value. Generally, this is all zeroes, so for a set this is an empty set. After that, you can add values to the set using incl or remove values from the set using excl.
# expected : a bitfield. So defining enum is the only way to do integers operation ?
I don’t understand your question. You can use enums, chars, int8, int16, uint8, uint16, int ranges, etc. Enums are cleaner and more elegant, but one could also write:
const
A = 0
B = 1
C = 2
D = 3
type
MyFlag = range[A..B]
MyFlags = set[MyFlag]
Remember also that it’s easy to convert an enum to an int using ord and conversely to convert an int to an enum using (in your example) MyFlag(3).
Ok, thanks to all your comments i have understood.
The differences of types (int vs int16) was not hard too understand (once you have been told of the distinction).
The bitfield was harder. I finally understood this is the position of enum elements which is important. As set elements are stored with a single bit, the pow of the bit will be applied to the value, to get an unique value. So the ABCD progression will use the bit position for the pow applied.
👑 INim 0.5.0
Nim Compiler Version 1.2.4 [Linux: amd64] at /home/…/nim
nim> type en=enum A=2,B=4,C=8,D=16
# so, pow applied will be A 0 , B 1 , C 2 , D 4
nim> cast[int]({C}) # 8^2
64 == type int
nim> cast[int]({D}) # 16^4
16384 == type int
Huh… hard to grasp, but i think it's okay now. Won't use that everyday, i guess :)
Thank you.
i see set can be used to define bitfields. Is it its only usage ?
Basically, yes. It's not a general purpose set as found in Python/Ruby/Swift/etc — its values can only be compile-time constants in a well-defined range (like an enumeration or a uint8). That's because it's implemented as a bit-map, not a hash table.
You're probably looking for the HashSet type in the sets module, which can hold arbitrary types (as long as they're hashable.)
its values can only be compile-time constants in a well-defined range
Indeed, sets contain values in a well-defined range as the type must be an ordinal, but they are not restricted to contain only compile-time constants.
var s: set[byte]
var x = 5'u8
s.incl(2 * x)
echo s
s = {x, 2 * x, 3 * x}
echo s
Sets can be used for many purposes, for instance, if x in {1, 5, 8..12, 15}: which allows to replace several tests on a value by only one test on a bit (with, I suppose, a shift to find the byte to use and an and to find the bit to test).
HashSet is more versatile, but considerably slower. When dealing with small values, we can frequently use sets rather than hash sets. We have only to be careful regarding the memory consumption which depends on the upper limit of the values, not on the actual number of elements in the set.
Won't use that everyday, i guess :)
You would be surprised. It's one of Nim's best features IMO, and I use it constantly.
sets : what are they
@jseb
I hope you have not already retired?
I have yesterday extended the sets section, it was indeed not detailed enough before:
http://ssalewski.de/nimprogramming.html#_sets
There are some typos left, I may do proofreading in winter. Let me know what is still unclear or what may be missing.
I have also added short sections about ranges and about distinct types, and about the fact that unsigned ints wrap around with C backend.
Sorry Stefan, i didn't saw your post before today. I was talking about sets in nim to a friend, so i read again this thread and saw you post.
I have read the new section in your book, and it's clear for me. It was welcome to strengthen my comprehension of set, that i haven't used since this question :)
Just one remark : you have some <= which are transformed to arrow (⇐) , but that's nothing. And n highlight is missing in the sentence : we have to use brackets for the inverted membership test like not(x in a) (in the rest of the section, you are highlighting expressions like not(x in a))
Your book is very good and would deserve a print, to complete «Nim in action». At least, put a link in the menu on your main page :)
Thank you.