dimanche 28 mars 2021

What is a simple and safe way to choose a random enum value?

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 that in the above example.

The compiler seems to be capable of this in principle:

echo 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 enums without holes, which I'm aware are another issue.




Aucun commentaire:

Enregistrer un commentaire