Jump to page: 1 2
Thread overview
std.random.uniform for enums
Feb 13, 2014
Anton
Feb 13, 2014
Jakob Ovrum
Feb 13, 2014
Frustrated
Feb 13, 2014
Anton
Feb 13, 2014
Adam D. Ruppe
Feb 13, 2014
Anton
Feb 13, 2014
Anton
Feb 13, 2014
Andrej Mitrovic
Feb 13, 2014
Meta
Feb 13, 2014
Jakob Ovrum
Feb 13, 2014
Jakob Ovrum
Feb 13, 2014
Meta
Feb 13, 2014
Adam D. Ruppe
February 13, 2014
I'm confused about how to use random.uniform to select a member of an enum.

Say I have an enum like

    enum Animals
    {
      cat  = 0,
      dog = 1,
      chimpanzee = 2
    }

I want to select a random animal. So far I've been trying to do uniform(Animals), but every time I try to compile that, I get a "does not match any function template declaration" error.

Am I misunderstanding how this function is meant to be used?
February 13, 2014
On Thursday, 13 February 2014 at 02:02:38 UTC, Anton wrote:
> I'm confused about how to use random.uniform to select a member of an enum.
>
> Say I have an enum like
>
>     enum Animals
>     {
>       cat  = 0,
>       dog = 1,
>       chimpanzee = 2
>     }
>
> I want to select a random animal. So far I've been trying to do uniform(Animals), but every time I try to compile that, I get a "does not match any function template declaration" error.
>
> Am I misunderstanding how this function is meant to be used?

The problem with using `uniform` for enums is that not all enums are sequential without holes, which would make the `uniform` implementation quite non-trivial if it were to try to handle enums generically.

If you know your enum is sequential and doesn't have any holes, assume responsibility for that fact with a cast:

---
enum Animals
{
	cat = 0,
	dog = 1,
	chimpanzee = 2
}

void main()
{
	import std.random, std.stdio;

	foreach(immutable _; 0 .. 10)
		writeln(cast(Animals)uniform!"[]"(Animals.min, Animals.max));
}
---
February 13, 2014
On Thursday, 13 February 2014 at 02:02:38 UTC, Anton wrote:
> Am I misunderstanding how this function is meant to be used?

Yeah, uniform takes two numerical arguments: a min and a max. It returns a value between the two, including the min, but not including the max. So

int a = uniform(0, 10); // returns 0,1,2,3,4,5,6,7,8, or 9.

You could do a random animal by doing `cast(Animals) uniform(0, 3);`, or getting fancier with reflection stuff... that'd take a few more lines, use __traits(getMember) and __traits(allMembers) to randomize rather than .min and .max because the latter wouldn't handle holes in the values.
February 13, 2014
On Thursday, 13 February 2014 at 02:14:02 UTC, Jakob Ovrum wrote:
> On Thursday, 13 February 2014 at 02:02:38 UTC, Anton wrote:
>> I'm confused about how to use random.uniform to select a member of an enum.
>>
>> Say I have an enum like
>>
>>    enum Animals
>>    {
>>      cat  = 0,
>>      dog = 1,
>>      chimpanzee = 2
>>    }
>>
>> I want to select a random animal. So far I've been trying to do uniform(Animals), but every time I try to compile that, I get a "does not match any function template declaration" error.
>>
>> Am I misunderstanding how this function is meant to be used?
>
> The problem with using `uniform` for enums is that not all enums are sequential without holes, which would make the `uniform` implementation quite non-trivial if it were to try to handle enums generically.
>
> If you know your enum is sequential and doesn't have any holes, assume responsibility for that fact with a cast:
>
> ---
> enum Animals
> {
> 	cat = 0,
> 	dog = 1,
> 	chimpanzee = 2
> }
>
> void main()
> {
> 	import std.random, std.stdio;
>
> 	foreach(immutable _; 0 .. 10)
> 		writeln(cast(Animals)uniform!"[]"(Animals.min, Animals.max));
> }
> ---

Could you not simply select one at random by "name"? Even though
the values of the enum may not be sequential the keys are.
February 13, 2014
I guess I'm mostly confused because the description for one of the templates of std.random.uniform says "Returns a uniformly selected member of enum E. If no random number generator is passed, uses the default rndGen." So I was wondering why that functionality didn't seem to work as I thought it would.

Otherwise, thanks for the workarounds.
February 13, 2014
On Thursday, 13 February 2014 at 02:52:44 UTC, Anton wrote:
> I guess I'm mostly confused because the description for one of the templates of std.random.uniform says "Returns a uniformly selected member of enum E.

Oooh, I didn't know it had one of those, the documentation can be so hard to read sometimes.

Try this then:

auto randomAnimal = uniform!Animals();


The enum E there is a compile time argument, so you need the ! in there to pass them. (uniform(Animal) would be trying to send it as a run time argument which doesn't work for types)
February 13, 2014
On Thursday, 13 February 2014 at 02:30:47 UTC, Frustrated wrote:
> Could you not simply select one at random by "name"? Even though
> the values of the enum may not be sequential the keys are.

Yeah, and there is apparently already an overload that does that.

Regardless, I wrote a version that has an optimized path for sequential enums:

https://gist.github.com/JakobOvrum/8968977
February 13, 2014
On Thursday, 13 February 2014 at 03:04:06 UTC, Jakob Ovrum wrote:
> On Thursday, 13 February 2014 at 02:30:47 UTC, Frustrated wrote:
>> Could you not simply select one at random by "name"? Even though
>> the values of the enum may not be sequential the keys are.
>
> Yeah, and there is apparently already an overload that does that.
>
> Regardless, I wrote a version that has an optimized path for sequential enums:
>
> https://gist.github.com/JakobOvrum/8968977

It's also worth noting that both the existing std.random.uniform and the one I posted would fail when the base type of the enum has mutable indirection.
February 13, 2014
On Thursday, 13 February 2014 at 02:30:47 UTC, Frustrated wrote:
> On Thursday, 13 February 2014 at 02:14:02 UTC, Jakob Ovrum wrote:
>> On Thursday, 13 February 2014 at 02:02:38 UTC, Anton wrote:
>>> I'm confused about how to use random.uniform to select a member of an enum.
>>>
>>> Say I have an enum like
>>>
>>>   enum Animals
>>>   {
>>>     cat  = 0,
>>>     dog = 1,
>>>     chimpanzee = 2
>>>   }
>>>
>>> I want to select a random animal. So far I've been trying to do uniform(Animals), but every time I try to compile that, I get a "does not match any function template declaration" error.
>>>
>>> Am I misunderstanding how this function is meant to be used?
>>
>> The problem with using `uniform` for enums is that not all enums are sequential without holes, which would make the `uniform` implementation quite non-trivial if it were to try to handle enums generically.
>>
>> If you know your enum is sequential and doesn't have any holes, assume responsibility for that fact with a cast:
>>
>> ---
>> enum Animals
>> {
>> 	cat = 0,
>> 	dog = 1,
>> 	chimpanzee = 2
>> }
>>
>> void main()
>> {
>> 	import std.random, std.stdio;
>>
>> 	foreach(immutable _; 0 .. 10)
>> 		writeln(cast(Animals)uniform!"[]"(Animals.min, Animals.max));
>> }
>> ---
>
> Could you not simply select one at random by "name"? Even though
> the values of the enum may not be sequential the keys are.

import std.random, std.stdio, std.traits;

enum Animals
{
	dog = "dog",
	cat = "cat",
	fox = "fox",
	cow = "cow",
}

void main()
{
	auto animals = [EnumMembers!Animals];
	auto rnd = uniform!"[)"(0, animals.length);
	writeln(animals[rnd]);
}

You have to wrap the EnumMembers template in an array, because tuples can only be sliced at compile-time, and uniform doesn't work at compile time.
February 13, 2014
On Thursday, 13 February 2014 at 02:52:44 UTC, Anton wrote:
> I guess I'm mostly confused because the description for one of the templates of std.random.uniform says "Returns a uniformly selected member of enum E. If no random number generator is passed, uses the default rndGen." So I was wondering why that functionality didn't seem to work as I thought it would.
>
> Otherwise, thanks for the workarounds.

Huh, disregard me. I didn't know there was a function for that.
« First   ‹ Prev
1 2