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