Pick a class at random
H. S. Teoh
hsteoh at qfbox.info
Wed Jan 3 17:44:00 UTC 2024
On Wed, Jan 03, 2024 at 04:50:57PM +0000, axricard via Digitalmars-d-learn wrote:
> I have an interface that is implemented by many classes, and I want to
> pick one of these implementations at random. There are two more
> constraints : first the distribution is not uniform, all classes can
> define the chance they have to be picked (this is reflected by the
> function 'weight()' below). And all classes are not always available,
> this depends on some runtime information.
I would tag each implementation with a compile-time enum and use
compile-time introspection with CRTP[1] to auto-generate the code for
choosing a class according to the desired distribution.
[1] https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
Something like this:
----------SNIP-----------
import std.stdio;
interface MyIntf {
void work();
}
struct ImplemInfo {
int weight;
MyIntf function() instantiate;
}
ImplemInfo[] implems; // list of implementations
int totalWeight;
MyIntf chooseImplem() {
import std.random;
auto pick = uniform(0, totalWeight);
auto slice = implems[];
assert(slice.length > 0);
while (slice[0].weight <= pick) {
pick -= slice[0].weight;
slice = slice[1 .. $];
}
return slice[0].instantiate();
}
// Base class that uses CRTP to auto-register implementations in
// .implems without needing too much boilerplate in every
// subclass.
class Base(C) : MyIntf {
// Derived class must define a .weight member readable
// at compile-time.
static assert(is(typeof(C.weight) : int),
"Derived class must define .weight");
static this() {
implems ~= ImplemInfo(C.weight, () {
return cast(MyIntf) new C;
});
totalWeight += C.weight;
}
// Derived classes must implement this
abstract void work();
}
// These classes can be anywhere
class Implem1 : Base!Implem1 {
enum weight = 1;
override void work() { writeln(typeof(this).stringof); }
}
class Implem2 : Base!Implem2 {
enum weight = 2;
override void work() { writeln(typeof(this).stringof); }
}
class Implem3 : Base!Implem3 {
enum weight = 3;
override void work() { writeln(typeof(this).stringof); }
}
void main() {
// pipe output of program to `sort | uniq -c` to verify that the
// required distribution is generated correctly.
foreach (_; 0 .. 100) {
auto impl = chooseImplem();
impl.work();
}
}
----------SNIP-----------
--T
More information about the Digitalmars-d-learn
mailing list