Thread overview |
---|
September 11, 2015 Enumap -- a lightweight AA alternative when your keys are enums | ||||
---|---|---|---|---|
| ||||
I frequently find myself needing a data structure that maps each member of an enum to a value; something similar what Java calls an EnumMap (http://docs.oracle.com/javase/7/docs/api/java/util/EnumMap.html). I couldn't find any D implementation out there, so I wrote a little module for it. Enumap is available on Github (https://github.com/rcorre/enumap) and via dub (http://code.dlang.org/packages/enumap). Docs are hosted at http://rcorre.github.io/enumap/. An Enumap is basically a thin wrapper that makes a static array look like an associative array: --- enum Attribute { strength, dexterity, constitution, wisdom, intellect, charisma } Enumap!(Attribute,int) attributes; attributes[Attribute.strength] = 10; --- However, you might prefer an Enumap to an associative array if: You like syntactic sugar: --- // Boring! if (hero.attributes[Attribute.wisdom] < 5) hero.drink(unidentifiedPotion); // Fun! And Concise! if (hero.attributes.wisdom < 5) hero.drink(unidentifiedPotion); --- You like ranges: --- // roll for stats! attributes = generate!(() => uniform!"[]"(1, 20)).take(6); --- You like default values: --- int[Attribute] aa; Enumap!(Attribute, int) em; aa[Attribute.strength]; // Range violation! em.strength; // 0 --- You like array-wise operations: --- // note the convenient constructor function: auto bonus = enumap(Attribute.charisma, 2, Attribute.wisdom, 1); // level up! adds 2 to charisma and 1 to wisdom. hero.attributes += bonus; --- You dislike garbage day: --- void donFancyHat(int[Attribute] aa) { aa[Attribute.charisma] += 1; } @nogc void donFancyHat(Enumap!(Attribute, int) map) { map.charisma += 1; } --- Check it out, report bugs and all that! P.S. The above example used to read: attributes = sequence!((a,n) => uniform!"[]"(1, 20)).take(6); before Gary's recently posted article at http://nomad.so/2015/08/more-hidden-treasure-in-the-d-standard-library/ made me realize generate existed. |
September 11, 2015 Re: Enumap -- a lightweight AA alternative when your keys are enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to rcorre | On Friday, 11 September 2015 at 02:17:25 UTC, rcorre wrote:
> I frequently find myself needing a data structure that maps each member of an enum to a value;
> something similar what Java calls an EnumMap (http://docs.oracle.com/javase/7/docs/api/java/util/EnumMap.html).
>
> I couldn't find any D implementation out there, so I wrote a little module for it.
> Enumap is available on Github (https://github.com/rcorre/enumap)
> and via dub (http://code.dlang.org/packages/enumap).
> Docs are hosted at http://rcorre.github.io/enumap/.
Hi,
this looks excellent! I've been playing around with it, and am looking forward to using it regularly.
I've ran into a compilation error when iterating over a const Enumap. In the following code:
import std.stdio;
import std.conv;
import enumap;
enum MyEnum { e1, e2, e3 }
struct A
{
Enumap!(MyEnum, int) map;
void mutable_output()
{
foreach (MyEnum e, int i; map)
writefln("%s: %d", e.to!string, i);
}
void const_output() const
{
foreach (MyEnum e, const int i; map)
writefln("%s: %d", e.to!string, i);
}
}
...the first method (mutable_output) compiles and works with no errors. The const method, however, gives:
source/app.d(19,13): Error: invalid foreach aggregate this.map,
define opApply(), range primitives, or use .tupleof".
It doesn't seem to matter whether I put const int, or int, in the foreach statement.
What's the idiomatic way to loop over a const Enumap? :-)
-- Simon
|
September 11, 2015 Re: Enumap -- a lightweight AA alternative when your keys are enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to SimonN | On Friday, 11 September 2015 at 03:25:58 UTC, SimonN wrote:
> Hi,
>
> this looks excellent! I've been playing around with it, and am looking forward to using it regularly.
>
> I've ran into a compilation error when iterating over a const Enumap. In the following code:
>
> import std.stdio;
> import std.conv;
> import enumap;
>
> enum MyEnum { e1, e2, e3 }
>
> struct A
> {
> Enumap!(MyEnum, int) map;
>
> void mutable_output()
> {
> foreach (MyEnum e, int i; map)
> writefln("%s: %d", e.to!string, i);
> }
>
> void const_output() const
> {
> foreach (MyEnum e, const int i; map)
> writefln("%s: %d", e.to!string, i);
> }
> }
>
> ...the first method (mutable_output) compiles and works with no errors. The const method, however, gives:
>
> source/app.d(19,13): Error: invalid foreach aggregate this.map,
> define opApply(), range primitives, or use .tupleof".
>
> It doesn't seem to matter whether I put const int, or int, in the foreach statement.
>
> What's the idiomatic way to loop over a const Enumap? :-)
>
> -- Simon
Interesting, thanks for pointing that out.
I don't think I did a great job with const-correctness here, I'll take a look tomorrow.
It should definitely be possible to iterate over (and index, etc...) a const/immutable Enumset, though you're right that it doesn't work right now.
|
September 11, 2015 Re: Enumap -- a lightweight AA alternative when your keys are enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to rcorre | On Friday, 11 September 2015 at 04:02:17 UTC, rcorre wrote:
> On Friday, 11 September 2015 at 03:25:58 UTC, SimonN wrote:
>> Hi,
>> I've ran into a compilation error when iterating over a const Enumap. In the following code:
>
> Interesting, thanks for pointing that out.
> I don't think I did a great job with const-correctness here, I'll take a look tomorrow.
> It should definitely be possible to iterate over (and index, etc...) a const/immutable Enumset, though you're right that it doesn't work right now.
No worries, take your time! Thanks for the quick clarification.
I've also tested a couple ways of assigning in a foreach. Continuing from my code above (struct A { Enumap!(MyEnum, int) map; /* ... */ }), I've tried this in the main function:
int some_value = 100;
A a;
foreach (MyEnum e, ref val; a.map)
val = ++some_value;
a.mutable_output();
foreach (MyEnum e, ref val; a.map)
a.map[e] = ++some_value;
a.mutable_output();
Output:
e1: 0
e2: 0
e3: 0
e1: 104
e2: 105
e3: 106
Since I have been using "ref val" in the first loop, I expected the output to be instead:
e1: 101
e2: 102
e3: 103
e1: 104
e2: 105
e3: 106
-- Simon
|
September 11, 2015 Re: Enumap -- a lightweight AA alternative when your keys are enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to rcorre | On Friday, 11 September 2015 at 02:17:25 UTC, rcorre wrote:
> @nogc void donFancyHat(Enumap!(Attribute, int) map) { map.charisma += 1; }
BTW, what this means? Isn't Enumap a value type?
|
September 11, 2015 Re: Enumap -- a lightweight AA alternative when your keys are enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to SimonN | On Friday, 11 September 2015 at 04:30:31 UTC, SimonN wrote:
>
> Since I have been using "ref val" in the first loop, I expected the output to be instead:
>
> e1: 101
> e2: 102
> e3: 103
> e1: 104
> e2: 105
> e3: 106
>
> -- Simon
Yep, as I was looking at the constness thing, I realized that ref parameters in foreach are totally broken. I may just have to use opApply instead of opSlice.
|
September 11, 2015 Re: Enumap -- a lightweight AA alternative when your keys are enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kagamin | On Friday, 11 September 2015 at 08:22:20 UTC, Kagamin wrote:
> On Friday, 11 September 2015 at 02:17:25 UTC, rcorre wrote:
>> @nogc void donFancyHat(Enumap!(Attribute, int) map) { map.charisma += 1; }
>
> BTW, what this means? Isn't Enumap a value type?
Correct, the parameter should be passed by ref in that example. Good catch!
|
September 13, 2015 Re: Enumap -- a lightweight AA alternative when your keys are enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to SimonN | On Friday, 11 September 2015 at 03:25:58 UTC, SimonN wrote:
>
> It doesn't seem to matter whether I put const int, or int, in the foreach statement.
>
> What's the idiomatic way to loop over a const Enumap? :-)
>
> -- Simon
I've released v0.4.0, which implements foreach with ref, and (hopefully) atones for my crimes against const-correctness. You should be able to modify values in a loop and work with const/immutable Enumaps.
Thanks for the feedback!
|
September 16, 2015 Re: Enumap -- a lightweight AA alternative when your keys are enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to rcorre | On Sunday, 13 September 2015 at 03:33:15 UTC, rcorre wrote:
> I've released v0.4.0, which implements foreach with ref, and (hopefully) atones for my crimes against const-correctness. You should be able to modify values in a loop and work with const/immutable Enumaps.
>
> Thanks for the feedback!
Yes, the 0.4.x version works with my examples perfectly. Thanks for adding const support!
(I haven't tested yet every combination of const/mutable Enumap, const/mutable foraech-value, and direct/ref foreach-value. My examples are exactly what I'd do in normal projects anyway, mapping enum-values to ints.)
-- Simon
|
September 21, 2015 Re: Enumap -- a lightweight AA alternative when your keys are enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to SimonN | On Wednesday, 16 September 2015 at 03:20:28 UTC, SimonN wrote:
> Yes, the 0.4.x version works with my examples perfectly. Thanks for adding const support!
>
> (I haven't tested yet every combination of const/mutable Enumap, const/mutable foraech-value, and direct/ref foreach-value. My examples are exactly what I'd do in normal projects anyway, mapping enum-values to ints.)
>
> -- Simon
Good to hear!
I have a pretty long set of static asserts that tries every (?) operation between mutable/const/immutable enumaps to verify that they do/don't compile as expected, so hopefully that has most situations covered.
|
Copyright © 1999-2021 by the D Language Foundation