I needed to choose a random value from an enum. In some article about Nim I found this solution:
import random
type Animal = enum
Cat
Dog
Cow
echo rand(0..2).Animal
But this doesn't scale well: If some values are added to or removed from the enum, we have to adjust the upper number.
We can even get a runtime error:
import random
type Animal = enum
Cat
Dog
randomize(123)
while true:
echo rand(0..2).Animal
Cat
Cat
Dog
…/example.nim(10) example
…/.choosenim/toolchains/nim-1.4.4/lib/system/fatal.nim(49) sysFatal
Error: unhandled exception: value out of range: 2 notin 0 .. 1 [RangeDefect]
I am looking for a simple way to choose a random value from an enum1
that is safe, meaning that if it compiles, it is guaranteed that there will be
no RangeDefect
or similar runtime error.
I would also be interested to know if there is a compiler setting that generates at least a warning in the above example.
The compiler seems to be capable of this in principle:
Animal(5)
→ Error: 5 can't be converted to Animal
After reading in https://nim-lang.org/docs/random.html about
I thought that one of the following could work, but they don't compile:
rand(Animal)
→ Error: type mismatch: got <type Animal>
rand(range(Animal))
→ Error: type mismatch: got <type Animal> but expected 'range = range (None)'
rand(range[Animal])
→ Error: expected range
rand(Slice[Animal])
→ Error: type mismatch: got <type Slice[example.Animal]>
rand(Slice(Animal))
→ Error: type mismatch: got <type Animal> but expected 'Slice = CompositeTypeClass'
This does work, but I guess it is unnecessarily inefficient, because it needs to allocate and fill a sequence:
import sequtils
echo sample(Animal.toSeq)
1I'm assuming no enums with holes, which I'm aware are another issue.
To use the random.Pass the enum to the list() class to convert it to a list. Pass the list to the random. choice() method to get a random enum member.
Show activity on this post. typedef enum int { IPV4_VERSION = 0, IPV4_IHL = 1, IPV4_TOTAL_LENGTH = 2,IPV4_CHECKSUM = 3 } ipv4_corrupton; ipv4_corrupton ipv4_corrupt; std::randomize(ipv4_corrupt) with {ipv4_corrupt dist { IPV4_VERSION :=2,IPV4_IHL := 4,IPV4_TOTAL_LENGTH := 4,IPV4_CHECKSUM := 2}; };
Random Enum Value with static Method First, we'll create a static function that returns a random-generated value from a specific enum set. Enum values represent a set of constants; yet, we can still declare static methods within the enum class body. We'll utilize a static method as a helper to generate a random enum value.
valueOf () method returns the enum constant of the specified string value, if exists. enum can contain a constructor and it is executed separately for each enum constant at the time of enum class loading. We can’t create enum objects explicitly and hence we can’t invoke enum constructor directly.
The difference is that the RandomEnumGenerator class has a constructor that expects an enum type from which to get the constant values. We could generate a random direction using the RandomEnumGenerator class as follows:
values () method can be used to return all values present inside enum. Order is important in enums.By using ordinal () method, each enum constant index can be found, just like array index. valueOf () method returns the enum constant of the specified string value, if exists.
A straightforward solution is to use low
and high
:
rand(Animal.low..Animal.high)
Using a generic proc allows to write rand(Animal)
:
import random
type Animal = enum
Cat
Dog
Cow
proc rand(T: typedesc): T =
rand(T.low..T.high)
randomize(123)
for _ in 1..6:
echo rand(Animal)
Output:
Cat
Cat
Dog
Cow
Cow
Dog
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With