Каков простой и безопасный способ выбрать случайное значение перечисления?

#random #enums #nim-lang

#Случайный #перечисления #ним-ланг

Вопрос:

Мне нужно было выбрать случайное значение из перечисления. В какой-то статье о Nim я нашел это решение:

 import random

type Animal = enum
  Cat
  Dog
  Cow

echo rand(0..2).Animal
 

Но это плохо масштабируется: если некоторые значения добавляются к перечислению или удаляются из
него, нам приходится корректировать верхнее число.

Мы даже можем получить ошибку во время выполнения:

 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]
 

Я ищу простой способ выбрать случайное значение из перечисления 1
это безопасно, а это означает, что при компиляции гарантируется
отсутствие RangeDefect или аналогичная ошибка во время выполнения.

Мне также было бы интересно узнать, существует ли настройка компилятора, которая генерирует хотя бы предупреждение в приведенном выше примере.

Компилятор, похоже, способен на это в принципе:

 Animal(5)

→ Error: 5 can't be converted to Animal
 

После прочтения в https://nim-lang.org/docs/random.html о чем

Я думал, что одно из следующих действий может сработать, но они не компилируются:

 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'
 

Это действительно работает, но я думаю, что это излишне неэффективно, потому что для этого нужно
выделить и заполнить последовательность:

 import sequtils

echo sample(Animal.toSeq)
 

1 Я предполагаю, что нет перечислений с отверстиями, которые, как я знаю, являются
еще одной проблемой
.

Ответ №1:

Простым решением является использование low и high :

 rand(Animal.low..Animal.high)
 

Использование универсального процесса позволяет писать 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)
 

Вывод:

 Cat
Cat
Dog
Cow
Cow
Dog
 

Комментарии:

1. просто хотел добавить, что это не будет компилироваться для перечисления с дырами, что решает вашу проблему безопасности. здесь вы используете rand(HSlice[Animal])