www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - std.random.uniform for enums

reply "Anton" <vizardx gmail.com> writes:
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?
Feb 12 2014
next sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
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)); } ---
Feb 12 2014
parent reply "Frustrated" <c1514843 drdrb.com> writes:
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.
Feb 12 2014
next sibling parent reply "Anton" <vizardx gmail.com> writes:
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.
Feb 12 2014
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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)
Feb 12 2014
parent reply "Anton" <vizardx gmail.com> writes:
On Thursday, 13 February 2014 at 02:59:01 UTC, Adam D. Ruppe
wrote:
 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)
Yeah, the docs aren't the easiest to read, especially coming from Python. But thanks a lot for your help. Using ! does the trick.
Feb 12 2014
parent reply "Anton" <vizardx gmail.com> writes:
It would be useful if someone added this as an example to the 
docs, so that beginners don't end up in the same situation as me 
in the future.
Feb 12 2014
parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 2/13/14, Anton <vizardx gmail.com> wrote:
 It would be useful if someone added this as an example to the
 docs, so that beginners don't end up in the same situation as me
 in the future.
My mistake for not adding an example when implementing the function. Here we go: https://github.com/D-Programming-Language/phobos/pull/1932
Feb 12 2014
prev sibling parent "Meta" <jared771 gmail.com> writes:
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.
Feb 12 2014
prev sibling next sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
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
Feb 12 2014
parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
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.
Feb 12 2014
prev sibling parent "Meta" <jared771 gmail.com> writes:
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.
Feb 12 2014
prev sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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.
Feb 12 2014