我已经采取从枚举中选择随机值,如下所示:
import std.random : uniform; import std.stdio : writefln; import std.conv; enum E {A, B, C} int main(){ auto select = cast(E)uniform(to!int(E.min), to!int(E.max)); writefln("select %s", select); return 0; }如果任何枚举成员的值超出了默认值(或大于int ),这是令人惊讶的详细,并且容易出现问题。
理想情况下,我将采用代表枚举元素的范围,并将其提供给randomSample 。 但是,这似乎不可能。
有没有更习惯的方式从D中的枚举中选择一个随机值?
编辑:
使用fwend提供的答案,下面是一个模板函数,它实现了我想要的功能:
T RandomEnumElement(T)() if (is(T == enum)){ auto members = [EnumMembers!T]; return members[(uniform(0, members.length))]; }I have taken to selecting random values from enums like so:
import std.random : uniform; import std.stdio : writefln; import std.conv; enum E {A, B, C} int main(){ auto select = cast(E)uniform(to!int(E.min), to!int(E.max)); writefln("select %s", select); return 0; }This is surprisingly verbose, and prone to issues if any enum members take values outside the default (or larger than int).
Ideally I would take a range that represents the elements of the enum, and provide this to randomSample. However, this doesn't appear to be possible.
Is there a more idiomatic way to select a random value from an enum in D?
EDIT:
Using the answer provided by fwend, here is a template function that achieves what I want:
T RandomEnumElement(T)() if (is(T == enum)){ auto members = [EnumMembers!T]; return members[(uniform(0, members.length))]; }最满意答案
import std.random : uniform; import std.stdio : writefln; import std.conv; import std.traits; enum E {A, B, C} int main(){ auto select = [EnumMembers!E][uniform(0, 3)]; writefln("select %s", select); return 0; }编辑 :如果您需要多次使用枚举值,则可以先将它们存储在静态不可变数组中,否则将每次都创建该数组。 这也可以让你摆脱魔术数字3。
(...) int main(){ static immutable Evalues = [EnumMembers!E]; auto select1 = Evalues[uniform(0, Evalues.length)]; writefln("select %s", select1); auto select2 = Evalues[uniform(0, Evalues.length)]; writefln("select %s", select2); return 0; }编辑2 :正如Idan Arye所指出的那样,模板可能更加简洁:
T RandomEnumElement(T)() if (is(T == enum)){ return [EnumMembers!T][(uniform(0, $))]; }编辑3 : tgehr提出了以下解决方案,它将在编译时构建一次查找表,并完全避免GC分配:
T RandomEnumElement(T)() if (is(T == enum)) { static immutable members = [EnumMembers!T]; return members[uniform(0, $)]; } import std.random : uniform; import std.stdio : writefln; import std.conv; import std.traits; enum E {A, B, C} int main(){ auto select = [EnumMembers!E][uniform(0, 3)]; writefln("select %s", select); return 0; }Edit: if you need to use the enum values more than once, you can store them in a static immutable array first, otherwise the array will be built every time. That also allows you to get rid of the magic number 3.
(...) int main(){ static immutable Evalues = [EnumMembers!E]; auto select1 = Evalues[uniform(0, Evalues.length)]; writefln("select %s", select1); auto select2 = Evalues[uniform(0, Evalues.length)]; writefln("select %s", select2); return 0; }Edit 2: As pointed out by Idan Arye, the template could be even terser:
T RandomEnumElement(T)() if (is(T == enum)){ return [EnumMembers!T][(uniform(0, $))]; }Edit 3: tgehr has suggested the following solution, which would build the lookup table once at compile time and avoid GC allocation altogether:
T RandomEnumElement(T)() if (is(T == enum)) { static immutable members = [EnumMembers!T]; return members[uniform(0, $)]; }更多推荐
发布评论